Releases: enisdenjo/graphql-ws
Releases · enisdenjo/graphql-ws
v5.5.0
v5.4.1
v5.4.0
5.4.0 (2021-08-21)
Bug Fixes
- client: Specify and fail on fatal internal WebSocket close codes (a720125)
- Use
4406
close code for unsupported subprotocol (1002
is an internal WebSocket close code) (df85281) - Use
4500
close code for internal server errors (1011
is an internal WebSocket close code) (3c0316d)
Features
v5.3.0
v5.2.0
5.2.0 (2021-06-21)
Features
- server: Optional
onPing
andonPong
message type listeners (f36066f)
Server usage with ws and subprotocol pings and pongs
import ws from 'ws'; // yarn add ws
import { makeServer, stringifyMessage, MessageType, CloseCode } from 'graphql-ws';
import { schema } from './my-graphql-schema';
// make
const server = makeServer({ schema });
// create websocket server
const wsServer = new ws.Server({
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);
});
});
v5.1.2
v5.1.1
v5.1.0
v5.0.0
5.0.0 (2021-06-08)
Features
- Bidirectional ping/pong message types (#201) (1efaf83), closes #117
- client: Rename
keepAlive
option tolazyCloseTimeout
(3c1f13c) - uWebSockets: Drop deprecated
request
context extra (02ea5ee)
BREAKING CHANGES
- Because of the Protocol's strictness, an instant connection termination will happen whenever an invalid message is identified; meaning, all previous implementations will fail when receiving the new subprotocol ping/pong messages.
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
}
},
},
});
- uWebSockets: The deprecated uWebSockets
request
context extra field has been dropped because it is stack allocated and cannot be used ouside the internalupgrade
callback. - client: Client
keepAlive
option has been renamed tolazyCloseTimeout
in order to eliminate ambiguity with the client to server pings keep-alive option.
v4.9.0
4.9.0 (2021-06-06)
Features
Server usage with fastify-websocket
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');
});