<!--
title: Serverless Framework - AWS Lambda Events - Websocket
menuText: Websocket
menuOrder: 3
description: Setting up AWS Websockets with AWS Lambda via the Serverless Framework
layout: Doc
-->

<!-- DOCS-SITE-LINK:START automatically generated  -->

### [Read this on the main serverless docs site](https://www.serverless.com/framework/docs/providers/aws/events/websocket)

<!-- DOCS-SITE-LINK:END -->

# Websocket

[Websockets](https://www.w3.org/TR/websockets/) make it possible to add support for a bi-directional communication channel between clients and servers. Connection channels are kept alive and are re-used to exchange messages back-and-forth.

The Serverless Framework makes it possible to setup an [API Gateway powered](https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-websocket-api-overview.html) Websocket backend with the help of the `websocket` event.

## Event Definition

### Simple

The following code will setup a websocket with a `$connect` route key:

```yml
functions:
  connectHandler:
    handler: handler.connectHandler
    events:
      - websocket: $connect
```

### Extended

This code will setup a websocket with a `$disconnect` route key:

```yml
functions:
  disconnectHandler:
    handler: handler.disconnectHandler
    events:
      - websocket:
          route: $disconnect
```

This code will setup a [RouteResponse](https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-websocket-api-route-response.html), enabling you to respond to websocket messages by using the `body` parameter of your handler's callback response:

```yml
functions:
  helloHandler:
    handler: handler.helloHandler
    events:
      - websocket:
          route: hello
          routeResponseSelectionExpression: $default
```

## Routes

The API-Gateway provides [4 types of routes](https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-websocket-api-overview.html) which relate to the lifecycle of a ws-client:

- `$connect` called on connect of a ws-client
- `$disconnect` called on disconnect of a ws-client (may not be called in some situations)
- `$default` called if there is no handler to use for the event
- custom routes - called if the route name is specified for a handler

### Example serverless.yaml

This Serverless yaml will specify handlers for the `$connect`, `$disconnect`, `$default` and the custom `foo` event.

```yml
service: serverless-ws-test

provider:
  name: aws
  runtime: nodejs12.x
  websocketsApiName: custom-websockets-api-name
  websocketsApiRouteSelectionExpression: $request.body.action # custom routes are selected by the value of the action property in the body

functions:
  connectionHandler:
    handler: handler.connectionHandler
    events:
      - websocket:
          route: $connect
      - websocket:
          route: $disconnect
  defaultHandler:
    handler: handler.defaultHandler
    events:
      - websocket: $default #simple event definition without extra route property
  customFooHandler:
    handler: handler.fooHandler
    events:
      - websocket:
          route: foo # will trigger if $request.body.action === "foo"
```

## Using Authorizers

You can enable an authorizer for your connect route by specifying the `authorizer` key in the websocket event definition.

**Note:** AWS only supports authorizers for the `$connect` route.

```yml
functions:
  connectHandler:
    handler: handler.connectHandler
    events:
      - websocket:
          route: $connect
          authorizer: auth # references the auth function below
  auth:
    handler: handler.auth
```

Or, if your authorizer function is not managed by this service, you can provide an arn instead:

```yml
functions:
  connectHandler:
    handler: handler.connectHandler
    events:
      - websocket:
          route: $connect
          authorizer: arn:aws:lambda:us-east-1:1234567890:function:auth
```

By default, the `identitySource` property is set to `route.request.header.Auth`, meaning that your request must include the auth token in the `Auth` header of the request. You can overwrite this by specifying your own `identitySource` configuration:

```yml
functions:
  connectHandler:
    handler: handler.connectHandler
    events:
      - websocket:
          route: $connect
          authorizer:
            name: auth
            identitySource:
              - 'route.request.header.Auth'
              - 'route.request.querystring.Auth'

  auth:
    handler: handler.auth
```

With the above configuration, you can now must pass the auth token in both the `Auth` query string as well as the `Auth` header.

You can also supply an ARN instead of the name when using the object syntax for the authorizer:

```yml
functions:
  connectHandler:
    handler: handler.connectHandler
    events:
      - websocket:
          route: $connect
          authorizer:
            arn: arn:aws:lambda:us-east-1:1234567890:function:auth
            identitySource:
              - 'route.request.header.Auth'
              - 'route.request.querystring.Auth'

  auth:
    handler: handler.auth
```

## Send a message to a ws-client

To send a message to a ws-client the [@connection](https://docs.amazonaws.cn/en_us/apigateway/latest/developerguide/apigateway-how-to-call-websocket-api-connections.html) command is used.

It uses the URL of the websocket API and most importantly the `connectionId` of the ws-client's connection. If you want to send a message to a ws-client from another function, you need this `connectionId` to address the ws-client.

Example on how to respond with the complete `event` to the same ws-client:

```js
const sendMessageToClient = (url, connectionId, payload) =>
  new Promise((resolve, reject) => {
    const apigatewaymanagementapi = new AWS.ApiGatewayManagementApi({
      apiVersion: '2018-11-29',
      endpoint: url,
    });
    apigatewaymanagementapi.postToConnection(
      {
        ConnectionId: connectionId, // connectionId of the receiving ws-client
        Data: JSON.stringify(payload),
      },
      (err, data) => {
        if (err) {
          console.log('err is', err);
          reject(err);
        }
        resolve(data);
      }
    );
  });

module.exports.defaultHandler = async (event, context) => {
  const domain = event.requestContext.domainName;
  const stage = event.requestContext.stage;
  const connectionId = event.requestContext.connectionId;
  const callbackUrlForAWS = util.format(util.format('https://%s/%s', domain, stage)); //construct the needed url
  await sendMessageToClient(callbackUrlForAWS, connectionId, event);

  return {
    statusCode: 200,
  };
};
```

## Respond to a ws-client message

To respond to a websocket message from your handler function, [Route Responses](https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-websocket-api-route-response.html) can be used. Set the `routeResponseSelectionExpression` option to enable this. This option allows you to respond to a websocket message using the `body` parameter.

```yml
functions:
  sayHelloHandler:
    handler: handler.sayHello
    events:
      - websocket:
          route: hello
          routeResponseSelectionExpression: $default
```

```js
module.exports.helloHandler = async (event, context) => {
  const body = JSON.parse(event.body);
  return {
    statusCode: 200,
    body: `Hello, ${body.name}`,
  };
};
```

## Logs

Use the following configuration to enable Websocket logs:

```yml
# serverless.yml
provider:
  name: aws
  logs:
    websocket: true
```

The log streams will be generated in a dedicated log group which follows the naming schema `/aws/websocket/{service}-{stage}`.

The default log level will be INFO. You can change this to error with the following:

```yml
# serverless.yml
provider:
  name: aws
  logs:
    websocket:
      level: ERROR
```

Valid values are INFO, ERROR.
