UNPKG

grafserv

Version:

A highly optimized server for GraphQL, powered by Grafast

225 lines 9.24 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.FastifyGrafserv = void 0; exports.grafserv = grafserv; const node_stream_1 = require("node:stream"); const websocket_1 = require("graphql-ws/use/@fastify/websocket"); const base_js_1 = require("../../../core/base.js"); const utils_js_1 = require("../../../utils.js"); const websocketKeepalive_js_1 = require("../../../websocketKeepalive.js"); function getDigest(request, reply) { return { httpVersionMajor: request.raw.httpVersionMajor, httpVersionMinor: request.raw.httpVersionMinor, // Fastify respects X-Forwarded-Proto when configured to trust the proxy, see: // https://github.com/fastify/fastify/blob/59c5b273dad30821d03c952bbe073a976f92a325/docs/Reference/Server.md#trustproxy isSecure: request.protocol === "https", method: request.method, path: request.url, headers: (0, utils_js_1.processHeaders)(request.headers), getQueryParams() { return request.query; }, getBody() { return (0, utils_js_1.getBodyFromFrameworkBody)(request.body); }, requestContext: { node: { req: request.raw, res: reply.raw, }, fastifyv4: { request, reply, }, }, preferJSON: true, }; } class FastifyGrafserv extends base_js_1.GrafservBase { constructor(config) { super(config); } async send(request, reply, result) { if (result === null) { // 404 reply.statusCode = 404; return "¯\\_(ツ)_/¯"; } switch (result.type) { case "error": { const { statusCode, headers } = result; reply.headers(headers); reply.statusCode = statusCode; // DEBT: mutating the error is probably bad form... const errorWithStatus = Object.assign(result.error, { status: statusCode, }); throw errorWithStatus; } case "buffer": { const { statusCode, headers, buffer } = result; reply.headers(headers); reply.statusCode = statusCode; return buffer; } case "json": { const { statusCode, headers, json } = result; reply.headers(headers); reply.statusCode = statusCode; return json; } case "noContent": { const { statusCode, headers } = result; reply.headers(headers); reply.statusCode = statusCode; return null; } case "bufferStream": { const { statusCode, headers, lowLatency, bufferIterator } = result; let bufferIteratorHandled = false; try { if (lowLatency) { request.raw.socket.setTimeout(0); request.raw.socket.setNoDelay(true); request.raw.socket.setKeepAlive(true); } reply.headers(headers); reply.statusCode = statusCode; const stream = new node_stream_1.PassThrough(); reply.send(stream); // Fork off and convert bufferIterator to try { bufferIteratorHandled = true; for await (const buffer of bufferIterator) { stream.write(buffer); } } finally { stream.end(); } } catch (e) { if (!bufferIteratorHandled) { try { if (bufferIterator.return) { bufferIterator.return(); } else if (bufferIterator.throw) { bufferIterator.throw(e); } } catch (e2) { /* nom nom nom */ } } throw e; } return reply; } default: { const never = result; console.log("Unhandled:"); console.dir(never); reply.type("text/plain"); reply.statusCode = 501; return "Server hasn't implemented this yet"; } } } async addTo(app) { // application/graphql-request+json isn't currently an official serialization format: // https://graphql.github.io/graphql-over-http/draft/#sec-Media-Types /* app.addContentTypeParser( "application/graphql-request+json", { parseAs: "string" }, app.getDefaultJsonParser("ignore", "ignore"), ); */ const { graphiql, graphiqlOnGraphQLGET, graphqlPath, graphiqlPath, graphiqlStaticPath, graphqlOverGET, maxRequestLength: bodyLimit, watch, } = this.dynamicOptions; const websockets = this.resolvedPreset.grafserv?.websockets ?? false; const exposeGetRoute = graphqlOverGET || graphiqlOnGraphQLGET || websockets; const exposeHeadRoute = true; // Build HTTP handler. const handler = async (request, reply) => { const digest = getDigest(request, reply); const handlerResult = await this.graphqlHandler((0, utils_js_1.normalizeRequest)(digest), this.graphiqlHandler); const result = await (0, base_js_1.convertHandlerResultToResult)(handlerResult); return this.send(request, reply, result); }; // Build websocket handler. const keepaliveInterval = this.getPreset().grafserv?.websocketKeepalive ?? websocketKeepalive_js_1.DEFAULT_WEBSOCKET_KEEPALIVE; const wsHandler = websockets ? (0, websocket_1.makeHandler)((0, utils_js_1.makeGraphQLWSConfig)(this), keepaliveInterval) : undefined; // Attach HTTP handler for POST requests. app.route({ method: "POST", url: graphqlPath, handler, bodyLimit }); // Attach websocket and HTTP handler for GET requests, if desired. if (exposeGetRoute) { app.route({ method: "GET", url: graphqlPath, exposeHeadRoute, handler, wsHandler, }); } if (graphiql) { app.route({ method: "GET", url: graphiqlPath, exposeHeadRoute, bodyLimit, handler: async (request, reply) => { const digest = getDigest(request, reply); const handlerResult = await this.graphiqlHandler((0, utils_js_1.normalizeRequest)(digest)); const result = await (0, base_js_1.convertHandlerResultToResult)(handlerResult); return this.send(request, reply, result); }, }); } if (watch) { app.route({ method: "GET", url: this.dynamicOptions.eventStreamPath, exposeHeadRoute: true, bodyLimit: this.dynamicOptions.maxRequestLength, handler: async (request, reply) => { const digest = getDigest(request, reply); // TODO: refactor this to use the eventStreamHandler once we write that... const handlerResult = { type: "event-stream", request: (0, utils_js_1.normalizeRequest)(digest), dynamicOptions: this.dynamicOptions, payload: this.makeStream(), statusCode: 200, }; const result = await (0, base_js_1.convertHandlerResultToResult)(handlerResult); return this.send(request, reply, result); }, }); } if (graphiql) { // Must come last, because wildcard app.route({ method: "GET", url: graphiqlStaticPath + "*", exposeHeadRoute, bodyLimit, handler: async (request, reply) => { const digest = getDigest(request, reply); const handlerResult = await this.graphiqlStaticHandler((0, utils_js_1.normalizeRequest)(digest)); const result = await (0, base_js_1.convertHandlerResultToResult)(handlerResult); return this.send(request, reply, result); }, }); } } } exports.FastifyGrafserv = FastifyGrafserv; function grafserv(config) { return new FastifyGrafserv(config); } //# sourceMappingURL=index.js.map