A RetroSearch Logo

Home - News ( United States | United Kingdom | Italy | Germany ) - Football scores

Search Query:

Showing content from https://docs.powertools.aws.dev/lambda/typescript/latest/features/event-handler/appsync-events/ below:

AppSync Events - Powertools for AWS Lambda (TypeScript)

AppSync Events

Event Handler for AWS AppSync real-time events.

stateDiagram-v2
    direction LR
    EventSource: AppSync Events
    EventHandlerResolvers: Publish & Subscribe events
    LambdaInit: Lambda invocation
    EventHandler: Event Handler
    EventHandlerResolver: Route event based on namespace/channel
    YourLogic: Run your registered handler function
    EventHandlerResolverBuilder: Adapts response to AppSync contract
    LambdaResponse: Lambda response

    state EventSource {
        EventHandlerResolvers
    }

    EventHandlerResolvers --> LambdaInit

    LambdaInit --> EventHandler
    EventHandler --> EventHandlerResolver

    state EventHandler {
        [*] --> EventHandlerResolver: app.resolve(event, context)
        EventHandlerResolver --> YourLogic
        YourLogic --> EventHandlerResolverBuilder
    }

    EventHandler --> LambdaResponse
Key Features Terminology

AWS AppSync Events. A service that enables you to quickly build secure, scalable real-time WebSocket APIs without managing infrastructure or writing API code.

It handles connection management, message broadcasting, authentication, and monitoring, reducing time to market and operational costs.

Getting started Tip: New to AppSync Real-time API?

Visit AWS AppSync Real-time documentation to understand how to set up subscriptions and pub/sub messaging.

Required resources

You must have an existing AppSync Events API with real-time capabilities enabled and IAM permissions to invoke your AWS Lambda function. That said, there are no additional permissions required to use Event Handler as routing requires no dependency.

Additionally, if you want the result of your handler to be used by AppSync you must set the integration type to REQUEST_RESPONSE.

template.yaml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31

Globals:
  Function:
    Timeout: 5
    MemorySize: 256
    Runtime: nodejs22.x
    Tracing: Active
    Environment:
      Variables:
        POWERTOOLS_LOG_LEVEL: INFO
        POWERTOOLS_SERVICE_NAME: hello

Resources:
  HelloWorldFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: index.handler
      CodeUri: hello_world

  WebsocketAPI:
    Type: AWS::AppSync::Api
    Properties:
      EventConfig:
        AuthProviders:
          - AuthType: API_KEY
        ConnectionAuthModes:
          - AuthType: API_KEY
        DefaultPublishAuthModes:
          - AuthType: API_KEY
        DefaultSubscribeAuthModes:
          - AuthType: API_KEY
      Name: RealTimeEventAPI

  WebsocketApiKey:
    Type: AWS::AppSync::ApiKey
    Properties:
      ApiId: !GetAtt WebsocketAPI.ApiId

  WebsocketAPINamespace:
    Type: AWS::AppSync::ChannelNamespace
    Properties:
      ApiId: !GetAtt WebsocketAPI.ApiId
      Name: powertools
      HandlerConfigs:
        OnPublish:
          Behavior: DIRECT
          Integration:
            DataSourceName: powertools_lambda
            LambdaConfig:
              InvokeType: REQUEST_RESPONSE
        OnSubscribe:
          Behavior: DIRECT
          Integration:
            DataSourceName: powertools_lambda
            LambdaConfig:
              InvokeType: REQUEST_RESPONSE

  DataSourceIAMRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: appsync.amazonaws.com
            Action: sts:AssumeRole
      Policies:
        - PolicyName: LambdaInvokePolicy
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - lambda:InvokeFunction
                Resource: !GetAtt HelloWorldFunction.Arn

  NameSpaceDataSource:
    Type: AWS::AppSync::DataSource
    Properties:
      ApiId: !GetAtt WebsocketAPI.ApiId
      LambdaConfig: 
        LambdaFunctionArn: !GetAtt HelloWorldFunction.Arn
      Name: powertools_lambda
      ServiceRoleArn: !GetAtt DataSourceIAMRole.Arn
      Type: AWS_LAMBDA
AppSync request and response format

