GraphQL SSE Library
graphql-sse is a lightweight, zero-dependency (aside from a `graphql` peer dependency) library that enables GraphQL operations, particularly subscriptions, over the Server-Sent Events (SSE) protocol. It provides both server-side handler utilities and a client for consuming SSE streams. The current stable version is 2.6.0, with releases occurring semi-regularly, focusing on bug fixes, feature enhancements like dynamic client URLs, and new framework integrations (e.g., Koa, Fastify, Express adapters). Its key differentiator is its exclusive use of SSE, making it HTTP/1 safe and suitable for environments where WebSockets might be problematic, offering a robust alternative to `graphql-ws` for subscription-heavy applications without requiring a separate WebSocket server infrastructure. It is designed for simplicity and direct integration with existing HTTP servers.
Common errors
-
Cannot find module 'graphql-sse/use/express' or its corresponding type declarations.
cause Incorrect import path for framework adapters or issues with bundler configuration not recognizing subpath exports.fixVerify the exact subpath export in `package.json` (e.g., `graphql-sse/use/express`). Ensure your bundler (Webpack, Rollup, Vite) is correctly configured to handle Node.js subpath exports, especially in older versions or non-standard setups. -
Client receives no events, or connection closes unexpectedly.
cause Server not correctly configured to send SSE (e.g., missing `Content-Type: text/event-stream` header, incorrect `Cache-Control`), network issue (firewall, proxy dropping long-lived HTTP connections), or incorrect client `url`.fixDouble-check server configuration for `createHandler` and the chosen `useServer` adapter. Ensure the response headers include `Content-Type: text/event-stream` and `Cache-Control: no-cache, no-transform`. Verify the client `url` is correct and accessible, and that any proxies allow SSE traffic. -
Error: Must provide document and operationName.
cause This is a common GraphQL execution error indicating the client is sending an invalid or incomplete GraphQL request body to the server.fixReview the client-side `iterate` call to ensure the query string is valid GraphQL and any necessary variables are correctly passed as the second argument. Confirm that your server's `createHandler` is receiving and parsing the request body correctly (e.g., `express.json()` middleware for Express).
Warnings
- breaking The v2.0.0 release of graphql-sse was a complete rewrite introducing significant breaking changes from v1.x. The API for server handler creation and client instantiation was overhauled.
- gotcha graphql-sse exclusively uses Server-Sent Events (SSE) for GraphQL operations, primarily subscriptions. It does not use WebSockets. Do not attempt to integrate with WebSocket clients or expect bidirectional communication typical of WebSocket-based GraphQL libraries.
- gotcha The library has a peer dependency on 'graphql' with a specified version range (currently '>=0.11 <=16'). Using an incompatible version of 'graphql' in your project can lead to schema parsing, validation, or execution errors at runtime.
Install
-
npm install graphql-sse -
yarn add graphql-sse -
pnpm add graphql-sse
Imports
- createHandler
const { createHandler } = require('graphql-sse');import { createHandler } from 'graphql-sse'; - createClient
const { createClient } = require('graphql-sse');import { createClient } from 'graphql-sse'; - useServer (e.g., for Express)
import { useServer } from 'graphql-sse'; // Incorrect subpath import { useExpress } from 'graphql-sse/use/express'; // Incorrect symbol nameimport { useServer } from 'graphql-sse/use/express';
Quickstart
import express from 'express';
import { createHandler } from 'graphql-sse';
import { useServer } from 'graphql-sse/use/express'; // Or use/fastify, use/koa, etc.
import { makeExecutableSchema } from '@graphql-tools/schema';
import { createClient } from 'graphql-sse';
// --- Server Setup ---
const typeDefs = `
type Query {
hello: String
}
type Subscription {
countdown(from: Int!): Int!
}
`;
const resolvers = {
Query: {
hello: () => 'world',
},
Subscription: {
countdown: {
subscribe: async function* (_, { from }: { from: number }) {
for (let i = from; i >= 0; i--) {
await new Promise((resolve) => setTimeout(resolve, 1000));
yield { countdown: i };
}
},
},
},
};
const schema = makeExecutableSchema({ typeDefs, resolvers });
const handler = createHandler({ schema });
const app = express();
app.use('/graphql/sse', useServer({ handler })); // Mount the SSE handler
app.get('/health', (req, res) => res.send('OK'));
const PORT = process.env.PORT || 4000;
app.listen(PORT, () => {
console.log(`Server listening on http://localhost:${PORT}/graphql/sse`);
});
// --- Client Setup ---
const client = createClient({
url: `http://localhost:${PORT}/graphql/sse`
});
async function runSubscription() {
const query = `
subscription Countdown($from: Int!) {
countdown(from: $from)
}
`;
const input = { from: 5 };
console.log('\nClient: Subscribing to countdown...');
try {
for await (const result of client.iterate(query, input)) {
console.log('Client: Received:', result.data?.countdown);
if (result.data?.countdown === 0) {
console.log('Client: Countdown finished.');
break;
}
}
} catch (error) {
console.error('Client: Subscription error', error);
}
}
// Start the client subscription after a short delay to allow server to boot
setTimeout(runSubscription, 2000);