apollo-server
Version:
Production ready GraphQL Server
136 lines (118 loc) • 4.22 kB
text/typescript
// This is the "batteries-included" version of `apollo-server-express`. It
// handles creating the Express app and HTTP server for you (using whatever
// version of `express` its dependency pulls in). If you need to customize the
// Express app or HTTP server at all, you just use `apollo-server-express`
// instead.
import express from 'express';
import http from 'http';
import {
ApolloServer as ApolloServerExpress,
CorsOptions,
ApolloServerExpressConfig,
} from 'apollo-server-express';
import type { AddressInfo } from 'net';
import { format as urlFormat } from 'url';
import { ApolloServerPluginDrainHttpServer } from 'apollo-server-core';
export * from './exports';
export interface ServerInfo {
address: string;
family: string;
url: string;
port: number | string;
server: http.Server;
}
export class ApolloServer extends ApolloServerExpress {
private cors: CorsOptions | boolean | undefined;
private onHealthCheck: ((req: express.Request) => Promise<any>) | undefined;
private healthCheckPath: string | null | undefined;
private httpServer: http.Server;
constructor(
config: ApolloServerExpressConfig & {
cors?: CorsOptions | boolean;
onHealthCheck?: (req: express.Request) => Promise<any>;
healthCheckPath?: string | null;
stopGracePeriodMillis?: number;
},
) {
const httpServer = http.createServer();
super({
...config,
plugins: [
...(config.plugins ?? []),
ApolloServerPluginDrainHttpServer({
httpServer: httpServer,
stopGracePeriodMillis: config.stopGracePeriodMillis,
}),
],
});
this.httpServer = httpServer;
this.cors = config.cors;
this.onHealthCheck = config.onHealthCheck;
this.healthCheckPath = config?.healthCheckPath;
}
private createServerInfo(): ServerInfo {
const addressInfo = this.httpServer.address() as AddressInfo;
// Convert IPs which mean "any address" (IPv4 or IPv6) into localhost
// corresponding loopback ip. If this heuristic is wrong for your use case,
// explicitly specify a frontend host (in the `host` option to
// ApolloServer.listen).
let hostForUrl = addressInfo.address;
if (hostForUrl === '' || hostForUrl === '::') {
hostForUrl = 'localhost';
}
const url = urlFormat({
protocol: 'http',
hostname: hostForUrl,
port: addressInfo.port,
pathname: this.graphqlPath,
});
return {
...addressInfo,
server: this.httpServer,
url,
};
}
public override applyMiddleware() {
throw new Error(
'To use Apollo Server with an existing express application, please use apollo-server-express',
);
}
public override async start(): Promise<void> {
throw new Error(
"When using the `apollo-server` package, you don't need to call start(); just call listen().",
);
}
// Listen takes the same arguments as http.Server.listen.
public async listen(...opts: Array<any>): Promise<ServerInfo> {
// First start the server and throw if startup fails (eg, schema can't be loaded
// or a serverWillStart plugin throws).
await this._start();
// This class is the easy mode for people who don't create their own express
// object, so we have to create it.
const app = express();
this.httpServer.on('request', app);
app.disable('x-powered-by');
// provide generous values for the getting started experience
super.applyMiddleware({
app: app,
path: '/',
bodyParserConfig: { limit: '50mb' },
onHealthCheck: this.onHealthCheck,
cors:
typeof this.cors !== 'undefined'
? this.cors
: {
origin: '*',
},
__internal_healthCheckPath: this.healthCheckPath,
});
await new Promise((resolve) => {
this.httpServer.once('listening', resolve);
// If the user passed a callback to listen, it'll get called in addition
// to our resolver. They won't have the ability to get the ServerInfo
// object unless they use our Promise, though.
this.httpServer.listen(...(opts.length ? opts : [{ port: 4000 }]));
});
return this.createServerInfo();
}
}