Skip to content

Commit 7c3196f

Browse files
dreamorosisvozza
andauthored
docs(event-handler): add AppSync GraphQL docs page (#4120)
Co-authored-by: Stefano Vozza <[email protected]>
1 parent 7e74c9e commit 7c3196f

23 files changed

+882
-7
lines changed

docs/features/event-handler/appsync-events.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
---
22
title: AppSync Events
33
description: Event Handler for AWS AppSync real-time events
4-
status: new
54
---
65

76
Event Handler for AWS AppSync real-time events.
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
---
2+
title: AppSync GraphQL
3+
description: Event Handler for AppSync GraphQL APIs
4+
status: new
5+
---
6+
7+
Event Handler for AWS AppSync GraphQL APIs simplifies routing and processing of events in AWS Lambda functions. It allows you to define resolvers for GraphQL types and fields, making it easier to handle GraphQL requests without the need for complex VTL or JavaScript templates.
8+
9+
```mermaid
10+
--8<-- "examples/snippets/event-handler/appsync-graphql/diagrams/intro.mermaid"
11+
```
12+
13+
## Key Features
14+
15+
- Route events based on GraphQL type and field keys
16+
- Automatically parse API arguments to function parameters
17+
- Handle GraphQL responses and errors in the expected format
18+
19+
## Terminology
20+
21+
**[Direct Lambda Resolver](https://docs.aws.amazon.com/appsync/latest/devguide/direct-lambda-reference.html){target="_blank"}**. A custom AppSync Resolver that bypasses Apache Velocity Template (VTL) and JavaScript templates, and automatically maps your function's response to a GraphQL field.
22+
23+
**[Batching resolvers](https://docs.aws.amazon.com/appsync/latest/devguide/tutorial-lambda-resolvers.html#advanced-use-case-batching){target="_blank"}**. A technique that allows you to batch multiple GraphQL requests into a single Lambda function invocation, reducing the number of calls and improving performance.
24+
25+
## Getting started
26+
27+
???+ tip "Tip: Designing GraphQL Schemas for the first time?"
28+
Visit [AWS AppSync schema documentation](https://docs.aws.amazon.com/appsync/latest/devguide/designing-your-schema.html){target="_blank"} to understand how to define types, nesting, and pagination.
29+
30+
### Required resources
31+
32+
You must have an existing AppSync GraphQL API and IAM permissions to invoke your Lambda function. That said, there is no additional permissions to use Event Handler as routing requires no dependency (_standard library_).
33+
34+
This is the sample infrastructure we will be using for the initial examples with an AppSync Direct Lambda Resolver.
35+
36+
=== "gettingStartedSchema.graphql"
37+
38+
```typescript
39+
--8<-- "examples/snippets/event-handler/appsync-graphql/templates/gettingStartedSchema.graphql"
40+
```
41+
42+
=== "template.yaml"
43+
44+
```yaml hl_lines="59-60 71-72 94-95 104-105 112-113"
45+
--8<-- "examples/snippets/event-handler/appsync-graphql/templates/gettingStartedSam.yaml"
46+
```
47+
48+
### Registering a resolver
49+
50+
You can register functions to match GraphQL types and fields with one of three methods:
51+
52+
- `onQuery()` - Register a function to handle a GraphQL Query type.
53+
- `onMutation()` - Register a function to handle a GraphQL Mutation type.
54+
- `resolver()` - Register a function to handle a GraphQL type and field.
55+
56+
!!! question "What is a type and field?"
57+
A type would be a top-level **GraphQL Type** like `Query`, `Mutation`, `Todo`. A **GraphQL Field** would be `listTodos` under `Query`, `createTodo` under `Mutation`, etc.
58+
59+
The function receives the parsed arguments from the GraphQL request as its first parameter. We also take care of parsing the response or catching errors and returning them in the expected format.
60+
61+
#### Query resolver
62+
63+
When registering a resolver for a `Query` type, you can use the `onQuery()` method. This method allows you to define a function that will be invoked when a GraphQL Query is made.
64+
65+
```typescript hl_lines="2 8 10 21" title="Registering a resolver for a Query type"
66+
--8<-- "examples/snippets/event-handler/appsync-graphql/gettingStartedOnQuery.ts"
67+
```
68+
69+
#### Mutation resolver
70+
71+
Similarly, you can register a resolver for a `Mutation` type using the `onMutation()` method. This method allows you to define a function that will be invoked when a GraphQL Mutation is made.
72+
73+
```typescript hl_lines="2-5 11 13 25" title="Registering a resolver for a Mutation type"
74+
--8<-- "examples/snippets/event-handler/appsync-graphql/gettingStartedOnMutation.ts"
75+
```
76+
77+
#### Generic resolver
78+
79+
When you want to have more control over the type and field, you can use the `resolver()` method. This method allows you to register a function for a specific GraphQL type and field including custom types.
80+
81+
```typescript hl_lines="2 8 10 27-30" title="Registering a resolver for a type and field"
82+
--8<-- "examples/snippets/event-handler/appsync-graphql/gettingStartedResolver.ts"
83+
```
84+
85+
#### Using decorators
86+
87+
If you prefer to use the decorator syntax, you can instead use the same methods on a class method to register your handlers. Learn more about how Powertools for TypeScript supports [decorators](../../getting-started/usage-patterns.md).
88+
89+
```typescript hl_lines="3-6 12 15 27 38 60" title="Using decorators to register a resolver"
90+
--8<-- "examples/snippets/event-handler/appsync-graphql/gettingStartedDecorators.ts"
91+
```
92+
93+
1. It's recommended to pass a refernce of `this` to ensure the correct class scope is propageted to the route handler functions.
94+
95+
### Scalar functions
96+
97+
When working with [AWS AppSync Scalar types](https://docs.aws.amazon.com/appsync/latest/devguide/scalars.html){target="_blank"}, you might want to generate the same values for data validation purposes.
98+
99+
For convenience, the most commonly used values are available as helper functions within the module.
100+
101+
```typescript hl_lines="2-6" title="Creating key scalar values"
102+
--8<-- "examples/snippets/event-handler/appsync-graphql/gettingStartedScalarFunctions.ts"
103+
```
104+
105+
Here's a table with their related scalar as a quick reference:
106+
107+
| Scalar type | Scalar function | Sample value |
108+
| ---------------- | --------------- | -------------------------------------- |
109+
| **ID** | `makeId` | `e916c84d-48b6-484c-bef3-cee3e4d86ebf` |
110+
| **AWSDate** | `awsDate` | `2022-07-08Z` |
111+
| **AWSTime** | `awsTime` | `15:11:00.189Z` |
112+
| **AWSDateTime** | `awsDateTime` | `2022-07-08T15:11:00.189Z` |
113+
| **AWSTimestamp** | `awsTimestamp` | `1657293060` |
114+
115+
## Advanced
116+
117+
### Nested mappings
118+
119+
!!! note
120+
121+
The following examples use a more advanced schema. These schemas differ from the [initial sample infrastructure we used earlier](#required-resources).
122+
123+
You can register the same route handler multiple times to resolve fields with the same return value.
124+
125+
=== "Nested Mappings Example"
126+
127+
```typescript hl_lines="8 33-39"
128+
--8<-- "examples/snippets/event-handler/appsync-graphql/advancedNestedMappings.ts"
129+
```
130+
131+
1. If omitted, the `typeName` defaults to `Query`.
132+
133+
=== "Nested Mappings Schema"
134+
135+
```graphql hl_lines="6 20"
136+
--8<-- "examples/snippets/event-handler/appsync-graphql/templates/advancedNestedMappingsSchema.graphql"
137+
```
138+
139+
### Accessing Lambda context and event
140+
141+
You can access the original Lambda event or context for additional information. These are passed to the handler function as optional arguments.
142+
143+
=== "Access event and context"
144+
145+
```typescript hl_lines="10"
146+
--8<-- "examples/snippets/event-handler/appsync-graphql/advancedAccessEventAndContext.ts"
147+
```
148+
149+
1. The `event` parameter contains the original AppSync event and has type `AppSyncResolverEvent` from the `@types/aws-lambda`.
150+
151+
### Logging
152+
153+
By default, the utility uses the global `console` logger and emits only warnings and errors.
154+
155+
You can change this behavior by passing a custom logger instance to the `AppSyncGraphQLResolver` or `Router` and setting the log level for it, or by enabling [Lambda Advanced Logging Controls](https://docs.aws.amazon.com/lambda/latest/dg/monitoring-cloudwatchlogs-advanced.html) and setting the log level to `DEBUG`.
156+
157+
When debug logging is enabled, the resolver will emit logs that show the underlying handler resolution process. This is useful for understanding how your handlers are being resolved and invoked and can help you troubleshoot issues with your event processing.
158+
159+
For example, when using the [Powertools for AWS Lambda logger](../logger.md), you can set the `LOG_LEVEL` to `DEBUG` in your environment variables or at the logger level and pass the logger instance to the constructor to enable debug logging.
160+
161+
=== "Debug logging"
162+
163+
```typescript hl_lines="11"
164+
--8<-- "examples/snippets/event-handler/appsync-graphql/advancedDebugLogging.ts"
165+
```
166+
167+
=== "Logs output"
168+
169+
```json
170+
--8<-- "examples/snippets/event-handler/appsync-graphql/samples/debugLogExcerpt.json"
171+
```
172+
173+
## Testing your code
174+
175+
You can test your resolvers by passing an event with the shape expected by the AppSync GraphQL API resolver.
176+
177+
Here's an example of how you can test your resolvers that uses a factory function to create the event shape:
178+
179+
```typescript
180+
--8<-- "examples/snippets/event-handler/appsync-graphql/advancedTestYourCode.ts"
181+
```

docs/features/event-handler/index.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
---
2+
title: Event Handler
3+
description: Simplify routing and processing of events in AWS Lambda functions
4+
---
5+
6+
<!-- markdownlint-disable MD043 -->
7+
8+
<div class="grid cards" markdown>
9+
10+
- __AppSync Events API__
11+
12+
---
13+
14+
Event Handler for AWS AppSync real-time events, easily handle publish and subscribe events with dedicated handler methods.
15+
16+
[:octicons-arrow-right-24: Read more](./appsync-events.md)
17+
18+
- __AppSync GraphQL API__
19+
20+
---
21+
22+
Event Handler for AWS AppSync GraphQL APIs, it allows you to define resolvers for GraphQL types and fields, making it easier to handle requests without the need for complex VTL or JavaScript templates.
23+
24+
[:octicons-arrow-right-24: Read more](./appsync-graphql.md)
25+
26+
- __Bedrock Agents__
27+
28+
---
29+
30+
Create [Amazon Bedrock Agents](https://docs.aws.amazon.com/bedrock/latest/userguide/agents.html#agents-how) and focus on building your agent's logic without worrying about parsing and routing requests.
31+
32+
[:octicons-arrow-right-24: Read more](./bedrock-agents.md)
33+
34+
</div>

docs/features/index.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,11 @@ description: Features of Powertools for AWS Lambda
3131

3232
[:octicons-arrow-right-24: Read more](./metrics.md)
3333

34-
- __Event Handler - AppSync Events__
34+
- __Event Handler__
3535

3636
---
3737

38-
Event Handler for AWS AppSync real-time events
38+
Event Handler for AWS AppSync real-time events, AppSync GraphQL APIs, Bedrock Agents, and more. It simplifies routing and processing of events in AWS Lambda functions.
3939

4040
[:octicons-arrow-right-24: Read more](./event-handler/appsync-events.md)
4141

docs/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ Powertools for AWS Lambda (TypeScript) is built as a modular toolkit, so you can
4747
| [Tracer](./features/tracer.md) | Decorators and utilities to trace Lambda function handlers, and both synchronous and asynchronous functions |
4848
| [Logger](./features/logger.md) | Structured logging made easier, and a middleware to enrich structured logging with key Lambda context details |
4949
| [Metrics](./features/metrics.md) | Custom Metrics created asynchronously via CloudWatch Embedded Metric Format (EMF) |
50-
| [Event Handler - AppSync Events](./features/event-handler/appsync-events.md) | Event Handler for AWS AppSync real-time events |
50+
| [Event Handler](./features/event-handler/appsync-events.md) | Event Handler for AWS AppSync real-time events, AppSync GraphQL APIs, Bedrock Agents, and more. |
5151
| [Parameters](./features/parameters.md) | High-level functions to retrieve one or more parameters from AWS SSM Parameter Store, AWS Secrets Manager, AWS AppConfig, and Amazon DynamoDB |
5252
| [Idempotency](./features/idempotency.md) | Class method decorator, Middy middleware, and function wrapper to make your Lambda functions idempotent and prevent duplicate execution based on payload content. |
5353
| [Batch Processing](./features/batch.md) | Utility to handle partial failures when processing batches from Amazon SQS, Amazon Kinesis Data Streams, and Amazon DynamoDB Streams. |

examples/snippets/event-handler/appsync-events/gettingStartedOnPublishDecorator.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ class Lambda {
1414
}
1515

1616
async handler(event: unknown, context: Context) {
17-
return app.resolve(event, context);
17+
return app.resolve(event, context, { scope: this });
1818
}
1919
}
2020

examples/snippets/event-handler/appsync-events/gettingStartedOnSubscribeDecorator.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ class Lambda {
1818
}
1919

2020
async handler(event: unknown, context: Context) {
21-
return app.resolve(event, context);
21+
return app.resolve(event, context, { scope: this });
2222
}
2323
}
2424

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { AppSyncGraphQLResolver } from '@aws-lambda-powertools/event-handler/appsync-graphql';
2+
import { Logger } from '@aws-lambda-powertools/logger';
3+
import type { Context } from 'aws-lambda';
4+
5+
const logger = new Logger({
6+
serviceName: 'TodoManager',
7+
});
8+
const app = new AppSyncGraphQLResolver({ logger });
9+
10+
app.onQuery<{ id: string }>('getTodo', async ({ id }, { event, context }) => {
11+
const { headers } = event.request; // (1)!
12+
const { awsRequestId } = context;
13+
logger.info('headers', { headers, awsRequestId });
14+
15+
return {
16+
id,
17+
title: 'Todo Title',
18+
completed: false,
19+
};
20+
});
21+
22+
export const handler = async (event: unknown, context: Context) =>
23+
app.resolve(event, context);
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { AppSyncGraphQLResolver } from '@aws-lambda-powertools/event-handler/appsync-graphql';
2+
import { Logger } from '@aws-lambda-powertools/logger';
3+
import {
4+
correlationPaths,
5+
search,
6+
} from '@aws-lambda-powertools/logger/correlationId';
7+
import type { Context } from 'aws-lambda';
8+
9+
const logger = new Logger({
10+
serviceName: 'TodoManager',
11+
logLevel: 'DEBUG',
12+
correlationIdSearchFn: search,
13+
});
14+
const app = new AppSyncGraphQLResolver({ logger });
15+
16+
app.onQuery<{ id: string }>('getTodo', async ({ id }) => {
17+
logger.debug('Resolving todo', { id });
18+
// Simulate fetching a todo from a database or external service
19+
return {
20+
id,
21+
title: 'Todo Title',
22+
completed: false,
23+
};
24+
});
25+
26+
export const handler = async (event: unknown, context: Context) => {
27+
logger.setCorrelationId(event, correlationPaths.APPSYNC_RESOLVER);
28+
return app.resolve(event, context);
29+
};
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { AppSyncGraphQLResolver } from '@aws-lambda-powertools/event-handler/appsync-graphql';
2+
import { Logger } from '@aws-lambda-powertools/logger';
3+
import type { Context } from 'aws-lambda';
4+
5+
const logger = new Logger({
6+
serviceName: 'TodoManager',
7+
});
8+
const app = new AppSyncGraphQLResolver({ logger });
9+
10+
type Location = {
11+
id: string;
12+
name: string;
13+
description?: string;
14+
};
15+
16+
const locationsResolver = async (): Promise<Location[]> => {
17+
logger.debug('Resolving locations');
18+
// Simulate fetching locations from a database or external service
19+
return [
20+
{
21+
id: 'loc1',
22+
name: 'Location One',
23+
description: 'First location description',
24+
},
25+
{
26+
id: 'loc2',
27+
name: 'Location Two',
28+
description: 'Second location description',
29+
},
30+
];
31+
};
32+
33+
app.resolver(locationsResolver, {
34+
fieldName: 'locations',
35+
typeName: 'Merchant',
36+
});
37+
app.resolver(locationsResolver, {
38+
fieldName: 'listLocations', // (1)!
39+
});
40+
41+
export const handler = async (event: unknown, context: Context) =>
42+
app.resolve(event, context);

0 commit comments

Comments
 (0)