### Minimal Server Implementation with ws Source: https://github.com/enisdenjo/graphql-ws/blob/master/website/src/pages/recipes.mdx A basic setup for graphql-ws using the 'ws' library. Ensure 'ws' is installed (`yarn add ws`). This example shows the core logic for handling WebSocket connections and integrating with the graphql-ws server. ```typescript // minimal version of `useServer` from `graphql-ws/use/ws` import { CloseCode, makeServer } from 'graphql-ws'; import { WebSocketServer } from 'ws'; // yarn add ws import { schema } from './my-graphql-schema'; // make const server = makeServer({ schema }); // create websocket server const wsServer = new WebSocketServer({ port: 4000, path: '/graphql', }); // implement wsServer.on('connection', (socket, request) => { // a new socket opened, let graphql-ws take over const closed = server.opened( { protocol: socket.protocol, // will be validated send: (data) => new Promise((resolve, reject) => { socket.send(data, (err) => (err ? reject(err) : resolve())); }), // control your data flow by timing the promise resolve close: (code, reason) => socket.close(code, reason), // there are protocol standard closures onMessage: (cb) => socket.on('message', async (event) => { try { // wait for the the operation to complete // - if init message, waits for connect // - if query/mutation, waits for result // - if subscription, waits for complete await cb(event.toString()); } catch (err) { // all errors that could be thrown during the // execution of operations will be caught here socket.close(CloseCode.InternalServerError, err.message); } }), }, // pass values to the `extra` field in the context { socket, request }, ); // notify server that the socket closed socket.once('close', (code, reason) => closed(code, reason)); }); ``` -------------------------------- ### ws Server with Console Logging Source: https://github.com/enisdenjo/graphql-ws/blob/master/website/src/pages/recipes.mdx This example shows how to set up a 'ws' server for GraphQL-WS and includes console logging for connection, subscription, next, error, and complete events. Ensure 'ws' is installed using 'yarn add ws'. ```typescript import { useServer } from 'graphql-ws/use/ws'; import { WebSocketServer } from 'ws'; // yarn add ws import { schema } from './my-graphql-schema'; const wsServer = new WebSocketServer({ port: 4000, path: '/graphql', }); useServer( { schema, onConnect: (ctx) => { console.log('Connect', ctx); }, onSubscribe: (ctx, id, payload) => { console.log('Subscribe', { ctx, id, payload }); }, onNext: (ctx, id, subscribePayload, args, result) => { console.debug('Next', { ctx, id, args, subscribePayload, result }); }, onError: (ctx, id, subscribePayload, errors) => { console.error('Error', { ctx, id, subscribePayload, errors }); }, onComplete: (ctx, id, subscribePayload) => { console.log('Complete', { ctx, id, subscribePayload }); }, }, wsServer, ); ``` -------------------------------- ### ws Server on a Multi WebSocket Server Setup Source: https://github.com/enisdenjo/graphql-ws/blob/master/website/src/pages/recipes.mdx This example demonstrates setting up multiple WebSocket servers on different paths using a single HTTP server. It delegates upgrade requests to either a 'wave' server or a 'graphql' server based on the URL path. The 'wave' server sends a wave emoji on connection, while the 'graphql' server is configured with GraphQL-WS. ```typescript import http from 'http'; import url from 'url'; import { useServer } from 'graphql-ws/use/ws'; import { WebSocketServer } from 'ws'; // yarn add ws import { schema } from './my-graphql-schema'; const server = http.createServer(function weServeSocketsOnly(_, res) { res.writeHead(404); res.end(); }); /** * Two websocket servers on different paths: * - `/wave` sends out waves * - `/graphql` serves graphql */ const waveWS = new WebSocketServer({ noServer: true }); const graphqlWS = new WebSocketServer({ noServer: true }); // delegate upgrade requests to relevant destinations server.on('upgrade', (request, socket, head) => { const pathname = url.parse(request.url).pathname; if (pathname === '/wave') { return waveWS.handleUpgrade(request, socket, head, (client) => { waveWS.emit('connection', client, request); }); } if (pathname === '/graphql') { return graphqlWS.handleUpgrade(request, socket, head, (client) => { graphqlWS.emit('connection', client, request); }); } return socket.destroy(); }); // wave on connect waveWS.on('connection', (socket) => { socket.send('🌊'); }); // serve graphql useServer({ schema }, graphqlWS); server.listen(4000); ``` -------------------------------- ### Start Server with Deno Source: https://github.com/enisdenjo/graphql-ws/blob/master/website/src/pages/get-started.mdx Set up a GraphQL-WS server in Deno using the standard HTTP server and graphql-ws/use/deno. ```ts import { serve } from 'https://deno.land/std/http/mod.ts'; import { GRAPHQL_TRANSPORT_WS_PROTOCOL, makeHandler, } from 'https://esm.sh/graphql-ws/use/deno'; import { schema } from './previous-step.ts'; const handler = makeHandler({ schema }); serve( (req: Request) => { const [path, _search] = req.url.split('?'); if (!path.endsWith('/graphql')) { return new Response('Not Found', { status: 404 }); } if (req.headers.get('upgrade') != 'websocket') { return new Response('Upgrade Required', { status: 426 }); } const { socket, response } = Deno.upgradeWebSocket(req, { protocol: GRAPHQL_TRANSPORT_WS_PROTOCOL, idleTimeout: 12_000, }); handler(socket); return response; }, { port: 4000 }, ); ``` -------------------------------- ### Start Server with ws Source: https://github.com/enisdenjo/graphql-ws/blob/master/website/src/pages/get-started.mdx Integrate graphql-ws with the 'ws' library to start a WebSocket server on port 4000. ```ts import { useServer } from 'graphql-ws/use/ws'; import { WebSocketServer } from 'ws'; // yarn add ws import { schema } from './previous-step'; const server = new WebSocketServer({ port: 4000, path: '/graphql', }); useServer({ schema }, server); console.log('Listening to port 4000'); ``` -------------------------------- ### Install graphql-ws Source: https://github.com/enisdenjo/graphql-ws/blob/master/website/src/pages/get-started.mdx Install the graphql-ws package using npm or yarn. ```sh npm i graphql-ws ``` -------------------------------- ### Start Server with Bun Source: https://github.com/enisdenjo/graphql-ws/blob/master/website/src/pages/get-started.mdx Implement a GraphQL-WS server using Bun's built-in HTTP server and WebSocket capabilities. ```ts import { handleProtocols, makeHandler } from 'graphql-ws/use/bun'; import { schema } from './previous-step'; Bun.serve({ fetch(req, server) { const [path, _search] = req.url.split('?'); if (!path.endsWith('/graphql')) { return new Response('Not Found', { status: 404 }); } if (req.headers.get('upgrade') != 'websocket') { return new Response('Upgrade Required', { status: 426 }); } if (!handleProtocols(req.headers.get('sec-websocket-protocol') || '')) { return new Response('Bad Request', { status: 404 }); } if (!server.upgrade(req)) { return new Response('Internal Server Error', { status: 500 }); } return new Response(); }, websocket: makeHandler({ schema }), port: 4000, }); console.log('Listening to port 4000'); ``` -------------------------------- ### Server Implementation with Custom Auth Handling Source: https://github.com/enisdenjo/graphql-ws/blob/master/website/src/pages/recipes.mdx An extended implementation of graphql-ws with custom authentication logic using the 'ws' library. This example demonstrates how to integrate authentication checks within the `onConnect` or `onSubscribe` lifecycle hooks. Ensure 'ws' is installed (`yarn add ws`). ```typescript // check extended implementation at `useServer` from `graphql-ws/use/ws` import http from 'http'; import { CloseCode, makeServer } from 'graphql-ws'; import { WebSocketServer } from 'ws'; // yarn add ws import { validate } from './my-auth'; import { schema } from './my-graphql-schema'; // extra in the context interface Extra { readonly request: http.IncomingMessage; } // your custom auth class Forbidden extends Error {} function handleAuth(request: http.IncomingMessage) { // do your auth on every subscription connect const good = validate(request.headers['authorization']); // or const { iDontApprove } = session(request.cookies); if (!good) { // throw a custom error to be handled throw new Forbidden(':('); } } // make graphql server const gqlServer = makeServer({ schema, onConnect: async (ctx) => { // do your auth on every connect (recommended) await handleAuth(ctx.extra.request); }, onSubscribe: async (ctx) => { // or maybe on every subscribe await handleAuth(ctx.extra.request); }, }); // create websocket server const wsServer = new WebSocketServer({ port: 4000, path: '/graphql', }); // implement wsServer.on('connection', (socket, request) => { // you may even reject the connection without ever reaching the lib // return socket.close(4403, 'Forbidden'); // pass the connection to graphql-ws const closed = gqlServer.opened( { protocol: socket.protocol, // will be validated send: (data) => new Promise((resolve, reject) => { // control your data flow by timing the promise resolve socket.send(data, (err) => (err ? reject(err) : resolve())); }), close: (code, reason) => socket.close(code, reason), // for standard closures onMessage: (cb) => { socket.on('message', async (event) => { try { // wait for the the operation to complete // - if init message, waits for connect // - if query/mutation, waits for result // - if subscription, waits for complete await cb(event.toString()); } catch (err) { // all errors that could be thrown during the // execution of operations will be caught here if (err instanceof Forbidden) { // your magic } else { socket.close(CloseCode.InternalServerError, err.message); } } }); }, }, // pass request to the extra { request }, ); // notify server that the socket closed socket.once('close', (code, reason) => closed(code, reason)); }); ``` -------------------------------- ### Start Server with @fastify/websocket Source: https://github.com/enisdenjo/graphql-ws/blob/master/website/src/pages/get-started.mdx Configure graphql-ws within a Fastify application using the @fastify/websocket plugin. ```ts import fastifyWebsocket from '@fastify/websocket'; // yarn add @fastify/websocket import Fastify from 'fastify'; // yarn add fastify import { makeHandler } from 'graphql-ws/use/@fastify/websocket'; import { schema } from './previous-step'; const fastify = Fastify(); fastify.register(fastifyWebsocket); fastify.register(async (fastify) => { fastify.get('/graphql', { websocket: true }, makeHandler({ schema })); }); fastify.listen(4000, (err) => { if (err) { fastify.log.error(err); return process.exit(1); } console.log('Listening to port 4000'); }); ``` -------------------------------- ### Start Server with uWebSockets.js Source: https://github.com/enisdenjo/graphql-ws/blob/master/website/src/pages/get-started.mdx Use graphql-ws with uWebSockets.js to handle GraphQL over WebSocket connections. ```ts import { makeBehavior } from 'graphql-ws/use/uWebSockets'; import uWS from 'uWebSockets.js'; // yarn add uWebSockets.js@uNetworking/uWebSockets.js# import { schema } from './previous-step'; uWS .App() .ws('/graphql', makeBehavior({ schema })) .listen(4000, (listenSocket) => { if (listenSocket) { console.log('Listening to port 4000'); } }); ``` -------------------------------- ### Server Usage with GraphQL Yoga and ws Source: https://github.com/enisdenjo/graphql-ws/blob/master/website/src/pages/recipes.mdx Integrates graphql-ws with GraphQL Yoga and the ws library. This setup allows for WebSocket subscriptions within a GraphQL Yoga server. Ensure 'graphql-yoga', 'graphql-ws', and 'ws' are installed. ```typescript import { createServer } from 'http'; import { useServer } from 'graphql-ws/use/ws'; import { createYoga } from 'graphql-yoga'; import { WebSocketServer } from 'ws'; import { schema } from './my-graphql-schema'; const yoga = createYoga({ schema, graphiql: { // Use WebSockets in GraphiQL subscriptionsProtocol: 'WS', }, }); // Get NodeJS Server from Yoga const server = createServer(yoga); // Create WebSocket server instance from our Node server const wsServer = new WebSocketServer({ server, path: yoga.graphqlEndpoint, }); // Integrate through Yoga's Envelop instance useServer( { execute: (args: any) => args.rootValue.execute(args), subscribe: (args: any) => args.rootValue.subscribe(args), onSubscribe: async (ctx, _id, payload) => { const { schema, execute, subscribe, contextFactory, parse, validate } = yoga.getEnveloped({ ...ctx, req: ctx.extra.request, socket: ctx.extra.socket, params: payload, }); const args = { schema, operationName: payload.operationName, document: parse(payload.query), variableValues: payload.variables, contextValue: await contextFactory(), rootValue: { execute, subscribe, }, }; const errors = validate(args.schema, args.document); if (errors.length) return errors; return args; }, }, wsServer, ); server.listen(4000, () => { console.log('Listening to port 4000'); }); ``` -------------------------------- ### Example TODO Comment Source: https://github.com/enisdenjo/graphql-ws/blob/master/CONTRIBUTING.md Follow this convention for comments in code, including TODO, FIXME, or NOTE, along with initials and date. ```plaintext // TODO-db-19001 leaving a todo note here for the next guy (or future me) ``` -------------------------------- ### ws Server with Custom Context Value Source: https://github.com/enisdenjo/graphql-ws/blob/master/website/src/pages/recipes.mdx This example demonstrates how to provide a custom context value to the GraphQL-WS server using the 'context' option. The context can be a static value or a function that dynamically generates the context based on the connection and subscription payload. Ensure 'ws' is installed using 'yarn add ws'. ```typescript import { useServer } from 'graphql-ws/use/ws'; import { WebSocketServer } from 'ws'; // yarn add ws import { getDynamicContext, schema } from './my-graphql'; const wsServer = new WebSocketServer({ port: 4000, path: '/graphql', }); useServer( { context: (ctx, id, subscribePayload, args) => { return getDynamicContext(ctx, id, subscribePayload, args); }, // or static context by supplying the value direcly schema, }, wsServer, ); ``` -------------------------------- ### Deno WebSocket Handler Source: https://context7.com/enisdenjo/graphql-ws/llms.txt This example demonstrates creating a WebSocket handler for Deno's native WebSocket API. It sets up the server and upgrades incoming requests to WebSocket connections. ```typescript import { makeHandler, GRAPHQL_TRANSPORT_WS_PROTOCOL } from 'https://esm.sh/graphql-ws/use/deno'; import { schema } from './schema.ts'; const handler = makeHandler({ schema, onConnect: (ctx) => { console.log('Client connected via Deno'); }, }); Deno.serve({ port: 4000 }, (req) => { const url = new URL(req.url); if (url.pathname !== '/graphql') { return new Response('Not Found', { status: 404 }); } if (req.headers.get('upgrade') !== 'websocket') { return new Response('Upgrade Required', { status: 426 }); } const { socket, response } = Deno.upgradeWebSocket(req, { protocol: GRAPHQL_TRANSPORT_WS_PROTOCOL, idleTimeout: 12000, }); handler(socket); return response; }); console.log('Deno GraphQL server running on ws://localhost:4000/graphql'); ``` -------------------------------- ### urql Integration with GraphQL-WS Source: https://context7.com/enisdenjo/graphql-ws/llms.txt Configure urql to use graphql-ws for subscriptions. This example shows how to set up the exchanges, forwarding subscription operations to the WebSocket client. ```typescript import { createClient as createUrqlClient, subscriptionExchange, fetchExchange } from 'urql'; import { createClient as createWsClient, SubscribePayload } from 'graphql-ws'; const wsClient = createWsClient({ url: 'ws://localhost:4000/graphql', }); const urqlClient = createUrqlClient({ url: 'http://localhost:4000/graphql', exchanges: [ fetchExchange, subscriptionExchange({ forwardSubscription: (operation) => ({ subscribe: (sink) => ({ unsubscribe: wsClient.subscribe(operation as SubscribePayload, sink), }), }), }), ], }); ``` -------------------------------- ### Server Usage with Express GraphQL and ws Source: https://github.com/enisdenjo/graphql-ws/blob/master/website/src/pages/recipes.mdx Integrates graphql-ws with Express GraphQL and the ws library. This setup enables WebSocket subscriptions within an Express application using express-graphql. Ensure 'express', 'express-graphql', 'graphql-ws', and 'ws' are installed. ```typescript import express from 'express'; import { graphqlHTTP } from 'express-graphql'; import { useServer } from 'graphql-ws/use/ws'; import { WebSocketServer } from 'ws'; // yarn add ws import { schema } from './my-graphql-schema'; // create express and middleware const app = express(); app.use('/graphql', graphqlHTTP({ schema })); const server = app.listen(4000, () => { // create and use the websocket server const wsServer = new WebSocketServer({ server, path: '/graphql', }); useServer({ schema }, wsServer); }); ``` -------------------------------- ### Server Usage with ws and Subprotocol Pings Source: https://github.com/enisdenjo/graphql-ws/blob/master/website/src/pages/recipes.mdx Integrates graphql-ws with the ws library, including custom subprotocol ping/pong handling for reliable connections. Ensure the 'ws' package is installed. ```typescript import { CloseCode, makeServer, MessageType, stringifyMessage, } from 'graphql-ws'; import { WebSocketServer } from 'ws'; // yarn add ws import { schema } from './my-graphql-schema'; // make const server = makeServer({ schema }); // create websocket server const wsServer = new WebSocketServer({ port: 4000, path: '/graphql', }); // implement wsServer.on('connection', (socket, request) => { // subprotocol pinger because WS level ping/pongs might not be available let pinger, pongWait; function ping() { if (socket.readyState === socket.OPEN) { // send the subprotocol level ping message socket.send(stringifyMessage({ type: MessageType.Ping })); // wait for the pong for 6 seconds and then terminate pongWait = setTimeout(() => { clearInterval(pinger); socket.close(); }, 6_000); } } // ping the client on an interval every 12 seconds pinger = setInterval(() => ping(), 12_000); // a new socket opened, let graphql-ws take over const closed = server.opened( { protocol: socket.protocol, // will be validated send: (data) => socket.send(data), close: (code, reason) => socket.close(code, reason), onMessage: (cb) => socket.on('message', async (event) => { try { // wait for the the operation to complete // - if init message, waits for connect // - if query/mutation, waits for result // - if subscription, waits for complete await cb(event.toString()); } catch (err) { // all errors that could be thrown during the // execution of operations will be caught here socket.close(CloseCode.InternalServerError, err.message); } }), // pong received, clear termination timeout onPong: () => clearTimeout(pongWait), }, // pass values to the `extra` field in the context { socket, request }, ); // notify server that the socket closed and stop the pinger socket.once('close', (code, reason) => { clearTimeout(pongWait); clearInterval(pinger); closed(code, reason); }); }); ``` -------------------------------- ### ws server with custom validation Source: https://github.com/enisdenjo/graphql-ws/blob/master/website/src/pages/recipes.mdx Implement custom validation rules for GraphQL operations. Ensure the 'graphql' package is installed. ```typescript import { validate } from 'graphql'; import { useServer } from 'graphql-ws/use/ws'; import { WebSocketServer } from 'ws'; // yarn add ws import { myValidationRules, schema } from './my-graphql'; const wsServer = new WebSocketServer({ port: 4000, path: '/graphql', }); useServer( { validate: (schema, document) => validate(schema, document, myValidationRules), }, wsServer, ); ``` -------------------------------- ### Apollo iOS WebSocket Integration Source: https://github.com/enisdenjo/graphql-ws/blob/master/website/src/pages/recipes.mdx Connect to a graphql-transport-ws compatible server using Apollo iOS in Swift. This example sets up both normal and WebSocket transports for a split network. ```swift import Foundation import Apollo import ApolloWebSocket let store = ApolloStore() let normalTransport = RequestChainNetworkTransport( interceptorProvider: DefaultInterceptorProvider(store: store), endpointURL: URL(string: "http://localhost:8080/graphql")! ) let webSocketClient = WebSocket( request: URLRequest(url: URL(string: "ws://localhost:8080/websocket")!), protocol: .graphql_transport_ws ) let webSocketTransport = WebSocketTransport( websocket: webSocketClient, store: store ) let splitTransport = SplitNetworkTransport( uploadingNetworkTransport: normalTransport, webSocketNetworkTransport: webSocketTransport ) let client = ApolloClient( networkTransport: splitTransport, store: store ) ``` -------------------------------- ### Client Usage with urql Source: https://github.com/enisdenjo/graphql-ws/blob/master/website/src/pages/recipes.mdx Integrates the graphql-ws client with urql for handling GraphQL subscriptions. This setup uses urql's `subscriptionExchange` to forward subscription operations to the WebSocket client. ```typescript import { createClient as createWSClient, SubscribePayload } from 'graphql-ws'; import { cacheExchange, createClient, fetchExchange, subscriptionExchange, } from 'urql'; const wsClient = createWSClient({ url: 'ws://its.urql:4000/graphql', }); const client = createClient({ url: '/graphql', exchanges: [ cacheExchange, fetchExchange, subscriptionExchange({ forwardSubscription(operation) { return { subscribe: (sink) => { const dispose = wsClient.subscribe( operation as SubscribePayload, sink, ); return { unsubscribe: dispose, }; }, }; }, }), ], }); ``` -------------------------------- ### Apollo Client Integration with GraphQL-WS Source: https://context7.com/enisdenjo/graphql-ws/llms.txt Integrate graphql-ws with Apollo Client to handle GraphQL subscriptions. This setup splits traffic between HTTP for queries/mutations and WebSocket for subscriptions. ```typescript import { ApolloClient, InMemoryCache, split, HttpLink } from '@apollo/client/core'; import { GraphQLWsLink } from '@apollo/client/link/subscriptions'; import { getMainDefinition } from '@apollo/client/utilities'; import { createClient } from 'graphql-ws'; const httpLink = new HttpLink({ uri: 'http://localhost:4000/graphql', }); const wsLink = new GraphQLWsLink( createClient({ url: 'ws://localhost:4000/graphql', connectionParams: () => ({ authToken: localStorage.getItem('token'), }), }) ); // Split traffic between HTTP and WebSocket const splitLink = split( ({ query }) => { const definition = getMainDefinition(query); return ( definition.kind === 'OperationDefinition' && definition.operation === 'subscription' ); }, wsLink, httpLink, ); const apolloClient = new ApolloClient({ link: splitLink, cache: new InMemoryCache(), }); ``` -------------------------------- ### Using the Augmented Client for Subscriptions Source: https://github.com/enisdenjo/graphql-ws/blob/master/website/src/pages/recipes.mdx Demonstrates how to use the `createClientWithSubscribeAck` function to establish a subscription and handle its acknowledgment. The `subscriptionAcknowledged` callback is invoked when the server confirms the subscription. ```typescript const client = createClientWithSubscribeAck({ url: 'ws://i.want.ack:4000/graphql', }); (async () => { const onNext = () => { /* handle incoming values */ }; let unsubscribe = () => { /* complete the subscription */ }; let subscriptionAcknowledged = () => { /* server successfully subscribed */ }; await new Promise((resolve, reject) => { unsubscribe = client.subscribe( { query: 'subscription { greetings }', }, { next: onNext, error: reject, complete: resolve, }, subscriptionAcknowledged, ); }); expect(subscriptionAcknowledged).toBeCalledFirst(); expect(onNext).then.toBeCalledTimes(5); // we say "Hi" in 5 languages })(); ``` -------------------------------- ### Client Usage in Browser (UMD) Source: https://github.com/enisdenjo/graphql-ws/blob/master/website/src/pages/recipes.mdx Integrate the graphql-ws client in a web browser using the UMD build. Include the script tag and then create the client instance using the global `graphqlWs` object. ```html GraphQL over WebSocket ``` -------------------------------- ### Client Usage with Promise Source: https://github.com/enisdenjo/graphql-ws/blob/master/website/src/pages/recipes.mdx Demonstrates how to use the graphql-ws client with Promises for handling GraphQL subscriptions. Ensure the client is created with the correct WebSocket URL. ```typescript import { createClient, SubscribePayload } from 'graphql-ws'; const client = createClient({ url: 'ws://hey.there:4000/graphql', }); (async () => { const query = client.iterate({ query: '{ hello }', }); try { const { value } = await query.next(); // next = value = { data: { hello: 'Hello World!' } } // complete } catch (err) { // error } })(); ``` -------------------------------- ### Create a GraphQL WebSocket Client Source: https://context7.com/enisdenjo/graphql-ws/llms.txt Use `createClient` to initialize a GraphQL over WebSocket client. Configure connection URL, authentication, lazy connection, retry strategies, and event handlers. For Node.js, specify the WebSocket implementation. ```typescript import { createClient } from 'graphql-ws'; // Basic client creation const client = createClient({ url: 'ws://localhost:4000/graphql', }); // Client with full configuration const configuredClient = createClient({ url: 'ws://localhost:4000/graphql', connectionParams: async () => { const token = await getAuthToken(); return { Authorization: `Bearer ${token}` }; }, lazy: true, // Connect on first subscribe, disconnect on last unsubscribe lazyCloseTimeout: 3000, // Wait 3s before closing idle connection keepAlive: 10000, // Ping server every 10 seconds retryAttempts: 5, // Retry connection up to 5 times connectionAckWaitTimeout: 5000, // Wait 5s for connection acknowledgement shouldRetry: (errOrCloseEvent) => true, // Retry on any close event on: { connecting: (isRetry) => console.log('Connecting...', { isRetry }), connected: (socket, payload, wasRetry) => console.log('Connected!', { payload, wasRetry }), closed: (event) => console.log('Connection closed', event), error: (error) => console.error('Connection error', error), ping: (received, payload) => console.log('Ping', { received, payload }), pong: (received, payload) => console.log('Pong', { received, payload }), }, }); // For Node.js environments, provide WebSocket implementation import WebSocket from 'ws'; const nodeClient = createClient({ url: 'ws://localhost:4000/graphql', webSocketImpl: WebSocket, }); ``` -------------------------------- ### GraphQL-WS Server with Persisted Queries Source: https://github.com/enisdenjo/graphql-ws/blob/master/website/src/pages/recipes.mdx Sets up a WebSocket server to handle GraphQL subscriptions using persisted queries. It requires a predefined `queriesStore` and uses `graphql-ws/use/ws` to integrate with the `ws` library. Only queries defined in the store are allowed. ```typescript // 🛸 server import { ExecutionArgs, parse } from 'graphql'; import { useServer } from 'graphql-ws/use/ws'; import { WebSocketServer } from 'ws'; // yarn add ws import { schema } from './my-graphql-schema'; // a unique GraphQL execution ID used for representing // a query in the persisted queries store. when subscribing // you should use the `SubscriptionPayload.query` to transmit the id type QueryID = string; const queriesStore: Record = { iWantTheGreetings: { schema, // you may even provide different schemas in the queries store document: parse('subscription Greetings { greetings }'), }, }; const wsServer = new WebSocketServer({ port: 4000, path: '/graphql', }); useServer( { onSubscribe: (_ctx, _id, payload) => { const persistedQuery = queriesStore[payload.extensions?.persistedQuery]; if (persistedQuery) { return { ...persistedQuery, variableValues: payload.variables, // use the variables from the client }; } // for extra security you only allow the queries from the store. // if you want to support both, simply remove the throw below and // graphql-ws will handle the query for you throw new Error('404: Query Not Found'); }, }, wsServer, ); ``` -------------------------------- ### GraphQL-WS Server with Subscription Acknowledgement Source: https://github.com/enisdenjo/graphql-ws/blob/master/website/src/pages/recipes.mdx Sets up a WebSocket server using 'ws' and integrates GraphQL-WS with acknowledgment logic. The `onConnect` handler initializes acknowledgment waiters, `onSubscribe` prepares for acknowledgments by storing waiter functions, and `onOperation` executes the acknowledgment when an operation is successful. ```typescript // 🛸 server import { MessageType, stringifyMessage } from 'graphql-ws'; import { useServer } from 'graphql-ws/use/ws'; import { WebSocketServer } from 'ws'; import { schema } from './my-graphql-schema'; const wsServer = new WebSocketServer({ port: 4000, path: '/graphql', }); useServer void> }>( { schema, onConnect: (ctx) => { // listeners waiting for operation acknowledgment. if subscription, this means the graphql.subscribe was successful // intentionally in context extra to avoid memory leaks when clients disconnect ctx.extra.ackWaiters = {}; }, onSubscribe: (ctx, id, payload) => { const ackId = payload.extensions?.ackId; if (typeof ackId === 'string') { // if acknowledgment ID is present, create an acknowledger that will be executed when operation succeeds ctx.extra.ackWaiters![id] = () => { ctx.extra.socket.send( stringifyMessage({ type: MessageType.Ping, payload: { ackId, }, }), ); }; } }, onOperation: (ctx, id) => { // acknowledge operation success and remove waiter ctx.extra.ackWaiters![id]?.(); delete ctx.extra.ackWaiters![id]; }, }, wsServer, ); console.log('Listening to port 4000'); ``` -------------------------------- ### GraphiQL with graphql-ws Fetcher Source: https://github.com/enisdenjo/graphql-ws/blob/master/website/src/pages/recipes.mdx Configure GraphiQL to use graphql-ws for subscriptions. This involves creating a fetcher with both a URL for queries/mutations and a wsClient for subscriptions. ```typescript import React from 'react'; import ReactDOM from 'react-dom'; import { GraphiQL } from 'graphiql'; import { createGraphiQLFetcher } from '@graphiql/toolkit'; import { createClient } from 'graphql-ws'; const fetcher = createGraphiQLFetcher({ url: 'https://myschema.com/graphql', wsClient: createClient({ url: 'wss://myschema.com/graphql', }), }); export const App = () => ; ReactDOM.render(document.getElementByID('graphiql'), ); ``` -------------------------------- ### ws server with custom execution arguments Source: https://github.com/enisdenjo/graphql-ws/blob/master/website/src/pages/recipes.mdx Provide custom execution arguments, including schema, operation name, document, and variables. Remember to validate when returning custom arguments. ```typescript import { parse, validate } from 'graphql'; import { useServer } from 'graphql-ws/use/ws'; import { WebSocketServer } from 'ws'; // yarn add ws import { myValidationRules, schema } from './my-graphql'; const wsServer = new WebSocketServer({ port: 4000, path: '/graphql', }); useServer( { onSubscribe: (ctx, _id, payload) => { const args = { schema, operationName: payload.operationName, document: parse(payload.query), variableValues: payload.variables, }; // dont forget to validate when returning custom execution args! const errors = validate(args.schema, args.document, myValidationRules); if (errors.length > 0) { return errors; // return `GraphQLError[]` to send `ErrorMessage` and stop subscription } return args; }, }, wsServer, ); ``` -------------------------------- ### Create Server for Custom WebSocket Implementation Source: https://context7.com/enisdenjo/graphql-ws/llms.txt Creates a protocol-compliant server API for custom WebSocket implementations. Gives full control over the WebSocket layer. Handles connection opening, message routing, and closing. ```typescript import { makeServer, CloseCode } from 'graphql-ws'; import { WebSocketServer } from 'ws'; import { schema } from './schema'; const server = makeServer({ schema, onConnect: async (ctx) => { if (!ctx.connectionParams?.token) { return false; // Close with 4403: Forbidden } return true; }, }); const wsServer = new WebSocketServer({ port: 4000, path: '/graphql' }); wsServer.on('connection', (socket, request) => { // Pass connection to graphql-ws const closed = server.opened( { protocol: socket.protocol, // Validated by the server send: (data) => new Promise((resolve, reject) => { if (socket.readyState !== socket.OPEN) return resolve(); socket.send(data, (err) => (err ? reject(err) : resolve())); }), close: (code, reason) => socket.close(code, reason), onMessage: (cb) => { socket.on('message', async (event) => { try { await cb(String(event)); } catch (err) { socket.close(CloseCode.InternalServerError, err.message); } }); }, onPing: (payload) => { console.log('Received ping from client', payload); // Custom pong handling if needed }, onPong: (payload) => { console.log('Received pong from client', payload); }, }, { socket, request }, // Extra context ); socket.once('close', (code, reason) => { closed(code, String(reason)); }); }); ``` -------------------------------- ### ConnectionInit Message Source: https://github.com/enisdenjo/graphql-ws/blob/master/PROTOCOL.md Client-to-server message to establish a connection within an existing socket. The server has a timeout for receiving this message. ```APIDOC ## ConnectionInit Message ### Description Indicates that the client wants to establish a connection within the existing socket. This connection is **not** the actual WebSocket communication channel, but is rather a frame within it asking the server to allow operation requests. The server must receive the connection initialisation message within the allowed waiting time specified in the `connectionInitWaitTimeout` parameter during the server setup. If the client does not request a connection within the allowed timeout, the server will close the socket with the event: `4408: Connection initialisation timeout`. If the server receives more than one `ConnectionInit` message at any given time, the server will close the socket with the event `4429: Too many initialisation requests`. If the server wishes to reject the connection, for example during authentication, it is recommended to close the socket with `4403: Forbidden`. ### Message Structure ```typescript interface ConnectionInitMessage { type: 'connection_init'; payload?: Record | null; } ``` ``` -------------------------------- ### ws Server with subscriptions-transport-ws Backwards Compatibility Source: https://github.com/enisdenjo/graphql-ws/blob/master/website/src/pages/recipes.mdx This snippet demonstrates how to configure a 'ws' server to support both 'graphql-ws' and 'subscriptions-transport-ws' protocols. It sets up two separate WebSocket servers and delegates incoming upgrade requests based on the 'sec-websocket-protocol' header. ```typescript import http from 'http'; import { execute, subscribe } from 'graphql'; import { GRAPHQL_TRANSPORT_WS_PROTOCOL } from 'graphql-ws'; import { useServer } from 'graphql-ws/use/ws'; import { GRAPHQL_WS, SubscriptionServer } from 'subscriptions-transport-ws'; import { WebSocketServer } from 'ws'; // yarn add ws import { schema } from './my-graphql-schema'; // graphql-ws const graphqlWs = new WebSocketServer({ noServer: true }); useServer({ schema }, graphqlWs); // subscriptions-transport-ws const subTransWs = new WebSocketServer({ noServer: true }); SubscriptionServer.create( { schema, execute, subscribe, }, subTransWs, ); // create http server const server = http.createServer(function weServeSocketsOnly(_, res) { res.writeHead(404); res.end(); }); // listen for upgrades and delegate requests according to the WS subprotocol server.on('upgrade', (req, socket, head) => { // extract websocket subprotocol from header const protocol = req.headers['sec-websocket-protocol']; const protocols = Array.isArray(protocol) ? protocol : protocol?.split(',').map((p) => p.trim()); // decide which websocket server to use const wss = protocols?.includes(GRAPHQL_WS) && // subscriptions-transport-ws subprotocol !protocols.includes(GRAPHQL_TRANSPORT_WS_PROTOCOL) // graphql-ws subprotocol ? subTransWs : // graphql-ws will welcome its own subprotocol and // gracefully reject invalid ones. if the client supports // both transports, graphql-ws will prevail graphqlWs; wss.handleUpgrade(req, socket, head, (ws) => { wss.emit('connection', ws, req); }); }); server.listen(4000); ``` -------------------------------- ### ws Server with deprecated fastify-websocket Source: https://github.com/enisdenjo/graphql-ws/blob/master/website/src/pages/recipes.mdx Demonstrates integrating GraphQL-WS with Fastify using the deprecated 'fastify-websocket' plugin. Note that this plugin is no longer recommended for new projects. ```typescript import Fastify from 'fastify'; // yarn add fastify@^3 import fastifyWebsocket from 'fastify-websocket'; // yarn add fastify-websocket@4.2.2 import { makeHandler } from 'graphql-ws/use/fastify-websocket'; import { schema } from './previous-step'; const fastify = Fastify(); fastify.register(fastifyWebsocket); fastify.get('/graphql', { websocket: true }, makeHandler({ schema })); fastify.listen(4000, (err) => { if (err) { fastify.log.error(err); return process.exit(1); } console.log('Listening to port 4000'); }); ``` -------------------------------- ### createClient - GraphQL WebSocket Client Source: https://context7.com/enisdenjo/graphql-ws/llms.txt Creates a disposable GraphQL over WebSocket client that manages connections, subscriptions, and automatic reconnection. The client supports lazy connections, custom WebSocket implementations, and configurable retry strategies. ```APIDOC ## createClient - GraphQL WebSocket Client ### Description Creates a disposable GraphQL over WebSocket client that manages connections, subscriptions, and automatic reconnection. The client supports lazy connections, custom WebSocket implementations, and configurable retry strategies. ### Method `createClient` ### Parameters #### Configuration Object - **url** (string) - Required - The WebSocket URL for the GraphQL endpoint. - **connectionParams** (object | function) - Optional - Parameters to send during connection, can be an object or a function returning an object. - **lazy** (boolean) - Optional - If true, the connection is established only on the first subscription and closed on the last unsubscribe. Defaults to false. - **lazyCloseTimeout** (number) - Optional - Time in milliseconds to wait before closing an idle connection when `lazy` is true. Defaults to 0. - **keepAlive** (number) - Optional - Interval in milliseconds to send ping frames to keep the connection alive. Defaults to 0 (disabled). - **retryAttempts** (number) - Optional - Maximum number of times to retry the connection. Defaults to 0. - **connectionAckWaitTimeout** (number) - Optional - Time in milliseconds to wait for the server's connection acknowledgement. Defaults to 10000. - **shouldRetry** (function) - Optional - A function that determines if a connection should be retried based on the close event or error. Defaults to `(errOrCloseEvent) => false`. - **on** (object) - Optional - Event handlers for connection lifecycle events. - **connecting** (function) - Called when the client is attempting to connect. - **connected** (function) - Called when the connection is successfully established. - **closed** (function) - Called when the connection is closed. - **error** (function) - Called when a connection error occurs. - **ping** (function) - Called when a ping frame is received. - **pong** (function) - Called when a pong frame is received. - **webSocketImpl** (object) - Optional - A custom WebSocket implementation (e.g., for Node.js environments). ### Request Example ```typescript import { createClient } from 'graphql-ws'; // Basic client creation const client = createClient({ url: 'ws://localhost:4000/graphql', }); // Client with full configuration const configuredClient = createClient({ url: 'ws://localhost:4000/graphql', connectionParams: async () => { const token = await getAuthToken(); return { Authorization: `Bearer ${token}` }; }, lazy: true, lazyCloseTimeout: 3000, keepAlive: 10000, retryAttempts: 5, connectionAckWaitTimeout: 5000, shouldRetry: (errOrCloseEvent) => true, on: { connecting: (isRetry) => console.log('Connecting...', { isRetry }), connected: (socket, payload, wasRetry) => console.log('Connected!', { payload, wasRetry }), closed: (event) => console.log('Connection closed', event), error: (error) => console.error('Connection error', error), ping: (received, payload) => console.log('Ping', { received, payload }), pong: (received, payload) => console.log('Pong', { received, payload }), }, }); // For Node.js environments, provide WebSocket implementation import WebSocket from 'ws'; const nodeClient = createClient({ url: 'ws://localhost:4000/graphql', webSocketImpl: WebSocket, }); ``` ``` -------------------------------- ### Use Server with ws WebSocket Library Source: https://context7.com/enisdenjo/graphql-ws/llms.txt Integrates the GraphQL WebSocket server with the `ws` library. Handles connection lifecycle, keep-alive pings, and protocol validation. Configure connection context, authentication, and lifecycle events. ```typescript import { useServer } from 'graphql-ws/use/ws'; import { WebSocketServer } from 'ws'; import { buildSchema } from 'graphql'; const schema = buildSchema(` type Query { hello: String } type Subscription { countdown(from: Int!): Int } `); const roots = { Query: { hello: () => 'Hello World!', }, Subscription: { countdown: async function* (_, { from }) { for (let i = from; i >= 0; i--) { yield { countdown: i }; await new Promise(resolve => setTimeout(resolve, 1000)); } }, }, }; const wsServer = new WebSocketServer({ port: 4000, path: '/graphql', }); const serverCleanup = useServer( { schema, roots, context: (ctx, id, payload, args) => { // Access connection info return { user: ctx.connectionParams?.user, request: ctx.extra.request, }; }, onConnect: async (ctx) => { // Authenticate on connect const token = ctx.connectionParams?.token; if (!isValidToken(token)) { return false; // Reject connection with 4403: Forbidden } // Return payload to send with ConnectionAck return { serverTime: Date.now() }; }, onDisconnect: (ctx, code, reason) => { console.log('Client disconnected', { code, reason }); }, onSubscribe: (ctx, id, payload) => { console.log('Subscription started', { id, query: payload.query }); }, onNext: (ctx, id, payload, args, result) => { console.log('Sending result', { id, result }); }, onComplete: (ctx, id, payload) => { console.log('Operation completed', { id }); }, }, wsServer, 12000, // Keep-alive interval in ms (default: 12000) ); console.log('GraphQL WebSocket server running on ws://localhost:4000/graphql'); // Graceful shutdown process.on('SIGTERM', async () => { await serverCleanup.dispose(); }); ```