Last active
December 31, 2024 13:34
-
-
Save ashbuilds/490d504d703a5ddfcdf504f6c21e220d to your computer and use it in GitHub Desktop.
Implementing GraphQL Subscriptions with Websockets in a Bun Server using graphql-yoga
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Bun from 'bun' | |
import { createYoga, YogaInitialContext, YogaServerInstance } from 'graphql-yoga' | |
import { makeHandler } from "graphql-ws/lib/use/bun"; | |
import { ExecutionArgs } from "@envelop/types"; | |
import { schema } from './graphql/schema'; | |
interface IUserContext { | |
token?: string; | |
} | |
const PORT = process.env.PORT || 4000; | |
const yoga: YogaServerInstance<{}, YogaInitialContext> = createYoga({ | |
graphqlEndpoint: '/graphql', | |
schema, | |
graphiql: { | |
subscriptionsProtocol: 'WS', | |
}, | |
context: async ({ request }): Promise<IUserContext> => { | |
return { | |
token: request.headers.get("token") ?? "" | |
} | |
} | |
}) | |
const websocketHandler = makeHandler({ | |
schema, | |
execute: (args: ExecutionArgs) => args.rootValue.execute(args), | |
subscribe: (args: ExecutionArgs) => args.rootValue.subscribe(args), | |
onSubscribe: async (ctx, msg) => { | |
const {schema, execute, subscribe, contextFactory, parse, validate} = yoga.getEnveloped({ | |
...ctx, | |
req: ctx.extra.request, | |
socket: ctx.extra.socket, | |
params: msg.payload | |
}) | |
const args = { | |
schema, | |
operationName: msg.payload.operationName, | |
document: parse(msg.payload.query), | |
variableValues: msg.payload.variables, | |
contextValue: await contextFactory(), | |
rootValue: { | |
execute, | |
subscribe | |
} | |
} | |
const errors = validate(args.schema, args.document) | |
if (errors.length) return errors | |
return args | |
}, | |
}) | |
const server: Bun.Server = Bun.serve({ | |
fetch: (request: Request, server: Bun.Server): Promise<Response> | Response => { | |
// Upgrade the request to a WebSocket | |
if (server.upgrade(request)) { | |
return new Response() | |
} | |
return yoga.fetch(request, server) | |
}, | |
port: PORT, | |
websocket: websocketHandler, | |
}) | |
console.info( | |
`🚀 Server is running on ${new URL( | |
yoga.graphqlEndpoint, | |
`http://${server.hostname}:${server.port}` | |
)}` | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
For those who faced issues with accessing the passed headers for custom context providing, like authorisation header, here's a useful note on the Authorization token example:
The typical
context.request
headers are not present in WS connection but appear incontext.params.extensions?.headers
Full example in context of global
user
context pass: