Skip to content

docs(event-handler): add AppSync GraphQL docs page #4120

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

Merged
merged 5 commits into from
Jul 2, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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: 0 additions & 1 deletion docs/features/event-handler/appsync-events.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
---
title: AppSync Events
description: Event Handler for AWS AppSync real-time events
status: new
---

Event Handler for AWS AppSync real-time events.
Expand Down
181 changes: 181 additions & 0 deletions docs/features/event-handler/appsync-graphql.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
---
title: AppSync GraphQL
description: Event Handler for AppSync GraphQL APIs
status: new
---

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.

```mermaid
--8<-- "examples/snippets/event-handler/appsync-graphql/diagrams/intro.mermaid"
```

## Key Features

- Route events based on GraphQL type and field keys
- Automatically parse API arguments to function parameters
- Handle GraphQL responses and errors in the expected format

## Terminology

**[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.

**[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.

## Getting started

???+ tip "Tip: Designing GraphQL Schemas for the first time?"
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.

### Required resources

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_).

This is the sample infrastructure we will be using for the initial examples with an AppSync Direct Lambda Resolver.

=== "gettingStartedSchema.graphql"

```typescript
--8<-- "examples/snippets/event-handler/appsync-graphql/templates/gettingStartedSchema.graphql"
```

=== "template.yaml"

```yaml hl_lines="59-60 71-72 94-95 104-105 112-113"
--8<-- "examples/snippets/event-handler/appsync-graphql/templates/gettingStartedSam.yaml"
```

### Registering a resolver

You can register functions to match GraphQL types and fields with one of three methods:

- `onQuery()` - Register a function to handle a GraphQL Query type.
- `onMutation()` - Register a function to handle a GraphQL Mutation type.
- `resolver()` - Register a function to handle a GraphQL type and field.

!!! question "What is a type and field?"
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.

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.

#### Query resolver

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.

```typescript hl_lines="2 8 10 21" title="Registering a resolver for a Query type"
--8<-- "examples/snippets/event-handler/appsync-graphql/gettingStartedOnQuery.ts"
```

#### Mutation resolver

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.

```typescript hl_lines="2-5 11 13 25" title="Registering a resolver for a Mutation type"
--8<-- "examples/snippets/event-handler/appsync-graphql/gettingStartedOnMutation.ts"
```

#### Generic resolver

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.

```typescript hl_lines="2 8 10 27-30" title="Registering a resolver for a type and field"
--8<-- "examples/snippets/event-handler/appsync-graphql/gettingStartedResolver.ts"
```

#### Using decorators

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).

```typescript hl_lines="3-6 12 15 27 38 60" title="Using decorators to register a resolver"
--8<-- "examples/snippets/event-handler/appsync-graphql/gettingStartedDecorators.ts"
```

1. It's recommended to pass a refernce of `this` to ensure the correct class scope is propageted to the route handler functions.

### Scalar functions

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.

For convenience, the most commonly used values are available as helper functions within the module.

```typescript hl_lines="2-6" title="Creating key scalar values"
--8<-- "examples/snippets/event-handler/appsync-graphql/gettingStartedScalarFunctions.ts"
```

Here's a table with their related scalar as a quick reference:

| Scalar type | Scalar function | Sample value |
| ---------------- | --------------- | -------------------------------------- |
| **ID** | `makeId` | `e916c84d-48b6-484c-bef3-cee3e4d86ebf` |
| **AWSDate** | `awsDate` | `2022-07-08Z` |
| **AWSTime** | `awsTime` | `15:11:00.189Z` |
| **AWSDateTime** | `awsDateTime` | `2022-07-08T15:11:00.189Z` |
| **AWSTimestamp** | `awsTimestamp` | `1657293060` |

## Advanced

### Nested mappings

!!! note

