diff --git a/packages/graphql-server-integration-testsuite/src/index.ts b/packages/graphql-server-integration-testsuite/src/index.ts index 985eb3fe79a..8a360cda938 100644 --- a/packages/graphql-server-integration-testsuite/src/index.ts +++ b/packages/graphql-server-integration-testsuite/src/index.ts @@ -817,5 +817,20 @@ export default (createApp: CreateAppFunc, destroyApp?: DestroyAppFunc) => { }); }); }); + + describe('server setup', () => { + it('throws error on 404 routes', () => { + app = createApp(); + + const query = { + query: '{ testString }', + }; + const req = request(app) + .get(`/bogus-route?${querystring.stringify(query)}`); + return req.then((res) => { + expect(res.status).to.equal(404); + }); + }); + }); }); }; diff --git a/packages/graphql-server-lambda/src/lambdaApollo.test.ts b/packages/graphql-server-lambda/src/lambdaApollo.test.ts index f775d26d8f6..a723848e777 100755 --- a/packages/graphql-server-lambda/src/lambdaApollo.test.ts +++ b/packages/graphql-server-lambda/src/lambdaApollo.test.ts @@ -6,19 +6,28 @@ import 'mocha'; import * as url from 'url'; function createLambda(options: CreateAppOptions = {}) { - let handler, + let route, + handler, callback, event, context; options.graphqlOptions = options.graphqlOptions || { schema: Schema }; if (options.graphiqlOptions ) { + route = '/graphiql'; handler = graphiqlLambda( options.graphiqlOptions ); } else { + route = '/graphql'; handler = graphqlLambda( options.graphqlOptions ); } return function(req, res) { + if (!req.url.startsWith(route)) { + res.statusCode = 404; + res.end(); + return; + } + let body = ''; req.on('data', function (chunk) { body += chunk; diff --git a/packages/graphql-server-micro/README.md b/packages/graphql-server-micro/README.md index 923d842eb3b..7cc6d50a87f 100644 --- a/packages/graphql-server-micro/README.md +++ b/packages/graphql-server-micro/README.md @@ -1,3 +1,26 @@ # graphql-server-micro This is the [Micro](https://github.com/zeit/micro) integration for the Apollo community GraphQL Server. [Read the docs.](http://dev.apollodata.com/tools/apollo-server/index.html) + + +## Example +```typescript +import { microGraphiql, microGraphql } from "graphql-server-micro"; +import micro, { send } from "micro"; +import { get, post, router } from "microrouter"; +import schema from "./schema"; + +const graphqlHandler = microGraphql({ schema }); +const graphiqlHandler = microGraphiql({ endpointURL: "/graphql" }); + +const server = micro( + router( + get("/graphql", graphqlHandler), + post("/graphql", graphqlHandler), + get("/graphiql", graphiqlHandler), + (req, res) => send(res, 404, "not found"), + ), +); + +server.listen(3000); +``` diff --git a/packages/graphql-server-micro/package.json b/packages/graphql-server-micro/package.json index 09cdac742d5..edf07d9fb3f 100644 --- a/packages/graphql-server-micro/package.json +++ b/packages/graphql-server-micro/package.json @@ -30,14 +30,16 @@ }, "devDependencies": { "graphql-server-integration-testsuite": "^0.8.4", - "micro": "^7.3.3" + "micro": "^7.3.3", + "microrouter": "^2.1.1" }, "peerDependencies": { "graphql": "^0.9.0 || ^0.10.1", "micro": "^7.3.3" }, "optionalDependencies": { - "@types/graphql": "^0.9.1" + "@types/graphql": "^0.9.1", + "@types/micro": "^7.3.0" }, "typings": "dist/index.d.ts", "typescript": { diff --git a/packages/graphql-server-micro/src/microApollo.test.ts b/packages/graphql-server-micro/src/microApollo.test.ts index d3f46321dcf..5eaaf6d9d1e 100644 --- a/packages/graphql-server-micro/src/microApollo.test.ts +++ b/packages/graphql-server-micro/src/microApollo.test.ts @@ -1,16 +1,39 @@ import { microGraphql, microGraphiql } from './microApollo'; import 'mocha'; -import * as micro from 'micro'; -import testSuite, { schema as Schema, CreateAppOptions } from 'graphql-server-integration-testsuite'; +import micro, { send } from 'micro'; +import { router, get, post, put, patch, del, head, options as opts } from 'microrouter'; +import testSuite, { schema, CreateAppOptions } from 'graphql-server-integration-testsuite'; + function createApp(options: CreateAppOptions) { - if (options && options.graphiqlOptions ) { - return micro(microGraphiql( options.graphiqlOptions )); - } else { - const graphqlOptions = (options && options.graphqlOptions) || { schema: Schema }; - return micro(microGraphql(graphqlOptions)); - } + const graphqlOptions = (options && options.graphqlOptions) || { schema }; + const graphiqlOptions = (options && options.graphiqlOptions) || { endpointURL: '/graphql' }; + + const graphqlHandler = microGraphql(graphqlOptions); + const graphiqlHandler = microGraphiql(graphiqlOptions); + + return micro( + router( + get('/graphql', graphqlHandler), + post('/graphql', graphqlHandler), + put('/graphql', graphqlHandler), + patch('/graphql', graphqlHandler), + del('/graphql', graphqlHandler), + head('/graphql', graphqlHandler), + opts('/graphql', graphqlHandler), + + get('/graphiql', graphiqlHandler), + post('/graphiql', graphiqlHandler), + put('/graphiql', graphiqlHandler), + patch('/graphiql', graphiqlHandler), + del('/graphiql', graphiqlHandler), + head('/graphiql', graphiqlHandler), + opts('/graphiql', graphiqlHandler), + + (req, res) => send(res, 404, 'not found'), + ), + ); } describe('integration:Micro', () => { diff --git a/packages/graphql-server-micro/src/microApollo.ts b/packages/graphql-server-micro/src/microApollo.ts index 5b984025df3..89721aaf62f 100644 --- a/packages/graphql-server-micro/src/microApollo.ts +++ b/packages/graphql-server-micro/src/microApollo.ts @@ -1,6 +1,6 @@ import { GraphQLOptions, HttpQueryError, runHttpQuery } from 'graphql-server-core'; import * as GraphiQL from 'graphql-server-module-graphiql'; -import { json } from 'micro'; +import { createError, json, RequestHandler } from 'micro'; import * as url from 'url'; import {IncomingMessage, ServerResponse} from 'http'; @@ -8,7 +8,7 @@ export interface MicroGraphQLOptionsFunction { (req?: IncomingMessage): GraphQLOptions | Promise; } -export function microGraphql(options: GraphQLOptions | MicroGraphQLOptionsFunction) { +export function microGraphql(options: GraphQLOptions | MicroGraphQLOptionsFunction): RequestHandler { if (!options) { throw new Error('Apollo Server requires options.'); } @@ -29,33 +29,34 @@ export function microGraphql(options: GraphQLOptions | MicroGraphQLOptionsFuncti query = url.parse(req.url, true).query; } - runHttpQuery([req, res], { - method: req.method, - options: options, - query: query, - }).then((gqlResponse) => { + try { + const gqlResponse = await runHttpQuery([req, res], { + method: req.method, + options: options, + query: query, + }); + res.setHeader('Content-Type', 'application/json'); - res.write(gqlResponse); - res.end(); - }, (error: HttpQueryError) => { - if ( 'HttpQueryError' !== error.name ) { - throw error; + return gqlResponse; + } catch (error) { + if ('HttpQueryError' === error.name) { + if (error.headers) { + Object.keys(error.headers).forEach((header) => { + res.setHeader(header, error.headers[header]); + }); + } } - if ( error.headers ) { - Object.keys(error.headers).forEach((header) => { - res.setHeader(header, error.headers[header]); - }); + if (!error.statusCode) { + error.statusCode = 500; } - res.statusCode = error.statusCode; - res.write(error.message); - res.end(); - }); + throw error; + } }; } -export function microGraphiql(options: GraphiQL.GraphiQLData) { +export function microGraphiql(options: GraphiQL.GraphiQLData): RequestHandler { return (req: IncomingMessage, res: ServerResponse) => { const q = req.url && url.parse(req.url, true).query || {}; const query = q.query || '';