

# Understanding channel namespaces
<a name="channel-namespaces"></a>

Channel namespaces (or just namespaces for short) define the channels that are available on your Event API, and the capabilities and behaviors of these channels. Channel namespaces provide a scalable approach to managing large numbers of channels. Instead of configuring each channel individually, developers can apply settings across an entire namespace. 

Each namespace has a name. This name represents the starting segment (the prefix) of a channel path. Any channel where the first segment in the path matches the name of a namespace, belongs to that namespace. For example, any channel with a first segment of `default`, such as `/default/messages`, `/default/greetings`, and `/default/inbox/user` belongs to the namespace named `default`. A channel namespace is made up of a maximum of five segments.

In a sports-related Event API example, you could have namespaces such as `basketball`, `soccer`, and `baseball`, with channels within each namespace such as `basketball/games/1`, `soccer/scores`, and `baseball/players`. 

You can only publish and subscribe to channels that belong to a defined namespace. However, a channel is ephemeral and is created on-demand when a client needs to publish or subscribe to it.

When you define your Event API, you must configure the default authorization for connecting, publishing and subscribing to an Event API. The publishing and subscribing authorization configuration is automatically applied to all namespaces. You can override this configuration at the namespace. To learn more about authorization, see [Configuring authorization and authentication to secure Event APIs](configure-event-api-auth.md).

# Process real-time events with AWS AppSync event handlers
<a name="channel-namespace-handlers"></a>

