UNPKG

@graphql-yoga/plugin-graphql-sse

Version:

GraphQL over Server-Sent Events Protocol plugin for GraphQL Yoga.

105 lines (104 loc) 5.13 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.useGraphQLSSE = void 0; const graphql_yoga_1 = require("graphql-yoga"); /** * Get [GraphQL over Server-Sent Events Protocol](https://github.com/enisdenjo/graphql-sse/blob/master/PROTOCOL.md) integration with GraphQL Yoga by simply installing this plugin! * * Note that the endpoint defaults to `/graphql/stream`, this is where your [graphql-sse](https://github.com/enisdenjo/graphql-sse) client should connect. */ function useGraphQLSSE(options = { pubsub: (0, graphql_yoga_1.createPubSub)(), }) { const { pubsub } = options; const tokenByRequest = new WeakMap(); const operationIdByRequest = new WeakMap(); return { onRequest({ request, url, fetchAPI, endResponse }) { const method = request.method.toLowerCase(); const token = request.headers.get('X-GraphQL-Event-Stream-Token') || url.searchParams.get('token'); const acceptHeader = request.headers.get('Accept'); if (token != null) { tokenByRequest.set(request, token); if (acceptHeader?.includes('text/event-stream') && method === 'get') { const encoder = new fetchAPI.TextEncoder(); endResponse(new fetchAPI.Response((0, graphql_yoga_1.map)((str) => encoder.encode(str))(pubsub.subscribe('graphql-sse-subscribe', token)), { status: 200, headers: { 'Content-Type': 'text/event-stream', }, })); } } if (method === 'delete') { const operationId = url.searchParams.get('operationId'); if (operationId) { pubsub.publish('graphql-sse-unsubscribe', operationId, true); } endResponse(new fetchAPI.Response(null, { status: 204, })); } if (method === 'put') { const token = fetchAPI.crypto.randomUUID(); endResponse(new fetchAPI.Response(token, { status: 201, statusText: 'Created', })); } }, onParams({ request, params }) { if (tokenByRequest.has(request) && params?.extensions?.operationId) { operationIdByRequest.set(request, params.extensions.operationId); } }, onResultProcess({ request, result, fetchAPI, serverContext, endResponse }) { const token = tokenByRequest.get(request); if (token) { const operationId = operationIdByRequest.get(request); // Batching is not supported by GraphQL SSE yet if (operationId && !Array.isArray(result)) { serverContext.waitUntil(Promise.resolve().then(async () => { if ((0, graphql_yoga_1.isAsyncIterable)(result)) { const asyncIterator = result[Symbol.asyncIterator](); pubsub .subscribe('graphql-sse-unsubscribe', operationId) .next() .finally(() => { asyncIterator.return?.(); }); let iteratorValue; while (!(iteratorValue = await asyncIterator.next()).done) { const chunk = iteratorValue.value; const messageJson = { id: operationId, payload: chunk, }; const messageStr = `event: next\nid: ${operationId}\ndata: ${JSON.stringify(messageJson)}\n\n`; pubsub.publish('graphql-sse-subscribe', token, messageStr); } } else { const messageJson = { id: operationId, payload: result, }; const messageStr = `event: next\nid: ${operationId}\ndata: ${JSON.stringify(messageJson)}\n\n`; pubsub.publish('graphql-sse-subscribe', token, messageStr); } const completeMessageJson = { id: operationId, }; const completeMessageStr = `event: complete\ndata: ${JSON.stringify(completeMessageJson)}\n\n`; pubsub.publish('graphql-sse-subscribe', token, completeMessageStr); })); endResponse(new fetchAPI.Response(null, { status: 202, })); } } }, }; } exports.useGraphQLSSE = useGraphQLSSE;