注目イベント!
春の新人向け連載2025開催中!
今年も春の新人向け連載が始動しました!!
現場で役立つ考え方やTipsを丁寧に解説、今日から学びのペースを整えよう。
詳細はこちらから!
event banner

Organizing How to Use Data Source Integrations in AWS AppSync Events

| 13 min read
Author: noboru-kudo noboru-kudoの画像
Information

To reach a broader audience, this article has been translated from Japanese.
You can find the original version here.

Last year (2024), I introduced AWS AppSync’s Event API in the following article.

AppSync is famous for GraphQL, but with the introduction of the WebSocket-based Event API, real-time communication has become even simpler. About half a year has passed since then, and the Event API has continued to gain new features.

Representative updates include:

Furthermore, last month, just like with GraphQL APIs, the Event API gained the ability to integrate directly with Lambda, DynamoDB, Bedrock, and more.

According to the official documentation, the following data sources are supported at this time:

  • Lambda
  • DynamoDB
  • RDS
  • EventBridge
  • OpenSearch Service
  • HTTP endpoints
  • Bedrock

Here, we will focus on Lambda and DynamoDB and organize how to integrate these data sources with the Event API.

Prerequisites

#

First, prepare an AppSync Events environment with the minimum configuration that does not use data source integrations.

Recently, the Event API has also been supported by AWS CDK L2 constructs.

This time, instead of a CloudFormation template, we'll use CDK to build the environment. The CDK script is as follows:

import * as cdk from 'aws-cdk-lib';
import { CfnOutput } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as appsync from 'aws-cdk-lib/aws-appsync';
import { AppSyncFieldLogLevel } from 'aws-cdk-lib/aws-appsync';
import * as logs from 'aws-cdk-lib/aws-logs';

export class AppSyncStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const apiKeyProvider: appsync.AppSyncAuthProvider = {
      authorizationType: appsync.AppSyncAuthorizationType.API_KEY
    };

    const api = new appsync.EventApi(this, 'EventApi', {
      apiName: 'AppSyncEventsSampleApi',
      authorizationConfig: {
        authProviders: [
          apiKeyProvider
        ],
        connectionAuthModeTypes: [
          appsync.AppSyncAuthorizationType.API_KEY
        ],
        defaultPublishAuthModeTypes: [
          appsync.AppSyncAuthorizationType.API_KEY
        ],
        defaultSubscribeAuthModeTypes: [
          appsync.AppSyncAuthorizationType.API_KEY
        ]
      },
      logConfig: {
        retention: logs.RetentionDays.ONE_DAY,
        fieldLogLevel: AppSyncFieldLogLevel.ALL
      }
    });
    // Channel without data source integration
    api.addChannelNamespace('sample');
  }
}

This is a simple configuration that uses only API Key authentication.

When you deploy this, the AppSync management console will display as follows:

Simple - AppSync Event

Use the built-in Pub/Sub editor in the console to verify the behavior.

  1. Connect (Subscribe section)
    Simple - Connect

  2. Subscribe to channel (Subscribe section)
    Simple - Subscribe

  3. Publish event (Publish section)
    You can choose HTTP or WebSocket; either works. On success, the result appears on the right.
    Simple - Publish

  4. Verify event delivery (Subscribe section)
    If you see the event you just published on the client side, it's successful.
    Simple - Subscribe Result

You can confirm that the published event is delivered unchanged to clients subscribed to the channel.

From here on, we'll add data source integrations to this AppSync Event API.

Integrating with DynamoDB (Custom Handler)

#

To use DynamoDB as a data source, you need to provide an event handler specific to the Event API. The handler runs on the APPSYNC_JS runtime (a custom JavaScript execution environment).

Here, we will implement a handler that saves events published on a channel in a specified namespace to DynamoDB, processes the write results, and delivers them to clients.

Resource setup (CDK)

#

First, create a DynamoDB table and register it as a data source for the Event API.

// (omitted)

