Skip to content

Commit bb915d4

Browse files
committed
Updated documentation with new event and package info
1 parent a704f02 commit bb915d4

File tree

1 file changed

+215
-75
lines changed

1 file changed

+215
-75
lines changed

docs/source/deployment/lambda.mdx

Lines changed: 215 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -60,30 +60,6 @@ const server = new ApolloServer({
6060
});
6161
```
6262

63-
```js title="server.js"
64-
import { ApolloServer } from '@apollo/server';
65-
66-
// The GraphQL schema
67-
const typeDefs = `#graphql
68-
type Query {
69-
hello: String
70-
}
71-
`;
72-
73-
// A map of functions which return data for the schema.
74-
const resolvers = {
75-
Query: {
76-
hello: () => 'world',
77-
},
78-
};
79-
80-
// Set up Apollo Server
81-
const server = new ApolloServer({
82-
typeDefs,
83-
resolvers,
84-
});
85-
```
86-
8763
</MultiCodeBlock>
8864

8965
Now we can import the `startServerAndCreateLambdaHandler` function from [`@as-integrations/aws-lambda`](https://www.npmjs.com/package/@as-integrations/aws-lambda), passing in our `ApolloServer` instance:
@@ -92,32 +68,12 @@ Now we can import the `startServerAndCreateLambdaHandler` function from [`@as-in
9268