AppSync Events uses a specific event format for Lambda requests and responses. In most scenarios, Powertools for AWS simplifies this interaction by automatically formatting resolver returns to match the expected AppSync response structure.

Request formatResponse formatResponse format with error

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
{
  "identity": "None",
  "result": "None",
  "request": {
    "headers": {
      "x-forwarded-for": "1.1.1.1, 2.2.2.2",
      "cloudfront-viewer-country": "US"
    },
    "domainName": "None"
  },
  "info": {
    "channel": {
      "path": "/default/channel",
      "segments": ["default", "channel"]
    },
    "channelNamespace": {
      "name": "default"
    },
    "operation": "PUBLISH"
  },
  "error": "None",
  "prev": "None",
  "stash": {},
  "outErrors": [],
  "events": [
    {
      "payload": {
        "data": "data_1"
      },
      "id": "1"
    },
    {
      "payload": {
        "data": "data_2"
      },
      "id": "2"
    }
  ]
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
{
  "events": [
    {
      "payload": {
        "data": "data_1"
      },
      "id": "1"
    },
    {
      "payload": {
        "data": "data_2"
      },
      "id": "2"
    }
  ]
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
{
  "events": [
    {
      "error": "Error message",
      "id": "1"
    },
    {
      "payload": {
        "data": "data_2"
      },
      "id": "2"
    }
  ]
}
Events response with error

When processing events with Lambda, you can return errors to AppSync in three ways:

Route handlers

The event handler automatically parses the incoming event data and invokes the appropriate handler based on the namespace/channel pattern you register.

You can define your handlers for different event types using the onPublish() and onSubscribe() methods and pass a function to handle the event.

Publish eventsSubscribe events

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
import { AppSyncEventsResolver } from '@aws-lambda-powertools/event-handler/appsync-events';
import type { Context } from 'aws-lambda';

const app = new AppSyncEventsResolver();

app.onPublish('/default/foo', (payload) => {
  return {
    processed: true,
    original_payload: payload,
  };
});

export const handler = async (event: unknown, context: Context) =>
  app.resolve(event, context);
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
import { AppSyncEventsResolver } from '@aws-lambda-powertools/event-handler/appsync-events';
import { Metrics, MetricUnit } from '@aws-lambda-powertools/metrics';
import type { Context } from 'aws-lambda';

const metrics = new Metrics({
  namespace: 'serverlessAirline',
  serviceName: 'chat',
  singleMetric: true,
});
const app = new AppSyncEventsResolver();

app.onSubscribe('/default/foo', (event) => {
  metrics.addDimension('channel', event.info.channel.path);
  metrics.addMetric('connections', MetricUnit.Count, 1);
});

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

If you prefer to use the decorator syntax, you can instead use the same methods on a class method to register your handlers.

Publish eventsSubscribe events

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
import { AppSyncEventsResolver } from '@aws-lambda-powertools/event-handler/appsync-events';
import type { AppSyncEventsPublishEvent } from '@aws-lambda-powertools/event-handler/types';
import type { Context } from 'aws-lambda';

const app = new AppSyncEventsResolver();

class Lambda {
  @app.onPublish('/default/foo')
  async fooHandler(payload: AppSyncEventsPublishEvent) {
    return {
      processed: true,
      original_payload: payload,
    };
  }

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

const lambda = new Lambda();
export const handler = lambda.handler.bind(lambda);
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import { AppSyncEventsResolver } from '@aws-lambda-powertools/event-handler/appsync-events';
import type { AppSyncEventsSubscribeEvent } from '@aws-lambda-powertools/event-handler/types';
import { Metrics, MetricUnit } from '@aws-lambda-powertools/metrics';
import type { Context } from 'aws-lambda';

const metrics = new Metrics({
  namespace: 'serverlessAirline',
  serviceName: 'chat',
  singleMetric: true,
});
const app = new AppSyncEventsResolver();

class Lambda {
  @app.onSubscribe('/default/foo')
  async fooHandler(event: AppSyncEventsSubscribeEvent) {
    metrics.addDimension('channel', event.info.channel.path);
    metrics.addMetric('connections', MetricUnit.Count, 1);
  }

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

const lambda = new Lambda();
export const handler = lambda.handler.bind(lambda);
Advanced Wildcard patterns and handler precedence

You can use wildcard patterns to create catch-all handlers for multiple channels or namespaces. This is particularly useful for centralizing logic that applies to multiple channels.

When an event matches with multiple handler, the most specific pattern takes precedence.

Supported wildcard patterns

Only the following patterns are supported:

Patterns like /namespace/channel* or /namespace/*/subpath are not supported.

More specific handlers will always take precedence over less specific ones. For example, /default/channel1 will take precedence over /default/*, which will take precedence over /*.

Wildcard patterns

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
import { AppSyncEventsResolver } from '@aws-lambda-powertools/event-handler/appsync-events';
import type { Context } from 'aws-lambda';

const app = new AppSyncEventsResolver();

app.onPublish('/default/*', (_payload) => {
  // your logic here
});

app.onSubscribe('/*', (_payload) => {
  // your logic here
});

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

If the event doesn't match any registered handler, the Event Handler will log a warning and skip processing the event.

Aggregated processing

In some scenarios, you might want to process all messages published to a channel as a batch rather than individually.

This is useful when you want to for example:

You can enable this with the aggregate parameter:

Aggregated processing

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import { AppSyncEventsResolver } from '@aws-lambda-powertools/event-handler/appsync-events';
import {
  BatchWriteItemCommand,
  DynamoDBClient,
  type WriteRequest,
} from '@aws-sdk/client-dynamodb';
import { marshall } from '@aws-sdk/util-dynamodb';
import type { Context } from 'aws-lambda';

const ddbClient = new DynamoDBClient();
const app = new AppSyncEventsResolver();

app.onPublish(
  '/default/foo/*',
  async (payloads) => {
    const writeOperations: WriteRequest[] = [];
    for (const payload of payloads) {
      writeOperations.push({
        PutRequest: {
          Item: marshall(payload),
        },
      });
    }
    await ddbClient.send(
      new BatchWriteItemCommand({
        RequestItems: {
          'your-table-name': writeOperations,
        },
      })
    );

    return payloads;
  },
  { aggregate: true }
);

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

When enabling aggregate, your handler receives a list of all the events, requiring you to manage the response format. Ensure your response includes results for each event in the expected AppSync Request and Response Format.

If you want to omit one or more events from the response, you can do so by excluding them from the returned array. Likewise, if you want to discard the entire batch and prevent subscribers from receiving it, you can return an empty array.

Aggregated processing with partial results

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import { AppSyncEventsResolver } from '@aws-lambda-powertools/event-handler/appsync-events';
import type { AppSyncEventsPublishEvent } from '@aws-lambda-powertools/event-handler/types';
import type { Context } from 'aws-lambda';

const app = new AppSyncEventsResolver();

app.onPublish(
  '/default/foo/*',
  async (events) => {
    const payloadsToReturn: AppSyncEventsPublishEvent['events'] = [];

    for (const event of events) {
      if (event.payload.includes('foo')) continue;
      payloadsToReturn.push(event);
    }

    return payloadsToReturn; // (1)!
  },
  { aggregate: true }
);

export const handler = async (event: unknown, context: Context) =>
  app.resolve(event, context);
  1. You can also return an empty array [] to discard the entire batch and prevent subscribers from receiving it.
Handling errors

You can filter or reject events by throwing exceptions in your resolvers or by formatting the payload according to the expected response structure. This instructs AppSync not to propagate that specific message, so subscribers will not receive it.

Handling errors with individual items

When processing items individually, you can throw an exception to fail a specific message. When this happens, the Event Handler will catch it and include the exception name and message in the response.

Error handling with individual itemsResponse format with error

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
import { AppSyncEventsResolver } from '@aws-lambda-powertools/event-handler/appsync-events';
import { Logger } from '@aws-lambda-powertools/logger';
import type { Context } from 'aws-lambda';

const logger = new Logger({
  serviceName: 'appsync-events',
  logLevel: 'DEBUG',
});
const app = new AppSyncEventsResolver();

app.onPublish('/default/foo', (payload) => {
  try {
    return payload;
  } catch (error) {
    logger.error('Error processing event', { error });
    throw error;
  }
});

export const handler = async (event: unknown, context: Context) =>
  app.resolve(event, context);
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
{
  "events": [
    {
      "error": "Error message",
      "id": "1"
    },
    {
      "payload": {
        "data": "data_2"
      },
      "id": "2"
    }
  ]
}
Handling errors with aggregate

When processing batch of items with aggregate enabled, you must format the payload according the expected response.

Error handling with batch of itemsResponse with errors in individual items

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import { AppSyncEventsResolver } from '@aws-lambda-powertools/event-handler/appsync-events';
import type { OnPublishAggregateOutput } from '@aws-lambda-powertools/event-handler/types';
import type { Context } from 'aws-lambda';

const app = new AppSyncEventsResolver();

app.onPublish(
  '/default/foo/*',
  async (payloads) => {
    const returnValues: OnPublishAggregateOutput<{
      processed: boolean;
      original_payload: unknown;
    }> = [];
    for (const payload of payloads) {
      try {
        returnValues.push({
          id: payload.id,
          payload: { processed: true, original_payload: payload },
        });
      } catch (error) {
        returnValues.push({
          id: payload.id,
          error: `${error.name} - ${error.message}`,
        });
      }
    }

    return returnValues;
  },
  { aggregate: true }
);

export const handler = async (event: unknown, context: Context) =>
  app.resolve(event, context);
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
{
  "events": [
    {
      "error": "SyntaxError - Invalid item",
      "id": "1"
    },
    {
      "payload": {
        "processed": true,
        "original_payload": {
          "event_2": "data_2"
        }
      },
      "id": "2"
    }
  ]
}

If instead you want to fail the entire batch, you can throw an exception. This will cause the Event Handler to return an error response to AppSync and fail the entire batch.

All or nothing error handling with batch of itemsResponse with entire batch error

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import { AppSyncEventsResolver } from '@aws-lambda-powertools/event-handler/appsync-events';
import type { OnPublishAggregateOutput } from '@aws-lambda-powertools/event-handler/types';
import { Logger } from '@aws-lambda-powertools/logger';
import type { Context } from 'aws-lambda';

const logger = new Logger({
  serviceName: 'serverlessAirline',
  logLevel: 'INFO',
});
const app = new AppSyncEventsResolver();

app.onPublish(
  '/default/foo/*',
  async (payloads) => {
    const returnValues: OnPublishAggregateOutput<{
      processed: boolean;
      original_payload: unknown;
    }> = [];
    try {
      for (const payload of payloads) {
        returnValues.push({
          id: payload.id,
          payload: { processed: true, original_payload: payload },
        });
      }
    } catch (error) {
      logger.error('Error processing payloads', { error });
      throw error;
    }

    return returnValues;
  },
  { aggregate: true }
);

export const handler = async (event: unknown, context: Context) =>
  app.resolve(event, context);
{
  "error": "Error - An unexpected error occurred"
}

Throwing UnauthorizedException will cause the Lambda invocation to fail.

You can also do content-based authorization for channel by throwing an UnauthorizedException error. This can cause two situations:

UnauthorizedException

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import {
  AppSyncEventsResolver,
  UnauthorizedException,
} from '@aws-lambda-powertools/event-handler/appsync-events';
import type { Context } from 'aws-lambda';

const app = new AppSyncEventsResolver();

app.onPublish('/default/foo', (payload) => {
  return payload;
});

app.onPublish('/*', () => {
  throw new UnauthorizedException('You can only publish to /default/foo');
});

app.onSubscribe('/private/*', async (info) => {
  const userGroups =
    info.identity?.groups && Array.isArray(info.identity?.groups)
      ? info.identity?.groups
      : [];
  const channelGroup = 'premium-users';

  if (!userGroups.includes(channelGroup)) {
    throw new UnauthorizedException(
      `Subscription requires ${channelGroup} group membership`
    );
  }
});

export const handler = async (event: unknown, context: Context) =>
  await app.resolve(event, context);
Accessing Lambda context and event

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

Access event and context

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
import { AppSyncEventsResolver } from '@aws-lambda-powertools/event-handler/appsync-events';
import { Logger } from '@aws-lambda-powertools/logger';
import type { Context } from 'aws-lambda';

const logger = new Logger({
  serviceName: 'serverlessAirline',
});
const app = new AppSyncEventsResolver();

app.onPublish('/*', (payload, event, context) => {
  const { headers } = event.request; // (1)!
  const { awsRequestId } = context;
  logger.info('headers', { headers, awsRequestId });

  // your business logic here

  return payload;
});

export const handler = async (event: unknown, context: Context) =>
  app.resolve(event, context);
  1. The event parameter contains the original AppSync event and has type AppSyncEventsPublishEvent or AppSyncEventsSubscribeEvent from the @aws-lambda-powertools/event-handler/types.
Logging

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

You can change this behavior by passing a custom logger instance to the AppSyncEventsResolver and setting the log level for it, or by enabling Lambda Advanced Logging Controls 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, you can set the LOG_LEVEL to DEBUG in your environment variables or at the logger level and pass the logger instance to the AppSyncEventsResolver constructor to enable debug logging.

Debug loggingLogs output

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
import { AppSyncEventsResolver } from '@aws-lambda-powertools/event-handler/appsync-events';
import { Logger } from '@aws-lambda-powertools/logger';
import type { Context } from 'aws-lambda';

const logger = new Logger({
  serviceName: 'serverlessAirline',
  logLevel: 'DEBUG',
});
const app = new AppSyncEventsResolver({ logger });

app.onPublish('/default/foo', (payload) => {
  return payload;
});

export const handler = async (event: unknown, context: Context) =>
  app.resolve(event, context);
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
[
  {
    "level": "DEBUG",
    "message": "Registering onPublish route handler for path '/default/foo' with aggregate 'false'",
    "timestamp": "2025-04-22T13:24:34.762Z",
    "service": "serverlessAirline",
    "sampling_rate": 0
  },
  {
    "level": "DEBUG",
    "message": "Resolving handler for path '/default/foo'",
    "timestamp": "2025-04-22T13:24:34.775Z",
    "service": "serverlessAirline",
    "sampling_rate": 0,
    "xray_trace_id": "1-68079892-6a1723770bc0b1f348d9a7ad"
  }
]
Flow diagrams Working with single items
sequenceDiagram
    participant Client
    participant AppSync
    participant Lambda
    participant EventHandler
    note over Client,EventHandler: Individual Event Processing (aggregate=False)
    Client->>+AppSync: Send multiple events to channel
    AppSync->>+Lambda: Invoke Lambda with batch of events
    Lambda->>+EventHandler: Process events with aggregate=False
    loop For each event in batch
        EventHandler->>EventHandler: Process individual event
    end
    EventHandler-->>-Lambda: Return array of processed events
    Lambda-->>-AppSync: Return event-by-event responses
    AppSync-->>-Client: Report individual event statuses
Working with aggregated items
sequenceDiagram
    participant Client
    participant AppSync
    participant Lambda
    participant EventHandler
    note over Client,EventHandler: Aggregate Processing Workflow
    Client->>+AppSync: Send multiple events to channel
    AppSync->>+Lambda: Invoke Lambda with batch of events
    Lambda->>+EventHandler: Process events with aggregate=True
    EventHandler->>EventHandler: Batch of events
    EventHandler->>EventHandler: Process entire batch at once
    EventHandler->>EventHandler: Format response for each event
    EventHandler-->>-Lambda: Return aggregated results
    Lambda-->>-AppSync: Return success responses
    AppSync-->>-Client: Confirm all events processed
Unauthorized publish
sequenceDiagram
    participant Client
    participant AppSync
    participant Lambda
    participant EventHandler
    note over Client,EventHandler: Publish Event Authorization Flow
    Client->>AppSync: Publish message to channel
    AppSync->>Lambda: Invoke Lambda with publish event
    Lambda->>EventHandler: Process publish event
    alt Authorization Failed
        EventHandler->>EventHandler: Authorization check fails
        EventHandler->>Lambda: Raise UnauthorizedException
        Lambda->>AppSync: Return error response
        AppSync--xClient: Message not delivered
        AppSync--xAppSync: No distribution to subscribers
    else Authorization Passed
        EventHandler->>Lambda: Return successful response
        Lambda->>AppSync: Return processed event
        AppSync->>Client: Acknowledge message
        AppSync->>AppSync: Distribute to subscribers
    end
Unauthorized subscribe
sequenceDiagram
    participant Client
    participant AppSync
    participant Lambda
    participant EventHandler
    note over Client,EventHandler: Subscribe Event Authorization Flow
    Client->>AppSync: Request subscription to channel
    AppSync->>Lambda: Invoke Lambda with subscribe event
    Lambda->>EventHandler: Process subscribe event
    alt Authorization Failed
        EventHandler->>EventHandler: Authorization check fails
        EventHandler->>Lambda: Raise UnauthorizedException
        Lambda->>AppSync: Return error response
        AppSync--xClient: Subscription denied (HTTP 403)
    else Authorization Passed
        EventHandler->>Lambda: Return successful response
        Lambda->>AppSync: Return authorization success
        AppSync->>Client: Subscription established
    end
Testing your code

You can test your event handlers by passing a mock payload with the expected structure.

For example, when working with PUBLISH events, you can use the OnPublishOutput to easily cast the output of your handler to the expected type and assert the expected values.

Testing publish eventsSample publish event

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import { readFileSync } from 'node:fs';
import type { OnPublishOutput } from '@aws-lambda-powertools/event-handler/types';
import type { Context } from 'aws-lambda';
import { describe, expect, it } from 'vitest';
import { handler } from './gettingStartedOnPublish.js'; // (1)!

describe('On publish', () => {
  it('handles publish on /default/foo', async () => {
    // Prepare
    const event = structuredClone(
      JSON.parse(readFileSync('./samples/onPublishEvent.json', 'utf-8'))
    );

    // Act
    const result = (await handler(event, {} as Context)) as OnPublishOutput;

    // Assess
    expect(result.events).toHaveLength(3);
    expect(result.events[0].payload).toEqual({
      processed: true,
      original_payload: event.events[0].payload,
    });
    expect(result.events[1].payload).toEqual({
      processed: true,
      original_payload: event.events[1].payload,
    });
    expect(result.events[2].payload).toEqual({
      processed: true,
      original_payload: event.events[2].payload,
    });
  });
});
  1. See here to see the implementation of this handler.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
{
  "identity": null,
  "result": null,
  "request": {
    "headers": {
      "key": "value"
    },
    "domainName": null
  },
  "info": {
    "channel": {
      "path": "/default/foo",
      "segments": ["default", "foo"]
    },
    "channelNamespace": {
      "name": "default"
    },
    "operation": "PUBLISH"
  },
  "error": null,
  "prev": null,
  "stash": {},
  "outErrors": [],
  "events": [
    {
      "payload": {
        "event_1": "data_1"
      },
      "id": "5f7dfbd1-b8ff-4c20-924e-23b42db467a0"
    },
    {
      "payload": {
        "event_2": "data_2"
      },
      "id": "ababdf65-a3e6-4c1d-acd3-87466eab433c"
    },
    {
      "payload": {
        "event_3": "data_3"
      },
      "id": "8bb2983a-0967-45a0-8243-0aeb8c83d80e"
    }
  ]
}

You can also assert that a handler throws an exception when processing a specific event.

Testing subscribe eventsSample subscribe event

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
import { readFileSync } from 'node:fs';
import { UnauthorizedException } from '@aws-lambda-powertools/event-handler/appsync-events';
import type { Context } from 'aws-lambda';
import { describe, expect, it } from 'vitest';
import { handler } from './unauthorizedException.js'; // (1)!

describe('On publish', () => {
  it('rejects subscriptions on /default/bar', async () => {
    // Prepare
    const event = structuredClone(
      JSON.parse(readFileSync('./samples/onSubscribeEvent.json', 'utf-8'))
    );

    // Act & Assess
    await expect(() => handler(event, {} as Context)).rejects.toThrow(
      UnauthorizedException
    );
  });
});
  1. See here to see the implementation of this handler.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
{
  "identity": null,
  "result": null,
  "request": {
    "headers": {
      "key": "value"
    },
    "domainName": null
  },
  "info": {
    "channel": {
      "path": "/default/bar",
      "segments": ["default", "bar"]
    },
    "channelNamespace": {
      "name": "default"
    },
    "operation": "SUBSCRIBE"
  },
  "error": null,
  "prev": null,
  "stash": {},
  "outErrors": [],
  "events": null
}
2025-08-14

RetroSearch is an open source project built by @garambo | Open a GitHub Issue

Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo

HTML: 3.2 | Encoding: UTF-8 | Version: 0.7.4