1 | [![npm version](https://badge.fury.io/js/apollo-server-lambda.svg)](https://badge.fury.io/js/apollo-server-lambda)
|
2 | [![Build Status](https://circleci.com/gh/apollographql/apollo-server/tree/main.svg?style=svg)](https://circleci.com/gh/apollographql/apollo-server)
|
3 | [![Join the community forum](https://img.shields.io/badge/join%20the%20community-forum-blueviolet)](https://community.apollographql.com)
|
4 | [![Read CHANGELOG](https://img.shields.io/badge/read-changelog-blue)](https://github.com/apollographql/apollo-server/blob/HEAD/CHANGELOG.md)
|
5 |
|
6 |
|
7 | This is the AWS Lambda 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/main/CHANGELOG.md).
|
8 |
|
9 | ```shell
|
10 | npm install apollo-server-lambda graphql
|
11 | ```
|
12 |
|
13 | ## Deploying with AWS Serverless Application Model (SAM)
|
14 |
|
15 | To deploy the AWS Lambda function we must create a Cloudformation Template and a S3 bucket to store the artifact (zip of source code) and template. We will use the [AWS Command Line Interface](https://aws.amazon.com/cli/).
|
16 |
|
17 | #### 1. Write the API handlers
|
18 |
|
19 | In a file named `graphql.js`, place the following code:
|
20 |
|
21 | ```js
|
22 | const { ApolloServer, gql } = require('apollo-server-lambda');
|
23 | const { ApolloServerPluginLandingPageGraphQLPlayground } = require('apollo-server-core');
|
24 |
|
25 | // Construct a schema, using GraphQL schema language
|
26 | const typeDefs = gql`
|
27 | type Query {
|
28 | hello: String
|
29 | }
|
30 | `;
|
31 |
|
32 | // Provide resolver functions for your schema fields
|
33 | const resolvers = {
|
34 | Query: {
|
35 | hello: () => 'Hello world!',
|
36 | },
|
37 | };
|
38 |
|
39 | const server = new ApolloServer({
|
40 | typeDefs,
|
41 | resolvers,
|
42 |
|
43 | // By default, the GraphQL Playground interface and GraphQL introspection
|
44 | // is disabled in "production" (i.e. when `process.env.NODE_ENV` is `production`).
|
45 | //
|
46 | // If you'd like to have GraphQL Playground and introspection enabled in production,
|
47 | // install the Playground plugin and set the `introspection` option explicitly to `true`.
|
48 | introspection: true,
|
49 | plugins: [ApolloServerPluginLandingPageGraphQLPlayground()],
|
50 | });
|
51 |
|
52 | exports.handler = server.createHandler();
|
53 | ```
|
54 |
|
55 | #### 2. Create an S3 bucket
|
56 |
|
57 | The bucket name must be universally unique.
|
58 |
|
59 | ```bash
|
60 | aws s3 mb s3://<bucket name>
|
61 | ```
|
62 |
|
63 | #### 3. Create the Template
|
64 |
|
65 | This will look for a file called graphql.js with the export `graphqlHandler`. It creates one API endpoints:
|
66 |
|
67 | * `/graphql` (GET and POST)
|
68 |
|
69 | In a file called `template.yaml`:
|
70 |
|
71 | ```yaml
|
72 | AWSTemplateFormatVersion: '2010-09-09'
|
73 | Transform: AWS::Serverless-2016-10-31
|
74 | Resources:
|
75 | GraphQL:
|
76 | Type: AWS::Serverless::Function
|
77 | Properties:
|
78 | Handler: graphql.handler
|
79 | Runtime: nodejs14.x
|
80 | Events:
|
81 | AnyRequest:
|
82 | Type: Api
|
83 | Properties:
|
84 | Path: /graphql
|
85 | Method: ANY
|
86 | ```
|
87 |
|
88 | #### 4. Package source code and dependencies
|
89 |
|
90 | This will read and transform the template, created in previous step. Package and upload the artifact to the S3 bucket and generate another template for the deployment.
|
91 |
|
92 | ```shell
|
93 | aws cloudformation package \
|
94 | --template-file template.yaml \
|
95 | --output-template-file serverless-output.yaml \
|
96 | --s3-bucket <bucket-name>
|
97 | ```
|
98 |
|
99 | #### 5. Deploy the API
|
100 |
|
101 | This will create the Lambda Function and API Gateway for GraphQL. We use the stack-name `prod` to mean production but any stack name can be used.
|
102 |
|
103 | ```
|
104 | aws cloudformation deploy \
|
105 | --template-file serverless-output.yaml \
|
106 | --stack-name prod \
|
107 | --capabilities CAPABILITY_IAM
|
108 | ```
|
109 |
|
110 |
|
111 | ## Customizing HTTP serving
|
112 |
|
113 | `apollo-server-lambda` is built on top of `apollo-server-express`. It combines the HTTP server framework `express` with a package called `@vendia/serverless-express` that translates between Lambda events and Express requests. By default, this is entirely behind the scenes, but you can also provide your own express app with the `expressAppFromMiddleware` option to `createHandler`:
|
114 |
|
115 | ```js
|
116 | const { ApolloServer } = require('apollo-server-lambda');
|
117 | const express = require('express');
|
118 |
|
119 | exports.handler = server.createHandler({
|
120 | expressAppFromMiddleware(middleware) {
|
121 | const app = express();
|
122 | app.use(someOtherMiddleware);
|
123 | app.use(middleware);
|
124 | return app;
|
125 | }
|
126 | });
|
127 | ```
|
128 |
|
129 | ## Getting request info
|
130 |
|
131 | Your ApolloServer's `context` function can read information about the current operation from both the original Lambda data structures and the Express request and response created by `@vendia/serverless-express`. These are provided to your `context` function as `event`, `context`, and `express` options.
|
132 |
|
133 | The `event` object contains the API Gateway event (HTTP headers, HTTP method, body, path, ...). The `context` object (not to be confused with the `context` function itself!) contains the current Lambda Context (Function Name, Function Version, awsRequestId, time remaining, ...). `express` contains `req` and `res` fields with the Express request and response. The object returned from your `context` function is provided to all of your schema resolvers in the third `context` argument.
|
134 |
|
135 | ```js
|
136 | const { ApolloServer, gql } = require('apollo-server-lambda');
|
137 |
|
138 | // Construct a schema, using GraphQL schema language
|
139 | const typeDefs = gql`
|
140 | type Query {
|
141 | hello: String
|
142 | }
|
143 | `;
|
144 |
|
145 | // Provide resolver functions for your schema fields
|
146 | const resolvers = {
|
147 | Query: {
|
148 | hello: () => 'Hello world!',
|
149 | },
|
150 | };
|
151 |
|
152 | const server = new ApolloServer({
|
153 | typeDefs,
|
154 | resolvers,
|
155 | context: ({ event, context, express }) => ({
|
156 | headers: event.headers,
|
157 | functionName: context.functionName,
|
158 | event,
|
159 | context,
|
160 | expressRequest: express.req,
|
161 | }),
|
162 | });
|
163 |
|
164 | exports.graphqlHandler = server.createHandler();
|
165 | ```
|
166 |
|
167 | ## Modifying the Lambda Response (Enable CORS)
|
168 |
|
169 | To enable CORS the response HTTP headers need to be modified. To accomplish this use the `cors` option.
|
170 |
|
171 | ```js
|
172 | const { ApolloServer, gql } = require('apollo-server-lambda');
|
173 |
|
174 | // Construct a schema, using GraphQL schema language
|
175 | const typeDefs = gql`
|
176 | type Query {
|
177 | hello: String
|
178 | }
|
179 | `;
|
180 |
|
181 | // Provide resolver functions for your schema fields
|
182 | const resolvers = {
|
183 | Query: {
|
184 | hello: () => 'Hello world!',
|
185 | },
|
186 | };
|
187 |
|
188 | const server = new ApolloServer({
|
189 | typeDefs,
|
190 | resolvers,
|
191 | });
|
192 |
|
193 | exports.handler = server.createHandler({
|
194 | expressGetMiddlewareOptions: {
|
195 | cors: {
|
196 | origin: '*',
|
197 | credentials: true,
|
198 | }
|
199 | },
|
200 | });
|
201 | ```
|
202 |
|
203 | 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.
|
204 |
|
205 | ```js
|
206 | const { ApolloServer, gql } = require('apollo-server-lambda');
|
207 |
|
208 | // Construct a schema, using GraphQL schema language
|
209 | const typeDefs = gql`
|
210 | type Query {
|
211 | hello: String
|
212 | }
|
213 | `;
|
214 |
|
215 | // Provide resolver functions for your schema fields
|
216 | const resolvers = {
|
217 | Query: {
|
218 | hello: () => 'Hello world!',
|
219 | },
|
220 | };
|
221 |
|
222 | const server = new ApolloServer({
|
223 | typeDefs,
|
224 | resolvers,
|
225 | });
|
226 |
|
227 | exports.handler = server.createHandler({
|
228 | expressGetMiddlewareOptions: {
|
229 | cors: {
|
230 | origin: true,
|
231 | credentials: true,
|
232 | }
|
233 | },
|
234 | });
|
235 | ```
|
236 |
|
237 | ### Cors Options
|
238 |
|
239 | The options correspond to the [express cors configuration](https://github.com/expressjs/cors#configuration-options) with the following fields(all are optional):
|
240 |
|
241 | * `origin`: boolean | string | string[]
|
242 | * `methods`: string | string[]
|
243 | * `allowedHeaders`: string | string[]
|
244 | * `exposedHeaders`: string | string[]
|
245 | * `credentials`: boolean
|
246 | * `maxAge`: number
|
247 |
|
248 | ## Principles
|
249 |
|
250 | GraphQL Server is built with the following principles in mind:
|
251 |
|
252 | * **By the community, for the community**: GraphQL Server's development is driven by the needs of developers
|
253 | * **Simplicity**: by keeping things simple, GraphQL Server is easier to use, easier to contribute to, and more secure
|
254 | * **Performance**: GraphQL Server is well-tested and production-ready - no modifications needed
|
255 |
|
256 | Anyone is welcome to contribute to GraphQL Server, just read [CONTRIBUTING.md](https://github.com/apollographql/apollo-server/blob/main/CONTRIBUTING.md), take a look at the [roadmap](https://github.com/apollographql/apollo-server/blob/main/ROADMAP.md) and make your first PR!
|