The following examples use a more advanced schema. These schemas differ from the [initial sample infrastructure we used earlier](#required-resources).

You can register the same route handler multiple times to resolve fields with the same return value.

=== "Nested Mappings Example"

```typescript hl_lines="8 33-39"
--8<-- "examples/snippets/event-handler/appsync-graphql/advancedNestedMappings.ts"
```

1. If omitted, the `typeName` defaults to `Query`.

=== "Nested Mappings Schema"

```graphql hl_lines="6 20"
--8<-- "examples/snippets/event-handler/appsync-graphql/templates/advancedNestedMappingsSchema.graphql"
```

### Accessing Lambda context and event

You can access the original Lambda event or context for additional information. These are passed to the handler function as optional arguments.

=== "Access event and context"

```typescript hl_lines="10"
--8<-- "examples/snippets/event-handler/appsync-graphql/advancedAccessEventAndContext.ts"
```

1. The `event` parameter contains the original AppSync event and has type `AppSyncResolverEvent` from the `@types/aws-lambda`.

### Logging

By default, the utility uses the global `console` logger and emits only warnings and errors.

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`.

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.

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.

=== "Debug logging"

```typescript hl_lines="11"
--8<-- "examples/snippets/event-handler/appsync-graphql/advancedDebugLogging.ts"
```

=== "Logs output"

```json
--8<-- "examples/snippets/event-handler/appsync-graphql/samples/debugLogExcerpt.json"
```

## Testing your code

You can test your resolvers by passing an event with the shape expected by the AppSync GraphQL API resolver.

Here's an example of how you can test your resolvers that uses a factory function to create the event shape:

```typescript
--8<-- "examples/snippets/event-handler/appsync-graphql/advancedTestYourCode.ts"
```
34 changes: 34 additions & 0 deletions docs/features/event-handler/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
---
title: Event Handler
description: Simplify routing and processing of events in AWS Lambda functions
---

<!-- markdownlint-disable MD043 -->

<div class="grid cards" markdown>

- __AppSync Events API__

---

Event Handler for AWS AppSync real-time events, easily handle publish and subscribe events with dedicated handler methods.

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

- __AppSync GraphQL API__

---

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.

[:octicons-arrow-right-24: Read more](./appsync-graphql.md)

- __Bedrock Agents__

---

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.

[:octicons-arrow-right-24: Read more](./bedrock-agents.md)

</div>
4 changes: 2 additions & 2 deletions docs/features/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,11 @@ description: Features of Powertools for AWS Lambda

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

- __Event Handler - AppSync Events__
- __Event Handler__

---

Event Handler for AWS AppSync real-time events
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.

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

Expand Down
2 changes: 1 addition & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ Powertools for AWS Lambda (TypeScript) is built as a modular toolkit, so you can
| [Tracer](./features/tracer.md) | Decorators and utilities to trace Lambda function handlers, and both synchronous and asynchronous functions |
| [Logger](./features/logger.md) | Structured logging made easier, and a middleware to enrich structured logging with key Lambda context details |
| [Metrics](./features/metrics.md) | Custom Metrics created asynchronously via CloudWatch Embedded Metric Format (EMF) |
| [Event Handler - AppSync Events](./features/event-handler/appsync-events.md) | Event Handler for AWS AppSync real-time events |
| [Event Handler](./features/event-handler/appsync-events.md) | Event Handler for AWS AppSync real-time events, AppSync GraphQL APIs, Bedrock Agents, and more. |
| [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 |
| [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. |
| [Batch Processing](./features/batch.md) | Utility to handle partial failures when processing batches from Amazon SQS, Amazon Kinesis Data Streams, and Amazon DynamoDB Streams. |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class Lambda {
}

async handler(event: unknown, context: Context) {
return app.resolve(event, context);
return app.resolve(event, context, { scope: this });
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class Lambda {
}

async handler(event: unknown, context: Context) {
return app.resolve(event, context);
return app.resolve(event, context, { scope: this });
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { AppSyncGraphQLResolver } from '@aws-lambda-powertools/event-handler/appsync-graphql';
import { Logger } from '@aws-lambda-powertools/logger';
import type { Context } from 'aws-lambda';

const logger = new Logger({
serviceName: 'TodoManager',
});
const app = new AppSyncGraphQLResolver({ logger });

app.onQuery<{ id: string }>('getTodo', async ({ id }, { event, context }) => {
const { headers } = event.request; // (1)!
const { awsRequestId } = context;
logger.info('headers', { headers, awsRequestId });

return {
id,
title: 'Todo Title',
completed: false,
};
});

export const handler = async (event: unknown, context: Context) =>
app.resolve(event, context);
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { AppSyncGraphQLResolver } from '@aws-lambda-powertools/event-handler/appsync-graphql';
import { Logger } from '@aws-lambda-powertools/logger';
import {
correlationPaths,
search,
} from '@aws-lambda-powertools/logger/correlationId';
import type { Context } from 'aws-lambda';

const logger = new Logger({
serviceName: 'TodoManager',
logLevel: 'DEBUG',
correlationIdSearchFn: search,
});
const app = new AppSyncGraphQLResolver({ logger });

app.onQuery<{ id: string }>('getTodo', async ({ id }) => {
logger.debug('Resolving todo', { id });
// Simulate fetching a todo from a database or external service
return {
id,
title: 'Todo Title',
completed: false,
};
});

export const handler = async (event: unknown, context: Context) => {
logger.setCorrelationId(event, correlationPaths.APPSYNC_RESOLVER);
return app.resolve(event, context);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { AppSyncGraphQLResolver } from '@aws-lambda-powertools/event-handler/appsync-graphql';
import { Logger } from '@aws-lambda-powertools/logger';
import type { Context } from 'aws-lambda';

const logger = new Logger({
serviceName: 'TodoManager',
});
const app = new AppSyncGraphQLResolver({ logger });

type Location = {
id: string;
name: string;
description?: string;
};

const locationsResolver = async (): Promise<Location[]> => {
logger.debug('Resolving locations');
// Simulate fetching locations from a database or external service
return [
{
id: 'loc1',
name: 'Location One',
description: 'First location description',
},
{
id: 'loc2',
name: 'Location Two',
description: 'Second location description',
},
];
};

app.resolver(locationsResolver, {
fieldName: 'locations',
typeName: 'Merchant',
});
app.resolver(locationsResolver, {
fieldName: 'listLocations', // (1)!
});

export const handler = async (event: unknown, context: Context) =>
app.resolve(event, context);
Loading