// DynamoDB table for data source integration
const table = new dynamodb.Table(this, 'EventTable', {
  tableName: 'AppSyncEventsTable',
  partitionKey: { name: 'id', type: dynamodb.AttributeType.STRING }
});
// Register as data source for AppSync Events
const dynamodbDataSource = api.addDynamoDbDataSource('EventDataSource', table);

Next, add the namespace (sample-dynamodb) that will use DynamoDB, and link the previously created data source and a custom handler.

// Namespace for DynamoDB
api.addChannelNamespace('sample-dynamodb', {
  // Set DynamoDB as data source
  publishHandlerConfig: {
    dataSource: dynamodbDataSource
  },
  // Custom handler
  code: appsync.Code.fromInline(`
import * as ddb from '@aws-appsync/utils/dynamodb'

// Hook executed on event publish/deliver
export const onPublish = {
  // Publish
  request(ctx) {
    const channel = ctx.info.channel.path
    return ddb.batchPut({
      tables: {
        '${table.tableName}': ctx.events.map(({ id, payload }) => ({ channel, id, ...payload })),
      }
    })
  },
  // Deliver
  response(ctx) {
    // ctx.result contains the result of the request execution
    return ctx.result.data['${table.tableName}'].map(({ id, ...payload }) => ({ id, payload: 'DynamoDB:' + payload.message }))
  }
}
// Hook executed when a client subscribes to a channel
export const onSubscribe = (ctx) => {
  console.log('Joined:' + ctx.info.channel.path)
}`)
});

The event handler registered in a namespace for AppSync Events provides two types of hooks:

Hook Timing Main Purpose
onPublish When an event is published (delivered to clients) Data source operations, event validation/transformation
onSubscribe When a client subscribes to a channel Authorization, logging, initialization

When data source integrations are enabled, each hook must be an object composed of the following functions:

  • request: Perform input validation, authorization checks, and transformation for data source calls.
  • response: Receive the data source execution results and handle errors or formatting.

In the code above, since we specify the DynamoDB data source in publishHandlerConfig, we implement request and response in the onPublish hook. The request function uses the built-in DynamoDB module provided by the AppSync runtime (APPSYNC_JS) to write to the table. The response function adds a prefix (DynamoDB:) to the write results before delivering them to clients.

Information

For simplicity, we are creating the event handler inline within AWS CDK here, but in a production environment, you probably wouldn’t do it this way. As your event handler implementation becomes more complex, you’ll want type support and linting from TypeScript (APPSYNC_JS is a fairly restrictive JavaScript runtime environment).

AppSync provides type definitions, utilities, and eslint rules as an NPM package. The official documentation below explains detailed usage steps and how to bundle with esbuild, so please refer to it:

Verifying Operation

#

Next, check that the DynamoDB integration works as expected.

After deploying the stack, open the AppSync management console and verify the configuration.

Data Sources
DynamoDB - Datasource

Namespaces
DynamoDB - Namespace

You can see the DynamoDB table added as a data source and linked to the sample-dynamodb namespace. The initial sample namespace has the handler set to None (unspecified), but sample-dynamodb is configured with AppSyncJS. Expanding the details shows the source code of the event handler you created earlier.

Next, use the Pub/Sub editor to test the actual flow (connection step omitted).

  1. Subscribe to channel (Subscribe section)
    This time, subscribe by specifying the namespace created for DynamoDB (sample-dynamodb).
    DynamoDB - Subscribe

  2. Publish event (Publish section)
    Publish an event to the channel in the same namespace.
    DynamoDB - Publish

  3. Verify event delivery (Subscribe section)
    As described in the response function of the event handler, the event is delivered with the prefix (DynamoDB:).
    DynamoDB - Subscribe Result

Finally, open the DynamoDB table and confirm that the events are stored as records.

DynamoDB - Table Editor

This confirms that the data source integration with DynamoDB is functioning correctly.

Integrating Directly with Lambda

#

Next, let’s use Lambda as a data source. Unlike in the DynamoDB case, it’s possible to directly integrate a Lambda function itself as an AppSync event handler. AppSync’s custom JavaScript runtime environment has various constraints[1], but by using a Node-based Lambda function, you can implement highly flexible code without these restrictions.

Here, we’ll try this direct integration.

Resource setup (CDK)

#

Add the following code to your CDK script:

// Lambda function for data source integration
const fn = new lambda.Function(this, 'EventHandler', {
  functionName: 'SampleAppSyncDataSourceHandler',
  runtime: lambda.Runtime.NODEJS_22_X,
  handler: 'index.handler',
  code: lambda.Code.fromInline(`
exports.handler = async (event) => {
  if (event.info.operation === 'PUBLISH') {
    // onPublish event
    return {
      events: event.events.map(e => ({
        id: e.id,
        payload: 'Lambda:' + e.payload.message // Set the delivery content here
      }))
      // On error, return:
      // error: 'error message'
    };
  } else {
    // onSubscribe event
    return null; // Subscription OK
  }
}`)
});
// Register as data source for AppSync Events
const lambdaDataSource = api.addLambdaDataSource('EventHandler', fn);
// Namespace for Lambda
api.addChannelNamespace('sample-lambda', {
  publishHandlerConfig: {
    direct: true, // Direct integration
    dataSource: lambdaDataSource
  },
  subscribeHandlerConfig: {
    direct: true, // Direct integration
    dataSource: lambdaDataSource
  }
});

First, define a Lambda function that will serve as the event handler for AppSync Events. In this direct integration method, the Lambda function itself acts as the handler and must return a response in the format required by AppSync Events.

The official documentation describes the details, but here we implement a Lambda function that supports both onPublish and onSubscribe events. The feature of direct integration is that, unlike AppSync’s custom handlers where you need to define request/response functions, you simply return the delivery content (events) as the return value. In this example, like the DynamoDB integration, we add a prefix (Lambda:) to the message before delivering the event.

After creating the Lambda function, configure the data source integration (addLambdaDataSource) and the namespace (sample-lambda). Unlike in the DynamoDB example, here we do not set AppSync’s custom handler in the namespace; instead, we set the direct parameter to true in publishHandlerConfig/subscribeHandlerConfig to enable direct integration with the Lambda function.

Information

Although we did not use it here, when implementing AppSync Events handlers in Lambda, you can achieve an intuitive implementation by using AWS Powertools provided by AWS.

Verifying Operation

#

After deploying the stack, the AppSync management console will display as follows:

Data Sources
Lambda - Datasource

Namespaces
Lambda - Namespace

You can see the Lambda function registered as a data source and linked to the sample-lambda namespace. Also, the event handler setting is Direct, indicating that direct integration with the Lambda function is enabled.

Now, let’s use the Pub/Sub editor here as well to verify the behavior.

  1. Subscribe to channel (Subscribe section)
    Subscribe to the channel by specifying the namespace created for Lambda (sample-lambda).
    Lambda - Subscribe

  2. Publish event (Publish section)
    Publish an event to the channel in the same namespace.
    Lambda - Publish

  3. Verify event delivery (Subscribe section)
    You can confirm that the event is delivered with the prefix (Lambda:) set by the Lambda function.
    Lambda - Subscribe Result

This confirms that direct integration with Lambda processes and delivers events as expected.

Conclusion

#

In this article, we covered the newly added data source integration feature in AppSync Events by walking through basic usage examples for DynamoDB and Lambda. This feature addition makes it extremely easy to integrate with various AWS services and external resources, greatly enhancing the practical utility of AppSync Events. The usage is intuitive, and setting up the environment with CDK is relatively straightforward.

Moving forward, I plan to explore other data source integrations like RDS and Bedrock, and uncover new possibilities for real-time applications.


  1. https://docs.aws.amazon.com/appsync/latest/eventapi/runtime-supported-features.html ↩︎

豆蔵では共に高め合う仲間を募集しています!

recruit

具体的な採用情報はこちらからご覧いただけます。