Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(fastify): Integrate apollo-fastify plugin #626 #1013

Merged
merged 35 commits into from
Jul 13, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
d1f7d96
Integrate apollo-fastify plugin #626
Apr 30, 2018
73cd6f2
#626 Fix typescript issues
addityasingh Apr 30, 2018
c8ec07e
#626 Update changelog
addityasingh Apr 30, 2018
46b7951
Merge branch 'master' into as-fastify-apollo
addityasingh Apr 30, 2018
dc8b4aa
#626 Update README
addityasingh May 1, 2018
6ebe74f
Merge branch 'master' into as-fastify-apollo
addityasingh May 1, 2018
5169229
#626 Fix the breaking tests
addityasingh May 1, 2018
73eb7ac
Merge branch 'master' into as-fastify-apollo
addityasingh May 2, 2018
e0ae046
Merge branch 'master' into as-fastify-apollo
addityasingh May 3, 2018
63e30be
Merge branch 'master' into as-fastify-apollo
addityasingh May 3, 2018
d1fdf78
Merge branch 'master' into as-fastify-apollo
addityasingh May 6, 2018
cec04da
Merge branch 'master' into as-fastify-apollo
addityasingh May 10, 2018
459594e
Merge branch 'master' into as-fastify-apollo
abernix May 11, 2018
b48d08a
#626 Fix code review comments
addityasingh May 16, 2018
e91e015
#626 Run Hapi tests only for node 8 and 9
addityasingh May 16, 2018
57589bb
#626 Run Hapi tests only for node 8 and 9
addityasingh May 16, 2018
530bce1
#626 Commit package.lock in working state
addityasingh May 16, 2018
79a27d1
#626 Use npm instead of yarn for node 4
addityasingh May 16, 2018
032220b
Revert package-lock and circle ci test job steps
addityasingh May 16, 2018
b91b93b
Merge branch 'master' into as-fastify-apollo
addityasingh May 21, 2018
ad29216
Merge branch 'master' into as-fastify-apollo
addityasingh May 22, 2018
3a72e86
Merge branch 'master' into as-fastify-apollo
addityasingh May 23, 2018
6a075dc
Merge branch 'master' into as-fastify-apollo
addityasingh May 23, 2018
71b2a2f
#626 Bump the version
addityasingh May 23, 2018
34f210c
Merge branch 'master' into as-fastify-apollo
addityasingh May 24, 2018
5d96da3
Merge branch 'master' into as-fastify-apollo
addityasingh Jun 1, 2018
b0b5c7d
Merge branch 'master' into as-fastify-apollo
addityasingh Jun 2, 2018
9b9caf7
Merge branch 'master' into as-fastify-apollo
addityasingh Jun 3, 2018
38208f3
Merge branch 'master' into as-fastify-apollo
addityasingh Jun 7, 2018
8f4920f
Merge branch 'master' into as-fastify-apollo
addityasingh Jun 8, 2018
b3ceac8
Merge branch 'master' into as-fastify-apollo
addityasingh Jun 9, 2018
f82fd09
Merge branch 'master' into as-fastify-apollo
addityasingh Jun 25, 2018
67fe5e1
Merge branch 'master' into as-fastify-apollo
addityasingh Jul 7, 2018
524e711
Merge branch 'master' into as-fastify-apollo
addityasingh Jul 13, 2018
05ecbc5
Merge branch 'master' into as-fastify-apollo
evans Jul 13, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ All of the packages in the `apollo-server` repo are released with the same versi

### vNEXT

