UNPKG

postgraphile

Version:

A GraphQL schema created by reflection over a PostgreSQL schema 🐘 (previously known as PostGraphQL)

258 lines 18 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.PostGraphileResponseFastify3 = exports.PostGraphileResponseKoa = exports.PostGraphileResponseNode = exports.PostGraphileResponse = void 0; const stream_1 = require("stream"); /** * The base class for PostGraphile responses; collects headers, status code and * body, and then hands to the relevant adaptor at the correct time. */ class PostGraphileResponse { constructor() { this._headers = {}; this._setHeaders = false; this.statusCode = 200; } _setHeadersOnce() { if (!this._setHeaders) { this._setHeaders = true; this.setHeaders(this.statusCode, this._headers); } } setHeader(header, value) { if (this._setHeaders) { throw new Error(`Cannot set a header '${header}' when headers already sent`); } this._headers[header] = value; } /** * Use `endWithStream` or `end`; not both. */ endWithStream() { if (this._body != null) { throw new Error("Cannot return a stream when there's already a response body"); } this._setHeadersOnce(); this._body = new stream_1.PassThrough(); this.setBody(this._body); return this._body; } /** * Use `endWithStream` or `end`; not both */ end(moreBody) { if (moreBody) { if (this._body != null) { if (typeof this._body === 'string') { if (Buffer.isBuffer(moreBody)) { throw new Error('Cannot mix string and buffer'); } this._body = this._body + moreBody; } else if (Buffer.isBuffer(this._body)) { if (typeof moreBody === 'string') { throw new Error('Cannot mix buffer and string'); } this._body = Buffer.concat([this._body, moreBody]); } else { throw new Error("Can't `.end(string)` when body is a stream"); } } else { this._body = moreBody; } } // If possible, set Content-Length to avoid unnecessary chunked encoding if (typeof this._body === 'string') { // String length is not reliable due to multi-byte characters; calculate via Buffer this.setHeader('Content-Length', String(Buffer.byteLength(this._body, 'utf8'))); } else if (Buffer.isBuffer(this._body)) { this.setHeader('Content-Length', String(this._body.byteLength)); } this._setHeadersOnce(); this.setBody(this._body); } } exports.PostGraphileResponse = PostGraphileResponse; /** * Suitable for Node's HTTP server, but also connect, express, restify and fastify v2. */ class PostGraphileResponseNode extends PostGraphileResponse { constructor(req, res, next) { super(); this._req = req; this._res = res; this._next = next; } getNodeServerRequest() { return this._req; } getNodeServerResponse() { return this._res; } getNextCallback() { return this._next; } setHeaders(statusCode, headers) { for (const key in headers) { if (Object.hasOwnProperty.call(headers, key)) { this._res.setHeader(key, headers[key]); } } this._res.statusCode = statusCode; } setBody(body) { if (typeof body === 'string') { this._res.end(body); } else if (Buffer.isBuffer(body)) { this._res.end(body); } else if (!body) { this._res.end(); } else { // Must be a stream // It'd be really nice if we could just: // // body.pipe(this._res); // // however we need to support running within the compression middleware // which requires special handling for server-sent events: // https://github.com/expressjs/compression#server-sent-events // // Because of this, we must handle the data streaming manually so we can // flush: const writeData = (data) => { this._res.write(data); // Technically we should see if `.write()` returned false, and if so we // should pause the stream. However, since our stream is coming from // watch mode, we find it unlikely that a significant amount of data // will be buffered (and we don't recommend watch mode in production), // so it doesn't feel like we need this currently. If it turns out you // need this, a PR would be welcome. if (typeof this._res.flush === 'function') { // https://github.com/expressjs/compression#server-sent-events this._res.flush(); } else if (typeof this._res.flushHeaders === 'function') { this._res.flushHeaders(); } }; let clean = false; const cleanup = () => { if (clean) return; clean = true; body.removeListener('data', writeData); body.removeListener('end', cleanup); this._req.removeListener('close', cleanup); this._req.removeListener('end', cleanup); this._req.removeListener('error', cleanup); }; body.on('data', writeData); body.on('end', () => { cleanup(); this._res.end(); }); this._req.on('close', cleanup); this._req.on('end', cleanup); this._req.on('error', cleanup); } } } exports.PostGraphileResponseNode = PostGraphileResponseNode; /** * Suitable for Koa. */ class PostGraphileResponseKoa extends PostGraphileResponse { constructor(ctx, next) { super(); this._ctx = ctx; this._next = next; const req = this.getNodeServerRequest(); // For backwards compatibility, and to allow getting "back" to the Koa // context from pgSettings, etc (this is a documented interface) req._koaCtx = ctx; // Make `koa-bodyparser` trigger skipping of our `body-parser` if (ctx.request.body) { req._body = true; req.body = ctx.request.body; } // In case you're using koa-mount or similar req.originalUrl = ctx.request.originalUrl; } getNodeServerRequest() { return this._ctx.req; } getNodeServerResponse() { return this._ctx.res; } getNextCallback() { return this._next; } setHeaders(statusCode, headers) { this._ctx.status = statusCode; this._ctx.set(headers); // DO NOT `this._ctx.flushHeaders()` as it will interfere with the compress // middleware. } endWithStream() { // We're going to assume this is the EventStream which we want to // be realtime for watch mode, and there's no value in compressing it. this._ctx.compress = false; // TODO: find a better way of flushing the event stream on write. return super.endWithStream(); } setBody(body) { this._ctx.body = body || ''; this._next(); } } exports.PostGraphileResponseKoa = PostGraphileResponseKoa; /** * Suitable for Fastify v3 (use PostGraphileResponseNode and middleware * approach for Fastify v2) */ class PostGraphileResponseFastify3 extends PostGraphileResponse { constructor(request, reply) { super(); this._request = request; this._reply = reply; // For backwards compatibility, and to allow getting "back" to the Fastify // request from pgSettings, etc const req = this.getNodeServerRequest(); req._fastifyRequest = this._request; // Make Fastify's body parsing trigger skipping of our `body-parser` if (this._request.body) { req._body = true; req.body = this._request.body; } } getNodeServerRequest() { return this._request.raw; } getNodeServerResponse() { return this._reply.raw; } setHeaders(statusCode, headers) { this._reply.status(statusCode); this._reply.headers(headers); } endWithStream() { // We're going to assume this is the EventStream which we want to // be realtime for watch mode, and there's no value in compressing it. // Fastify will disable compression if we set the relevant request header // (see: // https://github.com/fastify/fastify-compress/blob/068c673fc0bd50da1f4d9f3fd2423b482c364a89/index.js#L217-L218) this._request.headers['x-no-compression'] = '1'; // TODO: find a better way of flushing the event stream on write. return super.endWithStream(); } setBody(body) { this._reply.send(body); } } exports.PostGraphileResponseFastify3 = PostGraphileResponseFastify3; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZnJhbWV3b3Jrcy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9wb3N0Z3JhcGhpbGUvaHR0cC9mcmFtZXdvcmtzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLG1DQUE2QztBQThEN0M7OztHQUdHO0FBQ0gsTUFBc0Isb0JBQW9CO0lBQTFDO1FBQ1UsYUFBUSxHQUFZLEVBQUUsQ0FBQztRQUV2QixnQkFBVyxHQUFHLEtBQUssQ0FBQztRQUNyQixlQUFVLEdBQUcsR0FBRyxDQUFDO0lBd0UxQixDQUFDO0lBdEVTLGVBQWU7UUFDckIsSUFBSSxDQUFDLElBQUksQ0FBQyxXQUFXLEVBQUU7WUFDckIsSUFBSSxDQUFDLFdBQVcsR0FBRyxJQUFJLENBQUM7WUFDeEIsSUFBSSxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUMsVUFBVSxFQUFFLElBQUksQ0FBQyxRQUFRLENBQUMsQ0FBQztTQUNqRDtJQUNILENBQUM7SUFFTSxTQUFTLENBQUMsTUFBYyxFQUFFLEtBQWE7UUFDNUMsSUFBSSxJQUFJLENBQUMsV0FBVyxFQUFFO1lBQ3BCLE1BQU0sSUFBSSxLQUFLLENBQUMsd0JBQXdCLE1BQU0sNkJBQTZCLENBQUMsQ0FBQztTQUM5RTtRQUNELElBQUksQ0FBQyxRQUFRLENBQUMsTUFBTSxDQUFDLEdBQUcsS0FBSyxDQUFDO0lBQ2hDLENBQUM7SUFFRDs7T0FFRztJQUNJLGFBQWE7UUFDbEIsSUFBSSxJQUFJLENBQUMsS0FBSyxJQUFJLElBQUksRUFBRTtZQUN0QixNQUFNLElBQUksS0FBSyxDQUFDLDZEQUE2RCxDQUFDLENBQUM7U0FDaEY7UUFDRCxJQUFJLENBQUMsZUFBZSxFQUFFLENBQUM7UUFDdkIsSUFBSSxDQUFDLEtBQUssR0FBRyxJQUFJLG9CQUFXLEVBQUUsQ0FBQztRQUMvQixJQUFJLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQztRQUN6QixPQUFPLElBQUksQ0FBQyxLQUFLLENBQUM7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