I'm using Apollo Server 2 and Express.js vanilla (with apollo-server-express).
Everything works good also with Subscriptions except the Express session mechanism.
The problem:
I'm using cookie-session (https://github.com/expressjs/cookie-session, but I think this is the same for express-session middleware) and when my browser start a new connection with my server the ApolloServer onConnect hook doesn't have the req attribute and neither req.session and so on...
What I can do is to parse the cookies from webSocket.upgradeReq.headers.cookie in onConnect lifecycle hook, but it seems to me very hacky.
The code:
const { ApolloServer } = require('apollo-server-express')
const typeDefs = require('../src/graphql/types')
const resolvers = require('../src/graphql/resolvers')
const models = require('../src/models')
const apolloServer = new ApolloServer({
typeDefs,
resolvers,
context: ({ req, connection }) => {
// connection exists only on webSocket connection
if (connection) {
return {
currentUser: connection.context.currentUser // <-- I NEED THIS!
}
}
// if not a (webSocket) connection it is a "default" HTTP call
return {
models,
currentUser: { id: req.user.id }
}
},
subscriptions: {
onConnect: (connectionParams, webSocket) => {
// "connectionParams" is from the client but I cannot use it because cookies are HTTP-Only
// I can retrieve cookies from here: "webSocket.upgradeReq.headers.cookie" but then I need to parse them which seems a bit hacky to me
// return { currentUser: req.user.id } // <-- I NEED THIS (req.user.id doesn't exists)!
}
}
})
module.exports = apolloServer
I can't find anything on Apollo Server Docs site (for other topics very well documented!).
Where am I doing wrong?
StackOverflow question: https://stackoverflow.com/questions/52280481/graphql-subscription-websocket-nodejs-express-session-with-apollo-server-2
These two are probably relevant / helpful for fixing this in the apollo server itself:
The socket appears to already be available as part of the connection context, I'll see if it's possible to access the session cookie from there.
To get session contents https://github.com/expressjs/cookie-session/issues/117#issuecomment-452046225
I've got it to work on a sideproject of mine: https://github.com/niklaskorz/nkchat/
This is using Koa, but the same principle can be applied to express with few changes.
See https://github.com/niklaskorz/nkchat/blob/master/server/src/startServer.ts in particular.
I solved this problem in the onConnect callback function.
const options = {
cors: { credentials: true, origin },
port: PORT,
subscriptions: {
onConnect: async (connectionParams, webSocket) => {
try {
const promise = new Promise((resolve, reject) => {
session(webSocket.upgradeReq, {}, () => {
resolve(webSocket.upgradeReq.session.passport);
});
});
const user = await promise;
return user;
} catch (error) {
console.log('error', error);
}
},
},
};
Next, when you initialize the server, you can get the user in context.
const server = new GraphQLServer({
typeDefs,
resolvers,
context: ({ request, connection }) => {
let user = request ? request.user : null;
if (connection) {
if (connection.context.user) user = connection.context.user;
}
return { user, request, pubsub };
},
});
An example can be found here https://github.com/bakhaa/pw/blob/master/api/app.js.
I am just using apollo-express-sever and session inside of context.
const apollo = new ApolloServer({
typeDefs,
resolvers,
engine: false,
tracing: true,
context: ({ req }) => ({ session: req.session }),
playground: true
});
apollo.applyMiddleware({ app, cors: false })
const httpServer = http.createServer(app)
apollo.installSubscriptionHandlers(httpServer);
But I get this error when I am executing subscription in playground.
{
"error": {
"message": "Cannot read property 'session' of undefined"
}
}
If I remove the context including session, that error disappears. Why is it happening?
@Apollo725
hey, are you fixed your issue, and how?
i'm have same issue
We use this something like this:
subscriptions: {
keepAlive: 10000,
onConnect: (_params, _ws, ctx) => {
const promise = new Promise((resolve, reject) => {
const req = ctx.request as express.Request;
const res = ({} as any) as express.Response;
sessionHandler(req, res, _ => {
const userId = req.session && req.session.userId;
return resolve({ userId });
});
});
return promise;
},
},
I know this old but ran into this issue myself and how i solved it was I ran webSocket.upgradeReq.session through my session middleware.

Then in onConnect:

And you have access to your session
Most helpful comment
I know this old but ran into this issue myself and how i solved it was I ran
webSocket.upgradeReq.sessionthrough my session middleware.Then in onConnect:
And you have access to your session