apollo-server/packages/apollo-server-express/src/ApolloServer.ts

165 lines
4.4 KiB
TypeScript
Raw Normal View History

import express from 'express';
import corsMiddleware from 'cors';
import { json, OptionsJson } from 'body-parser';
import { createServer } from 'http';
2018-05-01 06:09:48 -07:00
import gui from 'graphql-playground-middleware-express';
import { ApolloServerBase, formatApolloErrors } from 'apollo-server-core';
import accepts from 'accepts';
2018-05-01 06:09:48 -07:00
import { graphqlExpress } from './expressApollo';
import {
processRequest as processFileUploads,
GraphQLUpload,
} from 'apollo-upload-server';
export { GraphQLOptions, GraphQLExtension } from 'apollo-server-core';
import { GraphQLOptions } from 'apollo-server-core';
const gql = String.raw;
export class ApolloServer extends ApolloServerBase {
//This translates the arguments from the middleware into graphQL options It
//provides typings for the integration specific behavior, ideally this would
//be propagated with a generic to the super class
async createGraphQLServerOptions(
req: express.Request,
res: express.Response,
): Promise<GraphQLOptions> {
return super.graphQLServerOptions({ req, res });
}
}
2018-05-01 06:09:48 -07:00
export interface ServerRegistration {
app: express.Application;
server: ApolloServer;
2018-05-01 06:09:48 -07:00
path?: string;
cors?: corsMiddleware.CorsOptions;
bodyParserConfig?: OptionsJson;
onHealthCheck?: (req: express.Request) => Promise<any>;
disableHealthCheck?: boolean;
enableGUI?: boolean;
//https://github.com/jaydenseric/apollo-upload-server#options
uploads?: boolean | Record<string, any>;
2018-05-01 06:09:48 -07:00
}
const fileUploadMiddleware = (
uploadsConfig: Record<string, any>,
server: ApolloServerBase,
) => (
req: express.Request,
res: express.Response,
next: express.NextFunction,
) => {
if (req.is('multipart/form-data')) {
processFileUploads(req, uploadsConfig)
.then(body => {
req.body = body;
next();
})
.catch(error => {
if (error.status && error.expose) res.status(error.status);
next(
formatApolloErrors([error], {
formatter: server.requestOptions.formatError,
debug: server.requestOptions.debug,
logFunction: server.requestOptions.logFunction,
}),
);
});
} else {
next();
}
};
2018-05-01 06:09:48 -07:00
export const registerServer = async ({
app,
server,
path,
cors,
bodyParserConfig,
disableHealthCheck,
enableGUI,
onHealthCheck,
uploads,
2018-05-01 06:09:48 -07:00
}: ServerRegistration) => {
2018-05-01 11:05:26 -07:00
if (!path) path = '/graphql';
if (!disableHealthCheck) {
//uses same path as engine proxy, but is generally useful.
app.use('/.well-known/apollo/server-health', (req, res) => {
//Response follows https://tools.ietf.org/html/draft-inadarei-api-health-check-01
res.type('application/health+json');
if (onHealthCheck) {
onHealthCheck(req)
.then(() => {
res.json({ status: 'pass' });
})
.catch(() => {
res.status(503).json({ status: 'fail' });
});
} else {
res.json({ status: 'pass' });
}
});
}
let uploadsMiddleware;
if (uploads !== false) {
server.enhanceSchema({
typeDefs: gql`
scalar Upload
`,
resolvers: { Upload: GraphQLUpload },
});
uploadsMiddleware = fileUploadMiddleware(
typeof uploads !== 'boolean' ? uploads : {},
server,
);
}
2018-05-01 06:09:48 -07:00
// XXX multiple paths?
2018-05-01 11:05:26 -07:00
server.use({
path,
getHttp: () => createServer(app),
});
2018-05-01 06:09:48 -07:00
app.use(
path,
corsMiddleware(cors),
json(bodyParserConfig),
uploadsMiddleware ? uploadsMiddleware : (_req, _res, next) => next(),
(req, res, next) => {
// make sure we check to see if graphql gui should be on
// enableGUI takes precedence over the server tools setting
if (
(enableGUI || (enableGUI === undefined && !server.disableTools)) &&
req.method === 'GET'
) {
//perform more expensive content-type check only if necessary
const accept = accepts(req);
const types = accept.types() as string[];
const prefersHTML =
types.find(
(x: string) => x === 'text/html' || x === 'application/json',
) === 'text/html';
2018-05-01 06:09:48 -07:00
if (prefersHTML) {
return gui({
endpoint: path,
subscriptionEndpoint: server.subscriptionsPath,
})(req, res, next);
}
2018-05-01 06:09:48 -07:00
}
return graphqlExpress(server.createGraphQLServerOptions.bind(server))(
req,
res,
next,
);
},
);
2018-05-01 06:09:48 -07:00
};