I have built graphql apollo (v4) server by following the docs. I just made the subscription and pubsub work.
However, when I was trying to work on authentication, I realized that my graphql server does not provide context
to my resolvers as expected.
Here is my code.
// index.ts
import { ApolloServer } from '@apollo/server';
import { expressMiddleware } from '@apollo/server/express4';
import { ApolloServerPluginDrainHttpServer } from '@apollo/server/plugin/drainHttpServer';
import express from 'express';
import { createServer } from 'http';
import { makeExecutableSchema } from '@graphql-tools/schema';
import { WebSocketServer } from 'ws';
import { useServer } from 'graphql-ws/lib/use/ws';
import bodyParser from 'body-parser';
import cors from 'cors';
import { PubSub } from 'graphql-subscriptions';
import { typeDefs, resolvers } from './graphql'
const PORT = 4000;
// Create schema, which will be used separately by ApolloServer and
// the WebSocket server.
const schema = makeExecutableSchema({ typeDefs, resolvers });
// Create an Express app and HTTP server; we will attach the WebSocket
// server and the ApolloServer to this HTTP server.
const app = express();
const httpServer = createServer(app);
// Set up WebSocket server.
const wsServer = new WebSocketServer({
server: httpServer,
path: '/graphql',
});
const pubsub = new PubSub();
const getSubscriptionContext = async (ctx: any): Promise<any> => {
ctx;
console.log('@@ ctx', ctx);
// ctx is the graphql-ws Context where connectionParams live
if (ctx.connectionParams && ctx.connectionParams.session) {
const { session } = ctx.connectionParams;
return { session, pubsub };
}
// Otherwise let our resolvers know we don't have a current user
return { session: null, pubsub };
};
const serverCleanup = useServer(
{
schema,
context: (ctx: any) => getSubscriptionContext(ctx)
},
wsServer
);
// Set up ApolloServer.
const server = new ApolloServer({
schema,
plugins: [
// Proper shutdown for the HTTP server.
ApolloServerPluginDrainHttpServer({ httpServer }),
// Proper shutdown for the WebSocket server.
{
async serverWillStart() {
return {
async drainServer() {
await serverCleanup.dispose();
},
};
},
},
],
});
async function init() {
await server.start();
app.use('/graphql', cors<cors.CorsRequest>(), bodyParser.json(), expressMiddleware(server));
// Now that our HTTP server is fully set up, actually listen.
httpServer.listen(PORT, () => {
console.log(`🚀 Query endpoint ready at http://localhost:${PORT}/graphql`);
console.log(`🚀 Subscription endpoint ready at ws://localhost:${PORT}/graphql`);
});
};
init();
For some reason, the getSubscriptionContext
does not seem to be running at all.
When I run query in the browser, I do not see console.log from this function at all.
I do not think the server is skipping this block.
Some of the posts have context
passed to ApolloServer constructor, but the ApolloServer from @apollo/server
(v4) no longer accepts a context
parameter.
In the resolver I have:
resolvers: {
Query: {
users: (_: any, __: any, context: any) => {
console.log('@@ context', context);
return 123;
}
}
}
this console.log returns an empty object.
@@ context {}
Here's a sample query and mutation:
const user = {
typeDefs: `
extend type Query {
hello: Int
}
extend type Mutation {
hi(name: String): Int
}
`,
resolvers: {
Query: {
hello: (_: any, __: any, context: any) => {
console.log('@@ context', context);
return 123;
}
},
Mutation: {
hi: (_: any, args: any, context: any) => {
console.log('@@', context, args);
return 123;
}
}
}
}
A context
is essential for authentication, how can I gain access to it?
const context = (ctx) => ctx;
expressMiddleware
call:app.use(
'/graphql',
cors<cors.CorsRequest>(),
bodyParser.json(),
expressMiddleware(server, { context })
);