traverse-apollo-server-micro
Version:
Production-ready Node.js GraphQL server for Micro
148 lines (129 loc) • 3.88 kB
text/typescript
import {
ApolloServerBase,
GraphQLOptions,
processFileUploads,
} from 'traverse-apollo-server-core';
import { ServerResponse } from 'http';
import { send } from 'micro';
import { parseAll } from 'accept';
import { graphqlMicro } from './microApollo';
import { MicroRequest } from './types';
export interface ServerRegistration {
path?: string;
disableHealthCheck?: boolean;
onHealthCheck?: (req: MicroRequest) => Promise<any>;
}
export class ApolloServer extends ApolloServerBase {
// Extract Apollo Server options from the request.
async createGraphQLServerOptions(
req: MicroRequest,
res: ServerResponse,
): Promise<GraphQLOptions> {
return super.graphQLServerOptions({ req, res });
}
// Prepares and returns an async function that can be used by Micro to handle
// GraphQL requests.
public createHandler({
path,
disableHealthCheck,
onHealthCheck,
}: ServerRegistration = {}) {
// We'll kick off the `willStart` right away, so hopefully it'll finish
// before the first request comes in.
const promiseWillStart = this.willStart();
return async (req, res) => {
this.graphqlPath = path || '/graphql';
await promiseWillStart;
if (typeof processFileUploads === 'function') {
await this.handleFileUploads(req, res);
}
(await this.handleHealthCheck({
req,
res,
disableHealthCheck,
onHealthCheck,
})) ||
(await this.handleGraphqlRequestsWithServer({ req, res })) ||
send(res, 404, null);
};
}
// This integration supports file uploads.
protected supportsUploads(): boolean {
return true;
}
// This integration supports subscriptions.
protected supportsSubscriptions(): boolean {
return true;
}
// If health checking is enabled, trigger the `onHealthCheck`
// function when the health check URL is requested.
private async handleHealthCheck({
req,
res,
disableHealthCheck,
onHealthCheck,
}: {
req: MicroRequest;
res: ServerResponse;
disableHealthCheck?: boolean;
onHealthCheck?: (req: MicroRequest) => Promise<any>;
}): Promise<boolean> {
let handled = false;
if (
!disableHealthCheck &&
req.url === '/.well-known/apollo/server-health'
) {
// Response follows
// https://tools.ietf.org/html/draft-inadarei-api-health-check-01
res.setHeader('Content-Type', 'application/health+json');
if (onHealthCheck) {
try {
await onHealthCheck(req);
} catch (error) {
send(res, 503, { status: 'fail' });
handled = true;
}
}
if (!handled) {
send(res, 200, { status: 'pass' });
handled = true;
}
}
return handled;
}
// Handle incoming GraphQL requests using Apollo Server.
private async handleGraphqlRequestsWithServer({
req,
res,
}: {
req: MicroRequest;
res: ServerResponse;
}): Promise<boolean> {
let handled = false;
const url = req.url.split('?')[0];
if (url === this.graphqlPath) {
const graphqlHandler = graphqlMicro(() => {
return this.createGraphQLServerOptions(req, res);
});
const responseData = await graphqlHandler(req, res);
send(res, 200, responseData);
handled = true;
}
return handled;
}
// If file uploads are detected, prepare them for easier handling with
// the help of `graphql-upload`.
private async handleFileUploads(req: MicroRequest, res: ServerResponse) {
if (typeof processFileUploads !== 'function') {
return;
}
const contentType = req.headers['content-type'];
if (
this.uploadsConfig &&
contentType &&
contentType.startsWith('multipart/form-data')
) {
req.filePayload = await processFileUploads(req, res, this.uploadsConfig);
}
}
}