From 9216e4cb09b3408acd40a4f8616d8b10419639c0 Mon Sep 17 00:00:00 2001 From: Marcel Miranda Date: Sat, 28 Jul 2018 13:44:44 +0200 Subject: [PATCH 01/10] Initial implementation of Apollo Server 2 for gcf --- .../apollo-server-cloud-function/.npmignore | 6 + .../apollo-server-cloud-function/README.md | 166 ++++++++++++++++++ .../apollo-server-cloud-function/package.json | 44 +++++ .../src/ApolloServer.ts | 131 ++++++++++++++ .../src/gqlApollo.ts | 59 +++++++ .../apollo-server-cloud-function/src/index.ts | 24 +++ .../tsconfig.json | 9 + 7 files changed, 439 insertions(+) create mode 100644 packages/apollo-server-cloud-function/.npmignore create mode 100644 packages/apollo-server-cloud-function/README.md create mode 100644 packages/apollo-server-cloud-function/package.json create mode 100644 packages/apollo-server-cloud-function/src/ApolloServer.ts create mode 100644 packages/apollo-server-cloud-function/src/gqlApollo.ts create mode 100644 packages/apollo-server-cloud-function/src/index.ts create mode 100644 packages/apollo-server-cloud-function/tsconfig.json diff --git a/packages/apollo-server-cloud-function/.npmignore b/packages/apollo-server-cloud-function/.npmignore new file mode 100644 index 00000000000..a165046d359 --- /dev/null +++ b/packages/apollo-server-cloud-function/.npmignore @@ -0,0 +1,6 @@ +* +!src/**/* +!dist/**/* +dist/**/*.test.* +!package.json +!README.md diff --git a/packages/apollo-server-cloud-function/README.md b/packages/apollo-server-cloud-function/README.md new file mode 100644 index 00000000000..b376d759161 --- /dev/null +++ b/packages/apollo-server-cloud-function/README.md @@ -0,0 +1,166 @@ +--- +title: Google Cloud Functions +description: Setting up Apollo Server with Google Cloud Functions +--- + +[![npm version](https://badge.fury.io/js/apollo-server-cloud-function.svg)](https://badge.fury.io/js/apollo-server-cloud-function) [![Build Status](https://circleci.com/gh/apollographql/apollo-server.svg?style=svg)](https://circleci.com/gh/apollographql/apollo-server) [![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 Google Cloud Function integration of GraphQL Server. Apollo Server is a community-maintained open-source GraphQL server that works with many Node.js HTTP server frameworks. [Read the docs](https://www.apollographql.com/docs/apollo-server/v2). [Read the CHANGELOG](https://github.com/apollographql/apollo-server/blob/master/CHANGELOG.md). + +```sh +npm install apollo-server-cloud-function@rc graphql +``` + +## Deploying with Google Cloud Function + +#### 1. Write the API handlers + +In a file named `graphql.js`, place the following code: + +```js +const { ApolloServer, gql } = require('apollo-server-cloud-function'); + +// Construct a schema, using GraphQL schema language +const typeDefs = gql` + type Query { + hello: String + } +`; + +// Provide resolver functions for your schema fields +const resolvers = { + Query: { + hello: () => 'Hello world!', + }, +}; + +const server = new ApolloServer({ + typeDefs, + resolvers, +}); + +exports.handler = server.createHandler(); +``` + +#### 2. Configure your Cloud function + +#### 4. Deploy the API + +## Getting request info + +To read information about the current request from the API Gateway event (HTTP headers, HTTP method, body, path, ...) or the current Google Cloud Function (Function Name, Function Version, awsRequestId, time remaining, ...) use the options function. This way they can be passed to your schema resolvers using the context option. + +```js +const { ApolloServer, gql } = require('apollo-server-lambda'); + +// Construct a schema, using GraphQL schema language +const typeDefs = gql` + type Query { + hello: String + } +`; + +// Provide resolver functions for your schema fields +const resolvers = { + Query: { + hello: () => 'Hello world!', + }, +}; + +const server = new ApolloServer({ + typeDefs, + resolvers, + context: ({ req, res }) => ({ + // TODO + }), +}); + +exports.handler = server.createHandler(); +``` + +## Modifying the GCF Response (Enable CORS) + +To enable CORS the response HTTP headers need to be modified. To accomplish this use the `cors` option. + +```js +const { ApolloServer, gql } = require('apollo-server-lambda'); + +// Construct a schema, using GraphQL schema language +const typeDefs = gql` + type Query { + hello: String + } +`; + +// Provide resolver functions for your schema fields +const resolvers = { + Query: { + hello: () => 'Hello world!', + }, +}; + +const server = new ApolloServer({ + typeDefs, + resolvers, +}); + +exports.handler = server.createHandler({ + cors: { + origin: '*', + credentials: true, + }, +}); +``` + +To enable CORS response for requests with credentials (cookies, http authentication) the allow origin header must equal the request origin and the allow credential header must be set to true. + +```js +const { ApolloServer, gql } = require('apollo-server-lambda'); + +// Construct a schema, using GraphQL schema language +const typeDefs = gql` + type Query { + hello: String + } +`; + +// Provide resolver functions for your schema fields +const resolvers = { + Query: { + hello: () => 'Hello world!', + }, +}; + +const server = new ApolloServer({ + typeDefs, + resolvers, +}); + +exports.handler = server.createHandler({ + cors: { + origin: true, + credentials: true, + }, +}); +``` + +### Cors Options + +The options correspond to the [express cors configuration](https://github.com/expressjs/cors#configuration-options) with the following fields(all are optional): + +- `origin`: boolean | string | string[] +- `methods`: string | string[] +- `allowedHeaders`: string | string[] +- `exposedHeaders`: string | string[] +- `credentials`: boolean +- `maxAge`: number + +## 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! diff --git a/packages/apollo-server-cloud-function/package.json b/packages/apollo-server-cloud-function/package.json new file mode 100644 index 00000000000..1b431476ca9 --- /dev/null +++ b/packages/apollo-server-cloud-function/package.json @@ -0,0 +1,44 @@ +{ + "name": "apollo-server-cloud-functions", + "version": "2.0.0", + "description": "Production-ready Node.js GraphQL server for Google Cloud Functions", + "keywords": [ + "GraphQL", + "Apollo", + "Server", + "Lambda", + "Javascript" + ], + "author": "opensource@apollographql.com", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/apollographql/apollo-server/tree/master/packages/apollo-server-cloud-functions" + }, + "homepage": "https://github.com/apollographql/apollo-server#readme", + "bugs": { + "url": "https://github.com/apollographql/apollo-server/issues" + }, + "main": "dist/index.js", + "types": "dist/index.d.ts", + "engines": { + "node": ">=6" + }, + "scripts": { + "clean": "rm -rf dist", + "compile": "tsc", + "prepublish": "npm run clean && npm run compile" + }, + "dependencies": { + "@apollographql/graphql-playground-html": "^1.6.0", + "apollo-server-core": "2.0.0", + "apollo-server-env": "2.0.0", + "graphql-tools": "^3.0.4" + }, + "xdevDependencies": { + "apollo-server-integration-testsuite": "2.0.0" + }, + "peerDependencies": { + "graphql": "^0.12.0 || ^0.13.0 || ^14.0.0" + } +} diff --git a/packages/apollo-server-cloud-function/src/ApolloServer.ts b/packages/apollo-server-cloud-function/src/ApolloServer.ts new file mode 100644 index 00000000000..6ca6b797e10 --- /dev/null +++ b/packages/apollo-server-cloud-function/src/ApolloServer.ts @@ -0,0 +1,131 @@ +import { ApolloServerBase } from 'apollo-server-core'; +import { GraphQLOptions, Config } from 'apollo-server-core'; +import { + renderPlaygroundPage, + RenderPageOptions as PlaygroundRenderPageOptions, +} from '@apollographql/graphql-playground-html'; + +import { graphqlLambda } from './gqlApollo'; + +export interface CreateHandlerOptions { + cors?: { + origin?: boolean | string | string[]; + methods?: string | string[]; + allowedHeaders?: string | string[]; + exposedHeaders?: string | string[]; + credentials?: boolean; + maxAge?: number; + }; +} + +const env = Object.assign({}, process.env); +const endpoint = `https://${env.X_GOOGLE_FUNCTION_REGION}-${ + env.X_GOOGLE_GCP_PROJECT +}.cloudfunctions.net/${env.X_GOOGLE_FUNCTION_NAME}`; + +export class ApolloServer extends ApolloServerBase { + // If you feel tempted to add an option to this constructor. Please consider + // another place, since the documentation becomes much more complicated when + // the constructor is not longer shared between all integration + constructor(options: Config) { + if (process.env.ENGINE_API_KEY || options.engine) { + options.engine = { + sendReportsImmediately: true, + ...(typeof options.engine !== 'boolean' ? options.engine : {}), + }; + } + super(options); + } + + // 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 + createGraphQLServerOptions(req, res): Promise { + return super.graphQLServerOptions({ req, res }); + } + + public createHandler({ cors }: CreateHandlerOptions = { cors: undefined }) { + const corsHeaders = {}; + + if (cors) { + if (cors.methods) { + if (typeof cors.methods === 'string') { + corsHeaders['Access-Control-Allow-Methods'] = cors.methods; + } else if (Array.isArray(cors.methods)) { + corsHeaders['Access-Control-Allow-Methods'] = cors.methods.join(','); + } + } + + if (cors.allowedHeaders) { + if (typeof cors.allowedHeaders === 'string') { + corsHeaders['Access-Control-Allow-Headers'] = cors.allowedHeaders; + } else if (Array.isArray(cors.allowedHeaders)) { + corsHeaders[ + 'Access-Control-Allow-Headers' + ] = cors.allowedHeaders.join(','); + } + } + + if (cors.exposedHeaders) { + if (typeof cors.exposedHeaders === 'string') { + corsHeaders['Access-Control-Expose-Headers'] = cors.exposedHeaders; + } else if (Array.isArray(cors.exposedHeaders)) { + corsHeaders[ + 'Access-Control-Expose-Headers' + ] = cors.exposedHeaders.join(','); + } + } + + if (cors.credentials) { + corsHeaders['Access-Control-Allow-Credentials'] = 'true'; + } + if (cors.maxAge) { + corsHeaders['Access-Control-Max-Age'] = cors.maxAge; + } + } + + return (req: any, res: any) => { + if (cors && cors.origin) { + if (typeof cors.origin === 'string') { + res.set('Access-Control-Allow-Origin', cors.origin); + } else if ( + typeof cors.origin === 'boolean' || + (Array.isArray(cors.origin) && + cors.origin.includes(req.get('origin'))) + ) { + res.set('Access-Control-Allow-Origin', req.get('origin')); + } + + if (!cors.allowedHeaders) { + res.set( + 'Access-Control-Allow-Headers', + req.get('Access-Control-Request-Headers'), + ); + } + } + + if (req.method === 'OPTIONS') { + res.status(204).send(''); + return; + } + + if (this.playgroundOptions && req.method === 'GET') { + if (req.accepts('text/html')) { + const playgroundRenderPageOptions: PlaygroundRenderPageOptions = { + endpoint, + ...this.playgroundOptions, + }; + + res + .status(200) + .send(renderPlaygroundPage(playgroundRenderPageOptions)); + return; + } + } + + res.set(corsHeaders); + + graphqlLambda(this.createGraphQLServerOptions.bind(this))(req, res); + }; + } +} diff --git a/packages/apollo-server-cloud-function/src/gqlApollo.ts b/packages/apollo-server-cloud-function/src/gqlApollo.ts new file mode 100644 index 00000000000..c3760507d08 --- /dev/null +++ b/packages/apollo-server-cloud-function/src/gqlApollo.ts @@ -0,0 +1,59 @@ +import { + GraphQLOptions, + HttpQueryError, + runHttpQuery, +} from 'apollo-server-core'; +import { Headers } from 'apollo-server-env'; + +export function graphqlLambda(options: GraphQLOptions): any { + if (!options) { + throw new Error('Apollo Server requires options.'); + } + + if (arguments.length > 1) { + throw new Error( + `Apollo Server expects exactly one argument, got ${arguments.length}`, + ); + } + + const graphqlHandler: any = (req, res): void => { + if (req.method === 'POST' && !req.body) { + res.status(500).send('POST body missing.'); + return; + } + + runHttpQuery([req, res], { + method: req.method, + options: options, + query: req.method === 'POST' ? req.body : (req.query as any), + request: { + url: req.url, + method: req.method, + headers: new Headers(req.headers), // ? Check if this actually works + }, + }).then( + ({ graphqlResponse, responseInit }) => { + res + .status(200) + .set(responseInit.headers) + .send(graphqlResponse); + }, + (error: HttpQueryError) => { + console.log('Error!'); + console.log(JSON.stringify(error)); + if ('HttpQueryError' !== error.name) { + res.status(500).send(error); + return; + } + console.log('other error'); + console.log(JSON.stringify(error)); + res + .status(error.statusCode) + .set(error.headers) + .send(error.message); + }, + ); + }; + + return graphqlHandler; +} diff --git a/packages/apollo-server-cloud-function/src/index.ts b/packages/apollo-server-cloud-function/src/index.ts new file mode 100644 index 00000000000..2ff184ef590 --- /dev/null +++ b/packages/apollo-server-cloud-function/src/index.ts @@ -0,0 +1,24 @@ +export { + GraphQLUpload, + GraphQLOptions, + GraphQLExtension, + Config, + gql, + // Errors + ApolloError, + toApolloError, + SyntaxError, + ValidationError, + AuthenticationError, + ForbiddenError, + UserInputError, + // playground + defaultPlaygroundOptions, + PlaygroundConfig, + PlaygroundRenderPageOptions, +} from 'apollo-server-core'; + +export * from 'graphql-tools'; + +// ApolloServer integration. +export { ApolloServer, CreateHandlerOptions } from './ApolloServer'; diff --git a/packages/apollo-server-cloud-function/tsconfig.json b/packages/apollo-server-cloud-function/tsconfig.json new file mode 100644 index 00000000000..5ac3c46b1f6 --- /dev/null +++ b/packages/apollo-server-cloud-function/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "rootDir": "./src", + "outDir": "./dist", + "lib": ["es2017", "esnext.asynciterable", "dom"] + }, + "exclude": ["node_modules", "dist"] +} From 28c4eb3e19108355091eedbdf59ceb8d27972298 Mon Sep 17 00:00:00 2001 From: Marcel Miranda Date: Sat, 28 Jul 2018 18:01:44 +0200 Subject: [PATCH 02/10] First try at running with tests --- .../apollo-server-cloud-function/package.json | 2 +- .../src/gqlApollo.test.ts | 30 +++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 packages/apollo-server-cloud-function/src/gqlApollo.test.ts diff --git a/packages/apollo-server-cloud-function/package.json b/packages/apollo-server-cloud-function/package.json index 1b431476ca9..b797b0dd27e 100644 --- a/packages/apollo-server-cloud-function/package.json +++ b/packages/apollo-server-cloud-function/package.json @@ -35,7 +35,7 @@ "apollo-server-env": "2.0.0", "graphql-tools": "^3.0.4" }, - "xdevDependencies": { + "devDependencies": { "apollo-server-integration-testsuite": "2.0.0" }, "peerDependencies": { diff --git a/packages/apollo-server-cloud-function/src/gqlApollo.test.ts b/packages/apollo-server-cloud-function/src/gqlApollo.test.ts new file mode 100644 index 00000000000..56393128b52 --- /dev/null +++ b/packages/apollo-server-cloud-function/src/gqlApollo.test.ts @@ -0,0 +1,30 @@ +import { ApolloServer } from './ApolloServer'; +import testSuite, { + schema as Schema, + CreateAppOptions, +} from 'apollo-server-integration-testsuite'; +import { Config } from 'apollo-server-core'; +import 'mocha'; +import { IncomingMessage, ServerResponse } from 'http'; + +const createCloudFunction = (options: CreateAppOptions = {}) => { + const server = new ApolloServer( + (options.graphqlOptions as Config) || { schema: Schema }, + ); + + const handler = server.createHandler(); + + return (req: IncomingMessage, res: ServerResponse) => { + // return 404 if path is /bogus-route to pass the test, lambda doesn't have paths + if (req.url.includes('/bogus-route')) { + res.statusCode = 404; + return res.end(); + } + + return handler(req, res); + }; +}; + +describe('integration:CloudFunction', () => { + testSuite(createCloudFunction); +}); From 490351686b4cd24517bfefbfae8d2f175b376936 Mon Sep 17 00:00:00 2001 From: Marcel Miranda Date: Sat, 28 Jul 2018 18:09:21 +0200 Subject: [PATCH 03/10] Updated naming --- packages/apollo-server-cloud-function/src/ApolloServer.ts | 7 +++++-- packages/apollo-server-cloud-function/src/gqlApollo.ts | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/apollo-server-cloud-function/src/ApolloServer.ts b/packages/apollo-server-cloud-function/src/ApolloServer.ts index 6ca6b797e10..ef1edfb8744 100644 --- a/packages/apollo-server-cloud-function/src/ApolloServer.ts +++ b/packages/apollo-server-cloud-function/src/ApolloServer.ts @@ -5,7 +5,7 @@ import { RenderPageOptions as PlaygroundRenderPageOptions, } from '@apollographql/graphql-playground-html'; -import { graphqlLambda } from './gqlApollo'; +import { graphqlCloudFunction } from './gqlApollo'; export interface CreateHandlerOptions { cors?: { @@ -125,7 +125,10 @@ export class ApolloServer extends ApolloServerBase { res.set(corsHeaders); - graphqlLambda(this.createGraphQLServerOptions.bind(this))(req, res); + graphqlCloudFunction(this.createGraphQLServerOptions.bind(this))( + req, + res, + ); }; } } diff --git a/packages/apollo-server-cloud-function/src/gqlApollo.ts b/packages/apollo-server-cloud-function/src/gqlApollo.ts index c3760507d08..4b2c3809342 100644 --- a/packages/apollo-server-cloud-function/src/gqlApollo.ts +++ b/packages/apollo-server-cloud-function/src/gqlApollo.ts @@ -5,7 +5,7 @@ import { } from 'apollo-server-core'; import { Headers } from 'apollo-server-env'; -export function graphqlLambda(options: GraphQLOptions): any { +export function graphqlCloudFunction(options: GraphQLOptions): any { if (!options) { throw new Error('Apollo Server requires options.'); } From 96c7f490695c55216aa9dea7a4ec2b4e038b4274 Mon Sep 17 00:00:00 2001 From: Marcel Miranda Date: Sat, 28 Jul 2018 18:20:10 +0200 Subject: [PATCH 04/10] Removed lambda mentions --- packages/apollo-server-cloud-function/README.md | 6 +++--- packages/apollo-server-cloud-function/package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/apollo-server-cloud-function/README.md b/packages/apollo-server-cloud-function/README.md index b376d759161..188cce44300 100644 --- a/packages/apollo-server-cloud-function/README.md +++ b/packages/apollo-server-cloud-function/README.md @@ -51,7 +51,7 @@ exports.handler = server.createHandler(); To read information about the current request from the API Gateway event (HTTP headers, HTTP method, body, path, ...) or the current Google Cloud Function (Function Name, Function Version, awsRequestId, time remaining, ...) use the options function. This way they can be passed to your schema resolvers using the context option. ```js -const { ApolloServer, gql } = require('apollo-server-lambda'); +const { ApolloServer, gql } = require('apollo-server-cloud-function'); // Construct a schema, using GraphQL schema language const typeDefs = gql` @@ -83,7 +83,7 @@ exports.handler = server.createHandler(); To enable CORS the response HTTP headers need to be modified. To accomplish this use the `cors` option. ```js -const { ApolloServer, gql } = require('apollo-server-lambda'); +const { ApolloServer, gql } = require('apollo-server-cloud-function'); // Construct a schema, using GraphQL schema language const typeDefs = gql` @@ -115,7 +115,7 @@ exports.handler = server.createHandler({ To enable CORS response for requests with credentials (cookies, http authentication) the allow origin header must equal the request origin and the allow credential header must be set to true. ```js -const { ApolloServer, gql } = require('apollo-server-lambda'); +const { ApolloServer, gql } = require('apollo-server-cloud-function'); // Construct a schema, using GraphQL schema language const typeDefs = gql` diff --git a/packages/apollo-server-cloud-function/package.json b/packages/apollo-server-cloud-function/package.json index b797b0dd27e..86d203eb96f 100644 --- a/packages/apollo-server-cloud-function/package.json +++ b/packages/apollo-server-cloud-function/package.json @@ -6,7 +6,7 @@ "GraphQL", "Apollo", "Server", - "Lambda", + "Google Cloud Functions", "Javascript" ], "author": "opensource@apollographql.com", From 7845ec1dcf1541b7c82a018da593e130c8338e67 Mon Sep 17 00:00:00 2001 From: Marcel Miranda Date: Sat, 28 Jul 2018 18:40:17 +0200 Subject: [PATCH 05/10] Simply use referer --- packages/apollo-server-cloud-function/src/ApolloServer.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/packages/apollo-server-cloud-function/src/ApolloServer.ts b/packages/apollo-server-cloud-function/src/ApolloServer.ts index ef1edfb8744..09b0cb8e365 100644 --- a/packages/apollo-server-cloud-function/src/ApolloServer.ts +++ b/packages/apollo-server-cloud-function/src/ApolloServer.ts @@ -18,11 +18,6 @@ export interface CreateHandlerOptions { }; } -const env = Object.assign({}, process.env); -const endpoint = `https://${env.X_GOOGLE_FUNCTION_REGION}-${ - env.X_GOOGLE_GCP_PROJECT -}.cloudfunctions.net/${env.X_GOOGLE_FUNCTION_NAME}`; - export class ApolloServer extends ApolloServerBase { // If you feel tempted to add an option to this constructor. Please consider // another place, since the documentation becomes much more complicated when @@ -112,7 +107,7 @@ export class ApolloServer extends ApolloServerBase { if (this.playgroundOptions && req.method === 'GET') { if (req.accepts('text/html')) { const playgroundRenderPageOptions: PlaygroundRenderPageOptions = { - endpoint, + endpoint: req.get('referer'), ...this.playgroundOptions, }; From 9bdd522da0105c3bba4faa2e3090d0f26f3fd805 Mon Sep 17 00:00:00 2001 From: Marcel Miranda Date: Sat, 28 Jul 2018 19:09:42 +0200 Subject: [PATCH 06/10] Updated README --- packages/apollo-server-cloud-function/README.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/apollo-server-cloud-function/README.md b/packages/apollo-server-cloud-function/README.md index 188cce44300..2a4692eed78 100644 --- a/packages/apollo-server-cloud-function/README.md +++ b/packages/apollo-server-cloud-function/README.md @@ -42,9 +42,10 @@ const server = new ApolloServer({ exports.handler = server.createHandler(); ``` -#### 2. Configure your Cloud function +#### 2. Configure your Cloud function and deploy -#### 4. Deploy the API +Set the _Function to execute_ option to _handler_ +and deploy ## Getting request info @@ -71,7 +72,9 @@ const server = new ApolloServer({ typeDefs, resolvers, context: ({ req, res }) => ({ - // TODO + headers: req.headers, + req, + res, }), }); From 14c5556aa997fe8c2317e3129a019b3bb116bd0f Mon Sep 17 00:00:00 2001 From: Marcel Miranda Date: Sat, 28 Jul 2018 19:09:56 +0200 Subject: [PATCH 07/10] Updated Changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f141f8ca28..63f539d6f08 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ All of the packages in the `apollo-server` repo are released with the same versi ### vNEXT +- Google Cloud Function support [#1402](https://github.com/apollographql/apollo-server/issues/1402) + ### v2.0.4 - apollo-server: Release due to failed build and install From e77bc58f03b65dc91358160e09931d1223a00726 Mon Sep 17 00:00:00 2001 From: Marcel Miranda Date: Tue, 31 Jul 2018 16:21:37 +0200 Subject: [PATCH 08/10] Renamed gqlApollo to googleCloudApollo --- packages/apollo-server-cloud-function/src/ApolloServer.ts | 2 +- .../src/{gqlApollo.test.ts => googleCloudApollo.test.ts} | 0 .../src/{gqlApollo.ts => googleCloudApollo.ts} | 0 3 files changed, 1 insertion(+), 1 deletion(-) rename packages/apollo-server-cloud-function/src/{gqlApollo.test.ts => googleCloudApollo.test.ts} (100%) rename packages/apollo-server-cloud-function/src/{gqlApollo.ts => googleCloudApollo.ts} (100%) diff --git a/packages/apollo-server-cloud-function/src/ApolloServer.ts b/packages/apollo-server-cloud-function/src/ApolloServer.ts index 09b0cb8e365..5f58fef8417 100644 --- a/packages/apollo-server-cloud-function/src/ApolloServer.ts +++ b/packages/apollo-server-cloud-function/src/ApolloServer.ts @@ -5,7 +5,7 @@ import { RenderPageOptions as PlaygroundRenderPageOptions, } from '@apollographql/graphql-playground-html'; -import { graphqlCloudFunction } from './gqlApollo'; +import { graphqlCloudFunction } from './googleCloudApollo'; export interface CreateHandlerOptions { cors?: { diff --git a/packages/apollo-server-cloud-function/src/gqlApollo.test.ts b/packages/apollo-server-cloud-function/src/googleCloudApollo.test.ts similarity index 100% rename from packages/apollo-server-cloud-function/src/gqlApollo.test.ts rename to packages/apollo-server-cloud-function/src/googleCloudApollo.test.ts diff --git a/packages/apollo-server-cloud-function/src/gqlApollo.ts b/packages/apollo-server-cloud-function/src/googleCloudApollo.ts similarity index 100% rename from packages/apollo-server-cloud-function/src/gqlApollo.ts rename to packages/apollo-server-cloud-function/src/googleCloudApollo.ts From 40fe3b7e09eb033eaace45d99658ca9f2dfaefb6 Mon Sep 17 00:00:00 2001 From: Marcel Miranda Date: Thu, 2 Aug 2018 15:57:26 +0200 Subject: [PATCH 09/10] Added more details --- .../apollo-server-cloud-function/README.md | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/packages/apollo-server-cloud-function/README.md b/packages/apollo-server-cloud-function/README.md index 2a4692eed78..07b651c34d4 100644 --- a/packages/apollo-server-cloud-function/README.md +++ b/packages/apollo-server-cloud-function/README.md @@ -15,7 +15,7 @@ npm install apollo-server-cloud-function@rc graphql #### 1. Write the API handlers -In a file named `graphql.js`, place the following code: +First, create a `package.json` file and include `apollo-server-cloud-function` in your dependencies. Then in a file named `index.js`, place the following code: ```js const { ApolloServer, gql } = require('apollo-server-cloud-function'); @@ -37,19 +37,27 @@ const resolvers = { const server = new ApolloServer({ typeDefs, resolvers, + playground: true, + introspection: true, }); exports.handler = server.createHandler(); ``` -#### 2. Configure your Cloud function and deploy +#### 2. Configure your Cloud Function and deploy -Set the _Function to execute_ option to _handler_ -and deploy +On the Create Function page, set _Trigger_ to `HTTP` and _Function to execute_ to the name of your exported handler, in this case `handler`. + +Since NODE_ENV is a reserved environment variable in GCF and it defaults to "production", both the **playground** and **introspection** +options need to be explicitly set to `true` for the GraphQL Playground to work correctly. + +After configuring your Function you can press **Create** and an http endpoint will be created a few seconds later. + +You can refer to the [Cloud Functions documentation](https://cloud.google.com/functions/docs/quickstart-console) for more details ## Getting request info -To read information about the current request from the API Gateway event (HTTP headers, HTTP method, body, path, ...) or the current Google Cloud Function (Function Name, Function Version, awsRequestId, time remaining, ...) use the options function. This way they can be passed to your schema resolvers using the context option. +To read information about the currently executing Google Cloud Function (HTTP headers, HTTP method, body, path, ...) use the context option. This way you can pass any request specific data to your schema resolvers. ```js const { ApolloServer, gql } = require('apollo-server-cloud-function'); From 25fa7bd859d39840c2195910a06609f4043a8028 Mon Sep 17 00:00:00 2001 From: Marcel Miranda Date: Thu, 2 Aug 2018 16:04:46 +0200 Subject: [PATCH 10/10] Removed extra check --- packages/apollo-server-cloud-function/src/ApolloServer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/apollo-server-cloud-function/src/ApolloServer.ts b/packages/apollo-server-cloud-function/src/ApolloServer.ts index 5f58fef8417..8517015f5d4 100644 --- a/packages/apollo-server-cloud-function/src/ApolloServer.ts +++ b/packages/apollo-server-cloud-function/src/ApolloServer.ts @@ -80,7 +80,7 @@ export class ApolloServer extends ApolloServerBase { } return (req: any, res: any) => { - if (cors && cors.origin) { + if (cors) { if (typeof cors.origin === 'string') { res.set('Access-Control-Allow-Origin', cors.origin); } else if (