apollo-server
apollo-server copied to clipboard
Better support for websockets (subscriptions) for apollo-server-fastify
I'm trying to use implement GraphQL subscriptions using graphql-ws library and apollo-server-fastify. Logical middle step would be fastify-websocket library, but there is no good way to configure subscriptions end point to be same as main GraphQL server endpoint (e.g /graphql).
One simple solution would be to add optional wsHandler parameter to ApolloServer.createHandler method, split instance.route by method and add wsHandler to GET route, something like this:
instance.route({
method: "GET",
url: "/",
preHandler,
handler,
wsHandler, // websocket handler
});
instance.route({
method: "POST",
url: "/",
handler,
});
Usage:
import { execute, subscribe } from "graphql";
import { makeHandler } from "graphql-ws/lib/use/fastify-websocket";
const app = fastify();
const server = new ApolloServer({ schema });
await server.start();
app.register(
server.createHandler({
wsHandler: makeHandler({
schema,
execute,
subscribe,
}),
}),
);
Apollo Server 3 does not support subscriptions. I'm not sure it makes much sense to add partial support like this. Fastify doesn't provide any mechanism to let you call the AS handler on GET/POST and the WS handler on CONNECT?
There should be other ways how to achieve it, e.g. subscriptions-transport-ws only needs server instance as input and works fine. Problem is that I have not found an easy way how to do something similar with apollo-server-fastify and fastify-express. Right now best solution for me seems to just fork apollo-server-fastify and make few simple changes rather than trying to take a deeper dive into fastify and websocket. Main aim of this Github issue is to check if others have similar issues and wether to try to fix it more generally.
Trying to do the same.
Hello, when will subscriptions be available for version 3? Do we have to go back to version 2?
We answered this question in https://github.com/apollographql/apollo-server/issues/6020#issuecomment-1020543875
Successful implementation of the 2022 bid.
import { env } from "process";
import { ApolloServer } from "apollo-server-fastify";
import { ApolloServerPluginDrainHttpServer } from "apollo-server-core";
import { ApolloServerPlugin } from "apollo-server-plugin-base";
import fastify, { FastifyInstance } from "fastify";
import { WebSocketServer } from "ws";
import { makeExecutableSchema } from "@graphql-tools/schema";
import { useServer } from "graphql-ws/lib/use/ws";
const fastifyAppClosePlugin = (app: FastifyInstance): ApolloServerPlugin => {
return {
async serverWillStart() {
return {
async drainServer() {
await app.close();
},
};
},
};
};
export const startApolloServer = async (
typeDefs: any,
resolvers: any
): Promise<void> => {
const app = fastify({
https: {
key: env.KEY ?? undefined,
cert: env.CERT ?? undefined,
},
});
const wsServer = new WebSocketServer({
server: app.server,
path: "/graphql",
});
const serverCleanup = useServer(
{ schema: makeExecutableSchema({ typeDefs, resolvers }) },
wsServer
);
const server = new ApolloServer({
typeDefs,
resolvers,
plugins: [
fastifyAppClosePlugin(app as any),
ApolloServerPluginDrainHttpServer({ httpServer: app.server as any }),
{
async serverWillStart() {
return {
async drainServer() {
await serverCleanup.dispose();
},
};
},
},
],
});
await server.start();
void app.register(import("@fastify/cookie"));
void app.register(
server.createHandler({
cors: {
origin: (_origin: any, cb: any) => {
cb(null, true);
},
credentials: true,
},
})
);
await app.listen(env.PORT ?? 4000);
console.log(
`🚀 Server ready at https://localhost:${env.PORT ?? 4000}${
server.graphqlPath
}`
);
};