**Topics**
+ [

## Overview
](#event-handlers-overview)
+ [

## onPublish handler
](#event-handler-onpublish)
+ [

## onSubscribe handler
](#event-handler-onsubscribe)
+ [

## Error handling
](#error-handling)

## Overview
<a name="event-handlers-overview"></a>

AWS AppSync Event handlers let you run custom business logic on real-time events.

These JavaScript functions:
+ Run on the AWS AppSync runtime
+ Process published events
+ Authorize subscription requests

This topic covers *simple* event handlers without data source integration. For information about integrating with data sources like DynamoDB tables and Lambda functions, see the following topics:
+ [Data source integrations](https://docs.aws.amazon.com/appsync/latest/eventapi/data-source-integrations.html) 
+ [Direct Lambda integrations](https://docs.aws.amazon.com/appsync/latest/eventapi/direct-lambda-integrations.html) 

**Note**  
Event handlers run after the incoming request is authorized through your configured authorization mode.

## onPublish handler
<a name="event-handler-onpublish"></a>

Use the `onPublish` handler to process and filter events before they reach subscribers. This handler runs each time an event is published to a channel.

The handler uses this signature:

```
function onPublish(context: Context): OutgoingEvent[] | null
```

```
type Context = {
  events: IncomingEvent[];  // Array of events to process
  channel: string;          // Channel the events were published to
  identity: Identity;       // Information about the publisher
  info: {
    channel: {
      path: string;        // Full channel path
      segments: string[];  // Path segments
    }
  }
  channelNamespace: {
    name: string
  }
  operation: 'SUBSCRIBE' | 'PUBLISH'
}
```

The handler receives a context object with:
+ `events` - Array of events to process
+ `channel` - Target channel name
+ `identity` - Publisher information

For more information on the context object reference, see the [Context Reference guide](context-reference.md).

### Common onPublish tasks
<a name="common-onpublish-tasks"></a>

#### Forward all events
<a name="forward-events"></a>

The default API behavior for the `onPublish` handler *forwards all received events*. The onPublish function needs a context object as a parameter.

```
export function onPublish(ctx) {
  return ctx.events
}
```

#### Filter specific events
<a name="filter-events"></a>

Filter out events and return only those matching specific criteria. In the following example, the handler filters the events and only forwards those that have *odds greater than 0.5*.

```
export function onPublish(ctx) {
  return ctx.events.filter((event) => event.payload.odds > 0.5)
}
```

#### Transform events
<a name="transform-events"></a>

Transform events by mapping them to a new shape. In the following example, the handler below formats each event to include a *timestamp* and changes the message to upper case format.

```
import { util } from '@aws-appsync/utils'

export function onPublish(ctx) {
  return ctx.events.map(event => ({
    id: event.id,
    payload: {
      ...event.payload,
      message: event.payload.message.toUpperCase(),
      timestamp: util.time.nowISO8601()
    }
  }))
}
```

### Important rules for onPublish
<a name="onpublish-rules"></a>
+ Avoid duplicate event IDs
+ Events with an `error` property won't broadcast
+ `Null` values in the returned array are ignored
+ Returns an array of events or null
+ Match each returned event ID to an incoming event
+ Using an unknown event ID will result in an error

## onSubscribe handler
<a name="event-handler-onsubscribe"></a>

Use the `onSubscribe` handler to authorize and process subscription requests before allowing channel subscriptions. The handler in the following example runs when the client subscribes. 

The handler uses this signature:

```
function onSubscribe(context: Context): void
```

For more information on the context object reference, see [the Context Reference Guide](https://docs.aws.amazon.com/appsync/latest/eventapi/context-reference.html).

### Examples
<a name="onsubscribe-examples"></a>

**Example Logging a subscription request**  <a name="logging-subscription"></a>
In the following example, a Amazon Cognito user pool authorization is used and the `onSubscribe` handler logs a message when an admin user subscribes to a channel.   

```
export function onSubscribe(ctx) {
  if (ctx.identity.groups.includes('admin')) {
    console.log(`Admin ${ctx.identity.sub} subscribed to ${ctx.channel}`)
  }
}
```

**Example Reject a request**  <a name="reject-subscription"></a>
In the following example, a subscription request is rejected by calling `util.unauthorized()`. The `onSubscribe` handler restricts the Amazon Cognito user pool's authenticated users to their own channel. Clients can only subscribe to channels that match the pattern `/messages/inbox/[username]`.  

```
export function onSubscribe(ctx) {
  const requested = ctx.info.channel.path
  const allowed = `/messages/inbox/${ctx.identity.username}`
  
  if (requested !== allowed) {
    util.unauthorized()
  }
}
```

## Error handling
<a name="error-handling"></a>

Use error handling to manage invalid requests and provide meaningful feedback to users. This toipic explains how to implement error handling for both *HTTP endpoints* and *WebSocket connections*. To reject requests and return errors to publishers or subscribers, use the `utility.error` function. When the publishing is done using the HTTP endpoint, it returns an HTTP `403` response. 

**Note**  
When publishing is done over an HTTP endpoint, it returns an HTTP `403` response.
When publishing over WebSocket, it returns a `publish_error` message with the provided message.

### Examples
<a name="error-handling-examples"></a>

The following examples demonstrates how to return an error message.

**Example Validating required message properties**  <a name="validate-properties"></a>

```
export function onPublish(ctx) {
  return ctx.events.map(event => {
    if (!event.payload.message) {
      event.error = "Message required"
    }
    return event
  })
}
```

**Example Rejecting entire requests**  <a name="reject-requests"></a>

```
export function onPublish(ctx) {
  util.error("Operation not allowed")
}
```

# Data source integrations for AWS AppSync events
<a name="data-source-integrations"></a>

With AWS AppSync Events, you can create event handlers that run custom business logic on AWS AppSync's JavaScript runtime. By configuring data source integrations, you can interact with external systems like Lambdafunctions, DynamoDB tables, or Amazon RDS Aurora databases.

**Topics**
+ [

## Overview
](#overview)
+ [

## Handler structure
](#handler-structure)
+ [

# Direct data source integrations using Lambda
](direct-lambda-integrations.md)

## Overview
<a name="overview"></a>

You can integrate data sources in your channel namespaces to interact with DynamoDB tables and Lambda functions. Each integration requires implementing request and response functions. This topic explains how to configure and use data sources with event handlers.

For detailed information about data source configuration options, see [Working with data sources for AWS AppSync Event APIs](https://docs.aws.amazon.com/appsync/latest/eventapi/data-sources-chapter.html).

## Handler structure
<a name="handler-structure"></a>

A handler that interacts with a data source has the following two functions:
+ Request — Defines the payload sent to the data source
+ Response — Defines the payload sent to the data source

**Example Saving events to DynamoDB before broadcasting**  
The following example defines an `onPublish` handler that saves all published events to a messages\$1table in DynamoDB before forwarding them to be broadcast.  

```
import * as ddb from `@aws-appsync/utils/dynamodb`

const TABLE = 'messages_table'
export const onPublish = {
  request(ctx) {
    const channel = ctx.info.channel.path
    return ddb.batchPut({
      tables: {
        [TABLE]: ctx.events.map(({ id, payload }) => ({ channel, id, ...payload })),
      },
    })
  },
  response(ctx) {
    console.log(`Batch Put result:`, ctx.result.data[TABLE])
    return ctx.events
  }
}
```

**Note**  
You must return the list of events you want broadcasted in the response function.

**Example Forward all events after accessing it from the data source**  <a name="forward-all-events"></a>

```
import * as ddb from `@aws-appsync/utils/dynamodb`

const TABLE = 'messages_table'
export const onPublish = {
  request(ctx) {
    const channel = ctx.info.channel.path
    return ddb.batchPut({
      tables: {
        [TABLE]: ctx.events.map(({ id, payload }) => ({ channel, id, ...payload })),
      },
    })
  },
  response: (ctx) => ctx.events // forward all events
}
```

### onSubscribe handler
<a name="onsubscribe-handler"></a>

Use the onSubscribe handler to process subscription requests.

**Example Logging subscriptions to an RDS database**  

```
import { insert, createPgStatement as pg } from '@aws-appsync/utils/rds'

export const onSubscribe = {
  request(ctx) {
    const values = {
      channel: ctx.info.channel.path,
      identity: ctx.identity
    }
    return pg(insert({table: 'subs', values}))
  },
  response(ctx) {} // Required empty function
}
```

**Note**  
You must provide a response function, which can be empty if no other action is required.

**Example Granting access based on a channel path**  

```
import { select, createPgtatement as pg, toJsonObject } from '@aws-appsync/utils/rds';

export const onSubscribe = {
  request(ctx) {
    const values = {
      channel: ctx.info.channel.path,
      identity: ctx.identity
    }
    return pg(insert({
      select: 'subs',
      where: { channel: { eq: 'ALLOWED' }}
    }))
  },
  response(ctx){
    const { error, result } = ctx;
    if (error) {
        return util.error('db error')
    }
    const res = toJsonObject(result)[1][0];
    if (!res.length) {
      // did not find the item, subscription is not authorized
      util.unauthorized()
    }
  }
}
```

**Example Skipping the data source**  <a name="skip-data-source"></a>
To skip the data source invocation at runtime use the `runtime.earlyReturn` utility. The `earlyReturn` operation returns the provided payload and skips the response function.  

```
import * as ddb from `@aws-appsync/utils/dynamodb`

export const onPublish = {
  request(ctx) {
    if (ctx.info.channel.segments.includes('private')) {
      // return early and do no execute the response.
      return runtime.earlyReturn(ctx.events)
    }
    const channel = ctx.info.channel.path
    const event = ctx.events[0]
    const key = { channel, id: event.payload.id }
    return ddb.put({ key, item: event.payload })
  },
  response: (ctx) => ctx.events // forward all events
}
```

# Direct data source integrations using Lambda
<a name="direct-lambda-integrations"></a>

AWS AppSync lets you integrate Lambda functions directly with your channel namespace without writing additional handler code. Both *publish* and *subscribe* operations are supported through `Request`/`Response` mode and `event` mode.

**Note**  
Configuring your direct Lambda integration in `event` mode triggers your Lambda aysncronously and does not wait for a reply. The result of the invocation does not impact the rest of the `onPublish` or `onSubscribe` handling.

## How it works
<a name="how-it-works"></a>

Your Lambda function receives a context object containing event information. The function then passes a context object containing information for the events. The Lambda function can perform the following operations.
+ Filter and transform events for broadcasting
+ Return error messages for failed processing
+ Handle both publish and subscribe operations

**Example Setting a Lambda configuration of a channel namespace in AWS CloudFormation**  
Set up a direct Lambda integration, configure `handlerConfigs` on your [channel namespace](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-appsync-channelnamespace.html) using AWS CloudFormation.  

```
{
  "Type" : "AWS::AppSync::ChannelNamespace",
  "Properties" : {
    "ApiId" : "api-id-123",
    "Name" : "lambda-direct-ns",
    "HandlerConfigs" : {
      "OnPublish": {
        "Behavior": "DIRECT",
        "Integration": {
          "DataSourceName": "LAMBDA_FUNCTION_DS",
          "LambdaConfig": {
            "InvokeType": "REQUEST_RESPONSE"
          }
        }
      }
    }
  }
}
```

**Example Setting a Lambda configuration of a channel namespace in the AWS Cloud Development Kit (AWS CDK)**  

```
api.addChannelNamespace('lambda-direct-ns', {
  publishHandlerConfig: {
    dataSource: lambdaDataSource,
    direct: true,
  },
});
```
With this configuration, you do not need to provide code for your channel namespace. Your Lambda function will be called for every `onPublish` event.

**Example `onPublish` response format**  <a name="onpublish-response"></a>
`onPublish` handlers that are configured with the `REQUEST_RESPONSE` invocation type must return a response payload with the following structure.  

```
type LambdaAppSyncEventResponse = {
  events?: OutgoingEvent[], // array of outgoing events to broadcast
  error?: string //Optional error message if processing fails
}
```

**Note**  
If you use `EVENT` as the invocation type, your Lambda function is trigged asynchronously and AWS AppSync does not wait for a response. AWS AppSync broadcasts the events as usual.
If you include an error message in the response when logging is enabled, AWS AppSync Events logs the error message but doesn't return it to the publisher.

**Example onSubscribe response format**  <a name="onsubscribe-response"></a>
For `onSubscribe` handlers configured with `REQUEST_RESPONSE` invocation type, your Lambda function must return one of the following.  
+ A payload containing an error message
+ `null` to indicate a successful subscription

```
type LambdaAppSyncEventResponse = {
  error: string // Error message if subscription fails
} | null
```

## Best practices
<a name="best-practices"></a>

We recommend the following best practices for using direct Lambda integrations.
+ Enable logging to track error messages.
+ Ensure your Lambda function handles both success and error cases.
+ Test your integration with various payload scenarios.

## Powertools for Lambda
<a name="powertools"></a>

You can utilize the following Powertools for Lambda to efficiently write your Lambda function handlers the following languages.
+ [TypeScript/Node.js ](https://docs.powertools.aws.dev/lambda/typescript/latest/features/event-handler/appsync-events/)
+ [Python](https://docs.powertools.aws.dev/lambda/python/latest/core/event_handler/appsync_events/) 
+ [.NET ](https://docs.powertools.aws.dev/lambda/dotnet/core/event_handler/appsync_events/)