* [Issue #626] Integrate apollo-fastify plugin. [PR #1013](https://github.com/apollographql/apollo-server/pull/1013)
* add hapi 16 next() invocation [PR #743](https://github.com/apollographql/apollo-server/pull/743)
* Add skipValidation option [PR #839](https://github.com/apollographql/apollo-server/pull/839)
* `apollo-server-module-graphiql`: adds an option to the constructor to disable url rewriting when editing a query [PR #1047](https://github.com/apollographql/apollo-server/pull/1047)
Expand Down
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ where `<variant>` is one of the following:

* `express`
* `koa`
* `fastify`
* `hapi`
* `restify`
* `lambda`
Expand Down Expand Up @@ -82,6 +83,34 @@ app.use('/graphql', graphqlConnect({ schema: myGraphQLSchema }));
http.createServer(app).listen(PORT);
```

### Fastify

```javascript
import fastify from 'fastify';
import jsonParser from 'fast-json-body';
import { graphqlFastify } from 'apollo-server-fastify';

const myGraphQLSchema = // ... define or import your schema here!
const PORT = 3000;

const app = fastify();

// jsonParser is needed for POST.
app.addContentTypeParser('application/json', function(req, done) {
jsonParser(req, function(err, body) {
done(err, body);
});
});
app.register(graphqlFastify, { schema: myGraphQLSchema });

try {
await app.listen(3007);
} catch (err) {
app.log.error(err);
process.exit(1);
}
```

### Hapi

Now with the Hapi plugins `graphqlHapi` and `graphiqlHapi` you can pass a route object that includes options to be applied to the route. The example below enables CORS on the `/graphql` route.
Expand Down
6 changes: 6 additions & 0 deletions packages/apollo-server-fastify/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
*
!src/**/*
!dist/**/*
dist/**/*.test.*
!package.json
!README.md
50 changes: 50 additions & 0 deletions packages/apollo-server-fastify/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
---
title: Fastify
description: Setting up Apollo Server with Fastify
---

[![Coverage Status](https://coveralls.io/repos/github/apollographql/apollo-server/badge.svg?branch=master)](https://coveralls.io/github/apollographql/apollo-server?branch=master) [![Get on Slack](https://img.shields.io/badge/slack-join-orange.svg)](https://www.apollographql.com/#slack)

This is the Fastify integration of GraphQL Server. Apollo Server is a community-maintained open-source GraphQL server that works with all Node.js HTTP server frameworks: Express, Connect, Fastify, Hapi, Koa and Restify. [Read the docs](https://www.apollographql.com/docs/apollo-server/). [Read the CHANGELOG.](https://github.com/apollographql/apollo-server/blob/master/CHANGELOG.md)

```sh
npm install apollo-server-fastify
```

## Fastify

```js
import fastify from 'fastify';
import jsonParser from 'fast-json-body';
import { graphqlFastify } from 'apollo-server-fastify';

const myGraphQLSchema = // ... define or import your schema here!
const PORT = 3000;

const app = fastify();

// jsonParser is needed for POST.
app.addContentTypeParser('application/json', function(req, done) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi! I'm one of the Fastify maintainers :)
Thank you for implementing this!
Just a nit, you don't need to add a content type parse for application/json, since it's included directly in Fastify!
Besides Fastify performs many security checks that fast-json-body is not doing.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@delvedor Thanks a lot for the pointer :) But I implemented this 4 months back and for some reason I had to explicitly add the content parser. But I am working on V2 of this plugin and will take your point into consideration while doing that

jsonParser(req, function(err, body) {
done(err, body);
});
});
app.register(graphqlFastify, { schema: myGraphQLSchema });

try {
await app.listen(3007);
} catch (err) {
app.log.error(err);
process.exit(1);
}
```

## Principles

GraphQL Server is built with the following principles in mind:

* **By the community, for the community**: GraphQL Server's development is driven by the needs of developers
* **Simplicity**: by keeping things simple, GraphQL Server is easier to use, easier to contribute to, and more secure
* **Performance**: GraphQL Server is well-tested and production-ready - no modifications needed

Anyone is welcome to contribute to GraphQL Server, just read [CONTRIBUTING.md](https://github.com/apollographql/apollo-server/blob/master/CONTRIBUTING.md), take a look at the [roadmap](https://github.com/apollographql/apollo-server/blob/master/ROADMAP.md) and make your first PR!
42 changes: 42 additions & 0 deletions packages/apollo-server-fastify/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{
"name": "apollo-server-fastify",
"version": "1.0.4",
"description": "Production-ready Node.js GraphQL server for fastify",
"main": "dist/index.js",
"scripts": {
"compile": "tsc",
"prepublish": "npm run compile"
},
"repository": {
"type": "git",
"url": "https://github.com/apollographql/apollo-server/tree/master/packages/apollo-server-fastify"
},
"keywords": [
"GraphQL",
"Apollo",
"Server",
"Fastify",
"Javascript"
],
"author": "Aditya pratap Singh <adisinghrajput@gmail.com>",
"license": "MIT",
"bugs": {
"url": "https://github.com/apollographql/apollo-server/issues"
},
"homepage": "https://github.com/apollographql/apollo-server#readme",
"dependencies": {
"apollo-server-core": "^1.3.6",
"apollo-server-module-graphiql": "^1.3.4",
"fastify": "1.3.1"
},
"devDependencies": {
"@types/graphql": "0.12.7",
"apollo-server-integration-testsuite": "^1.3.6",
"fast-json-body": "^1.1.0",
"http2": "^3.3.7"
},
"typings": "dist/index.d.ts",
"typescript": {
"definition": "dist/index.d.ts"
}
}
70 changes: 70 additions & 0 deletions packages/apollo-server-fastify/src/fastifyApollo.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import * as fastify from 'fastify';
import { FastifyInstance } from 'fastify';
const jsonParser = require('fast-json-body');
import { graphqlFastify, graphiqlFastify } from './fastifyApollo';
import testSuite, {
schema,
CreateAppOptions,
} from 'apollo-server-integration-testsuite';
import { expect } from 'chai';
import { GraphQLOptions } from 'apollo-server-core';
import 'mocha';

async function createApp(options: CreateAppOptions = {}) {
const app = fastify();
const graphqlOptions = options.graphqlOptions || { schema };

if (!options.excludeParser) {
// @ts-ignore: Dynamic addContentTypeParser error
app.addContentTypeParser('application/json', function(req, done) {
jsonParser(req, function(err, body) {
done(err, body);
});
});
}

if (options.graphiqlOptions) {
app.register(graphiqlFastify, options.graphiqlOptions);
}
app.register(graphqlFastify, { graphqlOptions });

try {
await app.listen(3007);
} catch (err) {
app.log.error(err);
process.exit(1);
}

return app.server;
}

async function destroyApp(app) {
if (!app || !app.close) {
return;
}
await new Promise(cb => app.close(cb));
}

describe('Fastify', () => {
describe('fastifyApollo', () => {
it('throws error if called without schema', function() {
expect(() =>
graphqlFastify(
{} as FastifyInstance,
undefined as CreateAppOptions,
undefined,
),
).to.throw('Apollo Server requires options.');
});

it('throws an error if called with argument not equal to 3', function() {
expect(() => (<any>graphqlFastify)({}, { graphqlOptions: {} })).to.throw(
'Apollo Server expects exactly 3 argument, got 2',
);
});
});

describe('integration:Fastify', () => {
testSuite(createApp, destroyApp);
});
});
136 changes: 136 additions & 0 deletions packages/apollo-server-fastify/src/fastifyApollo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import * as fastify from 'fastify';
import {
runHttpQuery,
HttpQueryRequest,
GraphQLOptions,
} from 'apollo-server-core';
import * as GraphiQL from 'apollo-server-module-graphiql';
import {
FastifyInstance,
FastifyRequest,
FastifyReply,
Middleware,
} from 'fastify';
import { IncomingMessage, ServerResponse, Server } from 'http';

export function graphqlFastify(
fastify: FastifyInstance,
options: any,
next: (err?: Error) => void,
) {
if (!options || !options.graphqlOptions) {
throw new Error('Apollo Server requires options.');
}

if (arguments.length !== 3) {
throw new Error(
`Apollo Server expects exactly 3 argument, got ${arguments.length}`,
);
}

async function handler<HttpResponse extends ServerResponse>(
request: any,
reply: FastifyReply<HttpResponse>,
) {
const { method } = request.raw;
try {
const gqlResponse = await runHttpQuery([request], {
method: method,
options: options.graphqlOptions,
query: method === 'POST' ? request.body : request.query,
});
reply
.type('application/json')
.code(200)
.header(
'Content-Length',
Buffer.byteLength(JSON.stringify(gqlResponse), 'utf8'),
)
.send(JSON.parse(gqlResponse));
} catch (error) {
if ('HttpQueryError' !== error.name) {
return next(error);
}

if (error.headers) {
Object.keys(error.headers).forEach(header => {
reply.header(header, error.headers[header]);
});
}

let errMessage;
try {
errMessage = JSON.parse(error.message);
} catch {
errMessage = error.message;
}

reply.code(error.statusCode).send(errMessage);
}
}

fastify.route({
method: ['GET', 'POST'],
url: options.url || '/graphql',
handler,
});

// This is a workaround because of this issue https://github.com/fastify/fastify/pull/862
fastify.route({
method: ['HEAD', 'PUT', 'DELETE', 'OPTIONS', 'PATCH'],
url: options.url || '/graphql',
handler: async (req, reply) => {
reply
.code(405)
.header('allow', 'GET, POST')
.send();
},
});

next();
return fastify;
}

export function graphiqlFastify(
fastify: FastifyInstance,
options: any,
next: (err?: Error) => void,
) {
const handler = async (request, reply) => {
try {
const query = request.query;
const giqlResponse = await GraphiQL.resolveGraphiQLString(
query,
options,
request,
);
reply
.header('Content-Type', 'text/html')
.code(200)
.send(giqlResponse);
} catch (error) {
reply.code(500).send(error);
}
};

fastify.route({
method: ['GET', 'POST'],
url: options.url || '/graphiql',
handler,
});

// This is a workaround because of this issue https://github.com/fastify/fastify/pull/862
fastify.route({
method: ['HEAD', 'PUT', 'DELETE', 'OPTIONS', 'PATCH'],
url: options.url || '/graphiql',
handler: async (req, reply) => {
reply
.code(405)
.header('allow', 'GET, POST')
.send();
},
});

next();
return fastify;
}
5 changes: 5 additions & 0 deletions packages/apollo-server-fastify/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Expose types which can be used by both middleware flavors.
export { GraphQLOptions } from 'apollo-server-core';

// Fastify Middleware
export { graphqlFastify, graphiqlFastify } from './fastifyApollo';
9 changes: 9 additions & 0 deletions packages/apollo-server-fastify/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"extends": "../../tsconfig",
"compilerOptions": {
"rootDir": "./src",
"outDir": "./dist",
"typeRoots": ["node_modules/@types"]
},
"exclude": ["node_modules", "dist"]
}
3 changes: 2 additions & 1 deletion test/tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ require('../packages/apollo-server-express/dist/expressApollo.test');
require('../packages/apollo-server-express/dist/connectApollo.test');
(NODE_MAJOR_VERSION >= 9 ||
(NODE_MAJOR_VERSION >= 8 && NODE_MAJOR_REVISION >= 9)) &&
require('../packages/apollo-server-hapi/dist/hapiApollo.test'); // Hapi 17 is 8.9+
require('../packages/apollo-server-fastify/dist/fastifyApollo.test') &&
require('../packages/apollo-server-hapi/dist/hapiApollo.test'); // Hapi 17 and Fastify are 8.9+
NODE_MAJOR_VERSION >= 6 &&
require('../packages/apollo-server-micro/dist/microApollo.test');
NODE_MAJOR_VERSION >= 7 &&
Expand Down