Skip to content

Commit

Permalink
Merge pull request #1059 from apollographql/server-2.0/default-impl-p…
Browse files Browse the repository at this point in the history
…arams

AS2: Move default options into listen function and add bodyParserConfig
  • Loading branch information
evans authored May 11, 2018
2 parents 731f3d3 + dbf94b1 commit d43df53
Show file tree
Hide file tree
Showing 7 changed files with 59 additions and 60 deletions.
1 change: 1 addition & 0 deletions packages/apollo-server-core/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

### vNEXT

* `apollo-server-core`: move subscriptions options into listen [PR#1059](https://github.com/apollographql/apollo-server/pull/1059)
* `apollo-server-core`: Replace console.error with logFunction for opt-in logging [PR #1024](https://github.com/apollographql/apollo-server/pull/1024)
* `apollo-server-core`: context creation can be async and errors are formatted to include error code [PR #1024](https://github.com/apollographql/apollo-server/pull/1024)
* `apollo-server-core`: add `mocks` parameter to the base constructor(applies to all variants) [PR#1017](https://github.com/apollographql/apollo-server/pull/1017)
Expand Down
28 changes: 12 additions & 16 deletions packages/apollo-server-core/src/ApolloServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ const NoIntrospection = (context: ValidationContext) => ({
});

export class ApolloServerBase<Request = RequestInit> {
public subscriptions: Config<Request>['subscriptions'];
public disableTools: boolean = !isDev;
public subscriptionsEnabled: boolean = false;

private schema: GraphQLSchema;
private context?: Context | ContextFunction;
Expand All @@ -67,7 +67,6 @@ export class ApolloServerBase<Request = RequestInit> {
resolvers,
schema,
schemaDirectives,
subscriptions,
typeDefs,
enableIntrospection,
mocks,
Expand Down Expand Up @@ -108,8 +107,6 @@ export class ApolloServerBase<Request = RequestInit> {
mocks: typeof mocks === 'boolean' ? {} : mocks,
});
}

this.subscriptions = subscriptions;
}

public use({ getHttp, path }: RegistrationOptions) {
Expand All @@ -121,18 +118,20 @@ export class ApolloServerBase<Request = RequestInit> {

public listen(opts: ListenOptions = {}): Promise<ServerInfo> {
this.http = this.getHttp();

const options = {
port: process.env.PORT || 4000,
...opts,
};

if (this.subscriptions !== false) {
if (opts.subscriptions !== false) {
const config: any =
this.subscriptions === true || typeof this.subscriptions === 'undefined'
opts.subscriptions === true || typeof opts.subscriptions === 'undefined'
? {
path: this.graphqlPath,
}
: this.subscriptions;
: opts.subscriptions;
this.subscriptionsEnabled = true;
this.createSubscriptionServer(this.http, config);
}

Expand All @@ -141,15 +140,12 @@ export class ApolloServerBase<Request = RequestInit> {
return new Promise((success, fail) => {
if (this.engineProxy) {
this.engineProxy.listen(
Object.assign(
{},
{
graphqlPaths: [this.graphqlPath],
port: options.port,
httpServer: this.http,
launcherOptions: options.engineLauncherOptions,
},
),
{
graphqlPaths: [this.graphqlPath],
port: options.port,
httpServer: this.http,
launcherOptions: options.engineLauncherOptions,
},
() => {
this.engineProxy.engineListeningAddress.url = require('url').resolve(
this.engineProxy.engineListeningAddress.url,
Expand Down
3 changes: 1 addition & 2 deletions packages/apollo-server-core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ export interface Config<Server>
schema?: GraphQLSchema;
schemaDirectives?: Record<string, typeof SchemaDirectiveVisitor>;
context?: Context<any> | ContextFunction<any>;
subscriptions?: SubscriptionServerOptions | string | false;
enableIntrospection?: boolean;
mocks?: boolean | IMocks;
}
Expand Down Expand Up @@ -66,6 +65,7 @@ export interface ListenOptions {
engineLauncherOptions?: EngineLauncherOptions;
// WebSocket options
keepAlive?: number;
subscriptions?: SubscriptionServerOptions | string | false;
onConnect?: (
connectionParams: Object,
websocket: WebSocket,
Expand All @@ -82,7 +82,6 @@ export interface MiddlewareOptions {

export interface RegistrationOptions {
path: string;
// subscriptions?: boolean;
getHttp: () => HttpServer;
}

Expand Down
46 changes: 26 additions & 20 deletions packages/apollo-server-express/src/ApolloServer.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as express from 'express';
import * as corsMiddleware from 'cors';
import { json } from 'body-parser';
import { json, OptionsJson } from 'body-parser';
import { createServer, Server as HttpServer } from 'http';
import gui from 'graphql-playground-middleware-express';
import { ApolloServerBase } from 'apollo-server-core';
Expand All @@ -12,15 +12,16 @@ export interface ServerRegistration {
app: express.Application;
server: ApolloServerBase<express.Request>;
path?: string;
subscriptions?: boolean;
cors?: corsMiddleware.CorsOptions;
bodyParserConfig?: OptionsJson;
}

export const registerServer = async ({
app,
server,
path,
cors,
bodyParserConfig,
}: ServerRegistration) => {
if (!path) path = '/graphql';

Expand All @@ -30,24 +31,29 @@ export const registerServer = async ({
getHttp: () => createServer(app),
});

app.use(path, corsMiddleware(cors), json(), (req, res, next) => {
// make sure we check to see if graphql gui should be on
if (!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';
app.use(
path,
corsMiddleware(cors),
json(bodyParserConfig),
(req, res, next) => {
// make sure we check to see if graphql gui should be on
if (!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';

if (prefersHTML) {
return gui({
endpoint: path,
subscriptionsEndpoint: server.subscriptions && path,
})(req, res, next);
if (prefersHTML) {
return gui({
endpoint: path,
subscriptionsEndpoint: server.subscriptionsEnabled && path,
})(req, res, next);
}
}
}
return graphqlExpress(server.request.bind(server))(req, res, next);
});
return graphqlExpress(server.request.bind(server))(req, res, next);
},
);
};
2 changes: 2 additions & 0 deletions packages/apollo-server/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,7 @@

### vNEXT

* `apollo-server`: move non-schema related options into listen [PR#1059](https://github.com/apollographql/apollo-server/pull/1059)
* `apollo-server`: add `bodyParserConfig` options [PR#1059](https://github.com/apollographql/apollo-server/pull/1059)
* `apollo-server`: add `/.well-known/apollo/server-health` endpoint with async callback for additional checks, ie database poke [PR#992](https://github.com/apollographql/apollo-server/pull/992)
* `apollo-server`: collocate graphql gui with endpoint and provide gui when accessed from browser [PR#987](https://github.com/apollographql/apollo-server/pull/987)
1 change: 1 addition & 0 deletions packages/apollo-server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
},
"homepage": "https://github.com/apollographql/apollo-server#readme",
"devDependencies": {
"@types/body-parser": "^1.17.0",
"@types/express": "^4.11.1",
"@types/graphql": "^0.13.0",
"typescript": "2.8.1"
Expand Down
38 changes: 16 additions & 22 deletions packages/apollo-server/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as express from 'express';
import { registerServer } from 'apollo-server-express';
import { OptionsJson } from 'body-parser';

import {
ApolloServerBase,
Expand All @@ -11,38 +12,31 @@ import {
export * from './exports';

export class ApolloServer extends ApolloServerBase<express.Request> {
private disableHealthCheck: boolean = false;
private onHealthCheck: (req: express.Request) => Promise<any>;

constructor({
disableHealthCheck,
onHealthCheck,
...opts
}: Config<express.Request> & {
onHealthCheck?: (req: express.Request) => Promise<any>;
disableHealthCheck?: boolean;
}) {
super(opts);
if (disableHealthCheck) this.disableHealthCheck = true;
this.onHealthCheck = onHealthCheck;
}

// here we overwrite the underlying listen to configure
// the fallback / default server implementation
async listen(opts: ListenOptions = {}): Promise<ServerInfo> {
async listen(
opts: ListenOptions & {
onHealthCheck?: (req: express.Request) => Promise<any>;
disableHealthCheck?: boolean;
bodyParserConfig?: OptionsJson;
} = {},
): Promise<ServerInfo> {
//defensive copy
const { onHealthCheck } = opts;

// we haven't configured a server yet so lets build the default one
// using express
if (!this.getHttp) {
const app = express();

if (!this.disableHealthCheck) {
if (!opts.disableHealthCheck) {
//uses same path as engine
app.use('/.well-known/apollo/server-health', (req, res, next) => {
//Response follows https://tools.ietf.org/html/draft-inadarei-api-health-check-01
res.type('application/health+json');

if (this.onHealthCheck) {
this.onHealthCheck(req)
if (onHealthCheck) {
onHealthCheck(req)
.then(() => {
res.json({ status: 'pass' });
})
Expand All @@ -53,9 +47,9 @@ export class ApolloServer extends ApolloServerBase<express.Request> {
res.json({ status: 'pass' });
}
});

await registerServer({ app, path: '/', server: this });
}

await registerServer({ app, path: '/', server: this });
}

return super.listen(opts);
Expand Down

0 comments on commit d43df53

Please sign in to comment.