9369
```ts title="src/server.ts"
9470
import { ApolloServer } from '@apollo/server';
95-
import { startServerAndCreateLambdaHandler } from '@as-integrations/aws-lambda'; //highlight-line
96-
97-
const typeDefs = `#graphql
98-
type Query {
99-
hello: String
100-
}
101-
`;
102-
103-
const resolvers = {
104-
Query: {
105-
hello: () => 'world',
106-
},
107-
};
108-
109-
const server = new ApolloServer({
110-
typeDefs,
111-
resolvers,
112-
});
113-
114-
// This final export is important!
115-
export const graphqlHandler = startServerAndCreateLambdaHandler(server); //highlight-line
116-
```
117-
118-
```js title="server.js"
119-
import { ApolloServer } from '@apollo/server';
120-
import { startServerAndCreateLambdaHandler } from '@as-integrations/aws-lambda'; //highlight-line
71+
// highlight-start
72+
import {
73+
startServerAndCreateLambdaHandler,
74+
handlers,
75+
} from '@as-integrations/aws-lambda';
76+
// highlight-end
12177

12278
const typeDefs = `#graphql
12379
type Query {
@@ -137,7 +93,13 @@ const server = new ApolloServer({
13793
});
13894

13995
// This final export is important!
140-
export const graphqlHandler = startServerAndCreateLambdaHandler(server); //highlight-line
96+
// highlight-start
97+
export const graphqlHandler = startServerAndCreateLambdaHandler(
98+
server,
99+
// We will be using the Proxy V2 handler
100+
handlers.createAPIGatewayProxyEventV2RequestHandler()
101+
);
102+
// highlight-end
141103
```
142104

143105
</MultiCodeBlock>
@@ -206,13 +168,19 @@ You can store a mock HTTP requests locally by creating a `query.json` file, like
206168
```json title="query.json"
207169
{
208170
"version": "2",
209-
"httpMethod": "POST",
210-
"path": "/",
211171
"headers": {
212-
"content-type": "application/json"
172+
"content-type": "application/json",
213173
},
214-
"requestContext": {},
174+
"isBase64Encoded": false,
215175
"rawQueryString": "",
176+
"requestContext": {
177+
"http": {
178+
"method": "POST",
179+
},
180+
// Other requestContext properties omitted for brevity
181+
},
182+
"rawPath": "/",
183+
"routeKey": "/",
216184
"body": "{\"operationName\": null, \"variables\": null, \"query\": \"{ hello }\"}"
217185
}
218186
```
@@ -284,42 +252,177 @@ If you ever want to remove the S3 bucket or Lambda functions that Serverless cre
284252
serverless remove
285253
```
286254

287-
## Customizing HTTP behavior
255+
## Middleware
288256

289-
The `@as-integrations/aws-lambda` package is compatible with the following event types `APIGatewayProxyEvent`, `APIGatewayProxyEventV2`, `ALBEvent`. This supports a wide range of services like API Gateway HTTP Proxy APIs, API Gateway REST Proxy APIs, Lambda Function URLs, and Application Load Balancers. However, it does not let you customize HTTP behavior directly or support other AWS products that invoke Lambda functions (e.g., S3 or DynamoDB).
257+
In order to impelment event and result mutations, type-safe middleware can be passed to the `startServerAndCreateLambdaHandler` call. The API is as follows:
290258

291-
If you want to customize your HTTP behavior, you can couple Apollo Server's Express integration (i.e., [`expressMiddleware`](../api/express-middleware)) with the [`@vendia/serverless-express`](https://github.com/vendia/serverless-express) package. The `@vendia/serverless-express` library translates between Lambda events and Express requests. Despite their similar names, the Serverless CLI and the `@vendia/serverless-express` package are unrelated.
259+
```ts
292260
293-
You can update your Apollo Server setup to the following to have a fully functioning Lambda server that works in a variety of AWS features:
261+
import { middleware, startServerAndCreateLambdaHandler, handlers } from "@as-integrations/aws-lambda";
294262
295-
<MultiCodeBlock>
263+
const requestHandler = handlers.createAPIGatewayProxyEventV2RequestHandler()
264+
265+
// All middleware must be async and will be strictly typed based on the `requestHandler`'s types
266+
const middlewareFn: middleware.MiddlewareFn<typeof requestHandler> = async (event) => {
267+
// event is intended to be mutable and can be updated here
268+
269+
// optional result mutation, otherwise function returns `Promise<void>`
270+
return async (result) => {
271+
// result is intended to be mutable and can be updated here
272+
273+
// `Promise<void>` return
274+
}
275+
}
276+
277+
startServerAndCreateLambdaHandler(..., ..., {
278+
middleware: [middlewareFn],
279+
})
280+
```
281+
282+
An example use case would be for cookie modificaiton. The `APIGatewayProxyStructuredResultV2` type contains a property `cookies` that then mutate to multiple `Set-Cookie` headers.
296283

297284
```ts
298-
const { ApolloServer } = require('@apollo/server');
299-
const { expressMiddleware } = require('@apollo/server/express4');
300-
const serverlessExpress = require('@vendia/serverless-express');
301-
const express = require('express');
302-
const { json } = require('body-parser');
303-
const cors = require('cors');
285+
import {
286+
startServerAndCreateLambdaHandler,
287+
middleware,
288+
handlers,
289+
} from '@as-integrations/aws-lambda';
290+
import { server } from './server';
291+
import { refreshCookie } from './cookies';
292+
293+
const requestHandler = handlers.createAPIGatewayProxyEventV2RequestHandler();
294+
295+
// Utilizing typeof
296+
const cookieMiddleware: middleware.MiddlewareFn<typeof requestHandler> = async (
297+
event,
298+
) => {
299+
// Access existing cookies and produce a refreshed one
300+
const cookie = refreshCookie(event.cookies);
301+
return async (result) => {
302+
// Assure proper initialization of the cookies property on the result
303+
result.cookies = result.cookies ?? [];
304+
// Result is mutable so it can be updated here
305+
result.cookies.push(cookie);
306+
};
307+
};
304308

305-
const server = new ApolloServer({
306-
typeDefs: 'type Query { x: ID }',
307-
resolvers: { Query: { x: () => 'hi!' } },
309+
310+
export default startServerAndCreateLambdaHandler(server, requestHandler, {
311+
middleware: [
312+
cookieMiddleware,
313+
],
308314
});
315+
```
309316

310-
server.startInBackgroundHandlingStartupErrorsByLoggingAndFailingAllRequests();
317+
More use-cases and API information can be found in the [library's README](https://github.com/apollo-server-integrations/apollo-server-integration-aws-lambda#middleware).
311318

312-
const app = express();
313-
app.use(cors(), json(), expressMiddleware(server));
314319

315-
exports.graphqlHandler = serverlessExpress({ app });
320+
## Event extensions
321+
322+
In many cases, API Gateway events will have an authorizer infront of them that contains custom state that will be used for authorization during GraphQL resolution. All of the handlers that are packged with the library contain a generic type which allows you to explicitly extend the base event type. By passing an event with authorization information, that event type will be used during the creation of `contextValue` and for `middleware`. Below is an example, and more information can be found in the [library's README](https://github.com/apollo-server-integrations/apollo-server-integration-aws-lambda/blob/main/README.md#event-extensions).
323+
324+
325+
```ts
326+
import {
327+
startServerAndCreateLambdaHandler,
328+
middleware,
329+
handlers,
330+
} from '@as-integrations/aws-lambda';
331+
import type { APIGatewayProxyEventV2WithLambdaAuthorizer } from 'aws-lambda';
332+
import { server } from './server';
333+
334+
export default startServerAndCreateLambdaHandler(
335+
server,
336+
handlers.createAPIGatewayProxyEventV2RequestHandler<
337+
APIGatewayProxyEventV2WithLambdaAuthorizer<{
338+
myAuthorizerContext: string;
339+
}>
340+
>(),
341+
);
316342
```
317343

344+
## Custom request handling
345+
346+
In order to support all event types from AWS Lambda (including custom ones), a request handler creation utility is exposed as `handlers.createHandler(eventParser, resultGenerator)`. This function returns a fully typed request handler that can be passed as the second argument to the `startServerAndCreateLambdaHandler` call. Below is an example and the exact API is documented in the [library's README](https://github.com/apollo-server-integrations/apollo-server-integration-aws-lambda/blob/main/README.md#custom-request-handlers).
347+
348+
<MultiCodeBlock>
349+
350+
```ts
351+
import {
352+
startServerAndCreateLambdaHandler,
353+
handlers,
354+
} from '@as-integrations/aws-lambda';
355+
import type { APIGatewayProxyEventV2 } from 'aws-lambda';
356+
import { HeaderMap } from '@apollo/server';
357+
import { server } from './server';
358+
359+
type CustomInvokeEvent = {
360+
httpMethod: string;
361+
queryParams: string;
362+
headers: Record<string, string>;
363+
body: string;
364+
};
365+
366+
type CustomInvokeResult =
367+
| {
368+
success: true;
369+
body: string;
370+
}
371+
| {
372+
success: false;
373+
error: string;
374+
};
375+
376+
const requestHandler = handlers.createRequestHandler<
377+
CustomInvokeEvent,
378+
CustomInvokeResult
379+
>(
380+
{
381+
parseHttpMethod(event) {
382+
return event.httpMethod;
383+
},
384+
parseHeaders(event) {
385+
const headerMap = new HeaderMap();
386+
for (const [key, value] of Object.entries(event.headers)) {
387+
headerMap.set(key, value);
388+
}
389+
return headerMap;
390+
},
391+
parseQueryParams(event) {
392+
return event.queryParams;
393+
},
394+
parseBody(event) {
395+
return event.body;
396+
},
397+
},
398+
{
399+
success({ body }) {
400+
return {
401+
success: true,
402+
body: body.string,
403+
};
404+
},
405+
error(e) {
406+
if (e instanceof Error) {
407+
return {
408+
success: false,
409+
error: e.toString(),
410+
};
411+
}
412+
console.error('Unknown error type encountered!', e);
413+
throw e;
414+
},
415+
},
416+
);
417+
418+
export default startServerAndCreateLambdaHandler(server, requestHandler);
419+
```
420+
318421
</MultiCodeBlock>
319422

320-
The setup enables you to customize your HTTP behavior as needed.
321423

322-
## Using request information
424+
425+
## Using event information
323426

324427
You can use the [`context` function](../data/context/#the-context-function) to get information about the current operation from the original Lambda data structures.
325428

@@ -394,3 +497,40 @@ exports.handler = serverlessExpress({ app });
394497
```
395498

396499
</MultiCodeBlock>
500+
501+
502+
## Customizing HTTP behavior
503+
504+
The `@as-integrations/aws-lambda` package is compatible with the following event types `APIGatewayProxyEvent`, `APIGatewayProxyEventV2`, `ALBEvent`. This supports a wide range of services like API Gateway HTTP Proxy APIs, API Gateway REST Proxy APIs, Lambda Function URLs, and Application Load Balancers. However, it does not let you customize HTTP behavior directly or support other AWS products that invoke Lambda functions (e.g., S3 or DynamoDB).
505+
506+
If you want to customize your HTTP behavior, you can couple Apollo Server's Express integration (i.e., [`expressMiddleware`](../api/express-middleware)) with the [`@vendia/serverless-express`](https://github.com/vendia/serverless-express) package. The `@vendia/serverless-express` library translates between Lambda events and Express requests. Despite their similar names, the Serverless CLI and the `@vendia/serverless-express` package are unrelated.
507+
508+
You can update your Apollo Server setup to the following to have a fully functioning Lambda server that works in a variety of AWS features:
509+
510+
<MultiCodeBlock>
511+
512+
```ts
513+
const { ApolloServer } = require('@apollo/server');
514+
const { expressMiddleware } = require('@apollo/server/express4');
515+
const serverlessExpress = require('@vendia/serverless-express');
516+
const express = require('express');
517+
const { json } = require('body-parser');
518+
const cors = require('cors');
519+
520+
const server = new ApolloServer({
521+
typeDefs: 'type Query { x: ID }',
522+
resolvers: { Query: { x: () => 'hi!' } },
523+
});
524+
525+
server.startInBackgroundHandlingStartupErrorsByLoggingAndFailingAllRequests();
526+
527+
const app = express();
528+
app.use(cors(), json(), expressMiddleware(server));
529+
530+
exports.graphqlHandler = serverlessExpress({ app });
531+
```
532+
533+
</MultiCodeBlock>
534+
535+
The setup enables you to customize your HTTP behavior as needed.
536+

0 commit comments

Comments
 (0)