This is a follow-up
Ok, a while back I asked a question and spent a few days trying to figure this out. I'm hoping that this can direct someone trying to solve the problem in the right direction. As a reference, this is the original post.
My problem
So, my problem was that I had set up an Apollo Server with the backend hosted from a /backend directory and the frontend hosted from a separate directory at /frontend. My frontend was running on localhost:3000 and the backend I had running at localhost:4000. I was running into various problems with CORS as I tried to pass cookies along with requests, and I couldn't figure out what was going on. I also had a difficult time figuring out how to pass certain things (like user data) with requests.
My problem was, I was trying to pass along specific cors options, and I didn't know that Apollo Server came with default cors settings. Those settings were overriding and conflicting with what I needed
The Solution
My solution ended up being 2 parts:
- Using the package
apollo-server-express
and the cors middleware. - Disabling the Apollo Server cors to avoid conflicts.
const express = require('express');
const { Prisma } = require('prisma-binding');
const { ApolloServer } = require('apollo-server-express');
const { importSchema } = require('graphql-import');
const cookieParser = require('cookie-parser');
const jwt = require('jsonwebtoken');
const cors = require('cors');
require('dotenv').config();
const typeDefs = importSchema('./src/schema.graphql');
const Query = require('./src/resolvers/Query');
const Mutation = require('./src/resolvers/Mutation');
const db = new Prisma({
typeDefs: './generated/prisma.graphql',
endpoint: process.env.DB_ENDPOINT,
secret: process.env.DB_SECRET
});
const server = new ApolloServer({
typeDefs,
resolvers: {
Mutation,
Query
},
context: req => ({ ...req, db }),
});
const app = express();
var corsOptions = {
origin: process.env.FRONTEND_URL,
credentials: true // <-- REQUIRED backend setting
};
app.use(cors(corsOptions));
app.use(cookieParser());
app.use((req, res, next) => { // checks for user in cookies and adds userId to the requests
const { token } = req.cookies;
if (token) {
const { userId } = jwt.verify(token, process.env.USER_SECRET);
req.userId = userId;
}
next();
})
app.use(async (req, res, next) => {
if (!req.userId) return next();
const user = await db.query.user(
{ where: { id: req.userId } },
'{id, permissions, email, name}' //the graphql query to pass to the user query
);
next();
})
server.applyMiddleware({
app,
path: '/',
cors: false, // disables the apollo-server-express cors to allow the cors middleware use
})
app.listen({ port: 4000 }, () =>
console.log(`🚀 Server ready at http://localhost:4000${server.graphqlPath}`)
);
This is my server/app set up as of now with everything working perfectly! Here are the main points and why I ended up implementing it that way.
-
Using
apollo-server-express
instead ofapollo-server
: This essentially allows you to create an express app and pass it to the server instead of using the vanilla app that Apollo Server creates on its own. It also allows you to create middlewares just as you would with a generic express based application and server! - app.use(cors(corsOptions)): For more granular cors options (I wanted the server to only listen to localhost:3000) you use the cors middleware and the options to listen to the origin you want, as well as the option to pass on credentials with requests.
- server.applyMiddleware: It is here that you pass to the Apollo Server the express application, as well as the other important part, turn off the default cors options. Even though you turn off the default options, you are using the cors middleware in the express app you pass to the server, so it's all handled there.
Top comments (12)
Why not just add corsOptions on server.applyMiddleware
This worked for me and I didn't have to use the cors dependency
This is the only method I got cors to work with. Thanks!
Worked out too. Thanks.
Hi Ryan,
I am using the
apollo-server
to setup a node apollo server. I just deployed to heroku a production verion of my app, but I can't seem to communicate with it via the React apollo-client code. It complains of cors. I have had no success in fixing it. I have tried a lot of solutions online, but no success yet.Do you why this is happening?
Not sure off hand, it's difficult to tell without any errors I can get a look at. One thing is, I didn't use CORS in Apollo client, which you still are. I added apollo-server-express and used that.
I have switched to apollo-server-express and it still had same issues. I am not using cors from my client app
Just spent several hours trying to figure out what on Earth I could possibly not be doing correct. Somehow, among all the tutorials / q&a's / blog posts / etc out there, it seems none of them mention the crucial point that you have to turn off the default cors option. You saved my day bud! Thanks!
For me I just have to set fetchOptions in the frontend code to
{credentials: 'included'}
, I use Urql instead of Apollo-Client though.Configuration for Apollo-Server-Express is as normal (didn't have to use the cors package).
apolloServer.applyMiddleware({ app, cors: { origin: FRONTEND_URL, credentials: true } })
Ryan Doyle, great article. I'm facing the same issue with my setup. Would be awesome if you could take a look at it: stackoverflow.com/questions/642075...
omg you're the best, this apollo server cors: false nearly killed me.
Thanks a lot!
You Saved my day
Lifesaver. Thanks so much for this write up.