Skip to content

Releases: enisdenjo/graphql-ws

v5.5.0

08 Sep 21:26
Compare
Choose a tag to compare

5.5.0 (2021-09-08)

Bug Fixes

  • Define graphql execution results (a64c91b)
  • server: Operation result can be async generator or iterable (b1fb883)

Features

  • client: Add connectionAckWaitTimeout option (#228) (35ce054)

v5.4.1

26 Aug 19:16
Compare
Choose a tag to compare

5.4.1 (2021-08-26)

Bug Fixes

  • Add support for graphql@v16 (ad5aea2)
  • Sink's next callback always receives an ExecutionResult (045b402)

v5.4.0

21 Aug 11:04
Compare
Choose a tag to compare

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

  • Centralise expected close codes in CloseCode enum (d10a75c)
  • server: Add support for ws@v8 (9119153)

v5.3.0

23 Jun 14:15
Compare
Choose a tag to compare

5.3.0 (2021-06-23)

Bug Fixes

  • client: ConnectionInit payload is absent if connectionParams returns nothing (98f8265)

Features

  • client: connectionParams can return undefined (a543187)
  • client: Add opened event for when a WebSocket opens (9053224)

v5.2.0

21 Jun 17:43
Compare
Choose a tag to compare

5.2.0 (2021-06-21)

Features

  • server: Optional onPing and onPong 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

09 Jun 18:16
Compare
Choose a tag to compare

5.1.2 (2021-06-09)

Bug Fixes

  • client: Return ping's payload through the response pong (ee6193a)

v5.1.1

09 Jun 17:01
Compare
Choose a tag to compare

5.1.1 (2021-06-09)

Bug Fixes

  • server: Return ping's payload through the response pong (47730a9), closes #117

v5.1.0

09 Jun 11:22
Compare
Choose a tag to compare

5.1.0 (2021-06-09)

Features

  • client: disablePong option for when implementing a custom pinger (6510360), closes #117
  • Optional payload for ping/pong message types (2fe0345), closes #117

v5.0.0

08 Jun 20:39
Compare
Choose a tag to compare

5.0.0 (2021-06-08)

Features

  • Bidirectional ping/pong message types (#201) (1efaf83), closes #117
  • client: Rename keepAlive option to lazyCloseTimeout (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 internal upgrade callback.
  • client: Client keepAlive option has been renamed to lazyCloseTimeout in order to eliminate ambiguity with the client to server pings keep-alive option.

v4.9.0

06 Jun 13:07
Compare
Choose a tag to compare

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');
});