NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
npm · #2686 most downloaded on npm
Coherent, zero-dependency, lazy, simple, GraphQL over WebSocket Protocol compliant server and client
Last release 10 days ago
24 Sep 2026
Release timing varies
gaps range from 9 days to 7 months
Nearly every release is documented
notes for 60 of the last 60 stable releases
16 versions withdrawn
withdrawn after publishing
9 years old
185 releases · first in 2017
client: Return ping's payload through the response pong
One column per quarter.
client: disablePong option for when implementing a custom pinger (6510360), closes #117
uWebSockets: Drop deprecated request context extra
keepAlive option to lazyCloseTimeout (3c1f13c)request context extra (02ea5ee)Beware, the client will NOT ping the server by default. Please make sure to upgrade your stack in order to support the new ping/pong message types.
A simple recipe showcasing a client that times out if no pong is received and measures latency, looks like this:
import { createClient } from 'graphql-ws';
let activeSocket,
timedOut,
pingSentAt = 0,
latency = 0;
createClient({
url: 'ws://i.time.out:4000/and-measure/latency',
keepAlive: 10_000, // ping server every 10 seconds
on: {
connected: (socket) => (activeSocket = socket),
ping: (received) => {
if (!received /* sent */) {
pingSentAt = Date.now();
timedOut = setTimeout(() => {
if (activeSocket.readyState === WebSocket.OPEN)
activeSocket.close(4408, 'Request Timeout');
}, 5_000); // wait 5 seconds for the pong and then close the connection
}
},
pong: (received) => {
if (received) {
latency = Date.now() - pingSentAt;
clearTimeout(timedOut); // pong is received, clear connection close timeout
}
},
},
});
request context extra field has been dropped because it is stack allocated and cannot be used ouside the internal upgrade callback.keepAlive option has been renamed to lazyCloseTimeout in order to eliminate ambiguity with the client to server pings keep-alive option.```ts import Fastify from 'fastify'; // yarn add fastify import fastifyWebsocket from 'fastify-websocket'; // yarn add fastify-websocket import { make
import Fastify from 'fastify'; // yarn add fastify
import fastifyWebsocket from 'fastify-websocket'; // yarn add fastify-websocket
import { makeHandler } from 'graphql-ws/lib/use/fastify-websocket';
const fastify = Fastify();
fastify.register(fastifyWebsocket);
fastify.get(
'/graphql',
{ websocket: true },
makeHandler(
// from the previous step
{ schema, roots },
),
);
fastify.listen(4000, (err) => {
if (err) {
fastify.log.error(err);
return process.exit(1);
}
console.log('Listening to port 4000');
});
uWebSockets: Add persistedRequest to context extra and deprecate uWS's stack allocated request
Add extensions field to the subscribe message payload
use: Generic for extending the context extras (401cd4c), closes #189
uWebSockets: Handle premature and abrupt socket closes (9d3ff52), closes #186
server: Init context first on connection open (a80e753), closes #181
Support custom JSON message reviver and replacer
client: complete should not be called after subscription error
complete should not be called after subscription error (1fba419)complete not being called after subscription errorPromises and Async Iterators can reject/throw/error or resolve/return/complete only once, subsequent calls to either will simply be ignored.
Furthermore, as per the Observer pattern, from RxJS docs:
In an Observable Execution, zero to infinite Next notifications may be delivered. If either an Error or Complete notification is delivered, then nothing else can be delivered afterwards.
client: Subscribes even if socket is in CLOSING state due to all subscriptions being completed (3e3b8b7), closes #173 #170
client: Lazy connects after successful reconnects are not retries
Add uWebSockets exports path (36247cb), closes #155
server: Use uWebSockets (#89) (45d08fc), closes #61
import uWS from 'uWebSockets.js'; // yarn add uWebSockets.js@uNetworking/uWebSockets.js#<tag>
import { makeBehavior } from 'graphql-ws/lib/use/uWebSockets';
import { schema } from './my-graphql-schema';
uWS
.App()
.ws('/graphql/is-performant', makeBehavior({ schema }))
.listen(4000, (listenSocket) => {
if (listenSocket) {
console.log('Listening to port 4000');
}
});
client: Subscriptions acquire locks
client: Connection locks dont increment on retries (1e7bd97), closes #153
server: Async iterator must implement return (d99982b), closes #149
Close the details tag in the README
server: Respect completed subscriptions even if subscribe or onOperation didnt resolve yet
subscribe or onOperation didnt resolve yet (4700154)url option accepts a function or a Promise (#143) (76f522f), closes #145 #146execute and subscribe are optional (#148) (af748b0)schema support by accepting a function or a Promise (#147) (6a0bf94), closes #127validate option for custom GraphQL validation (b68d56c)client: Reduce WebSocket event listeners and add new client message event (#104) (68d0e20), closes #102
server: return instead of break at switch case ends (e9447e4), closes #140
client: New error event listener for handling connection errors (#136) (127b69f), closes #135
Only UMD build has side effects
Main entrypoint in exports is just "."
Define entry points through the exports field and use .mjs suffixed ESM imports
client: Should emit closed event when disposing (5800de8), closes #108
client: Export relevant elements from the browser bundle (b106dbe), closes #97
server: onDisconnect is called exclusively if the connection is acknowledged
server: Client can complete/cancel any operation
onDisconnect callback (#94) (2a61268)server.opened (closed) now requires the close event code and reason for reporting to the onDisconnect callback.Context.subscriptions record value can be either an AsyncIterator or a Promise.# 3.2.0 (2020-12-17) ### Features * Package ECMAScript Modules too
client: Time retries and socket change waits (7c707db), closes #85
client: No retries when disposed
client: Await timeouts only in recursive connects
client: Retry with randomised exponential backoff or provide your own strategy
retryTimeout option has been replaced with the new retryWait.retryWait allows you to control the retry timeout strategy by resolving the returned promise when ready. The default implements the randomised exponential backoff like so:
// this is the default
const retryWait = async function randomisedExponentialBackoff(retries: number) {
let retryDelay = 1000; // start with 1s delay
for (let i = 0; i < retries; i++) {
retryDelay *= 2; // square `retries` times
}
await new Promise((resolve) =>
setTimeout(
// resolve pending promise with added random timeout from 300ms to 3s
resolve,
retryDelay + Math.floor(Math.random() * (3000 - 300) + 300),
),
);
};
client: Close event's wasClean is not necessary (2c65f0e), closes #81
server: Make and use with your own flavour (#64) (38bde87), closes #61 #73 #75
Summary of breaking changes:
keepAlive. The user should provide its own keep-alive implementation. (I highly recommend WebSocket Ping and Pongs)request in the server context.makeServer (no more createServer)extra field in the Context for storing custom values useful for callbacksOnly the server has to be migrated. Since this release allows you to use your favourite WebSocket library (or your own implementation), using ws is just one way of using graphql-ws. This is how to use the implementation shipped with the lib:
/**
* ❌ instead of the lib creating a WebSocket server internally with the provided arguments
*/
import https from 'https';
import { createServer } from 'graphql-ws';
const server = https.createServer(...);
createServer(
{
onConnect(ctx) {
// were previously directly on the context
ctx.request as IncomingRequest
ctx.socket as WebSocket
},
...rest,
},
{
server,
path: '/graphql',
},
);
/**
* ✅ you have to supply the server yourself
*/
import https from 'https';
import ws from 'ws'; // yarn add ws
import { useServer } from 'graphql-ws/lib/use/ws'; // notice the import path
const server = https.createServer(...);
const wsServer = new ws.Server({
server,
path: '/graphql',
});
useServer(
{
onConnect(ctx) {
// are now in the `extra` field
ctx.extra.request as IncomingRequest
ctx.extra.socket as WebSocket
},
...rest,
},
wsServer,
// optional keepAlive with ping pongs (defaults to 12 seconds)
);
server: context may return a promise (cd5c2f8), closes #74
client: Some close events are not worth retrying
client: Close with error message during connecting issues
Node 10 is the min supported version
graphql versions (de69b4e)onSubscribe returns invalid array (#53) (0464a54)rootValue and contextValue, if not overridden (#49) (7aa3bcd)Drop TypeScript DOM lib dependency
server: Make sure to use onSubscribe result exclusively
Package rename graphql-transport-ws 👉 graphql-ws.
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Your coding agent can read these notes before it upgrades. Set up the MCP server →