Serverless Offline WebSockets

July 07, 2024

The API Gateway WebSocket APIs are emulated by Serverless Offline, but setting it right can be tricky.

In production, you probably want to use the Amazon API Gateway Management API SDK client.

pnpm i @aws-sdk/client-apigatewaymanagementapi

But it won't work properly offline so you need to check if you are offline and send a request instead to the @connections/{connectionId} local management endpoint.

The IS_OFFLINE variable is injected by Serverless Offline.

const { WS_ENDPOINT, IS_OFFLINE } = process.env

const client = new ApiGatewayManagementApi({
  apiVersion: "2018-11-29",
  endpoint: WS_ENDPOINT,
})

const sendToConnection = async (connectionId, input) => {
  if (IS_OFFLINE === "true") {
    await fetch(`http://127.0.0.1:3001/@connections/${connectionId}`, {
      method: "POST",
      body: JSON.stringify(input),
    })
  } else {
    await client.postToConnection({
      ConnectionId: connectionId,
      Data: Buffer.from(JSON.stringify(input)),
    })
  }
}

To easily get the endpoint you will be making requests in production when you deploy and assign to the environment variables of your Lambda function you can use the following value.

It assumes you are using a serverless.ts or .js file and deploying to us-east-1.

const WS_ENDPOINT = {
  "Fn::Join": [
    "",
    [
      "https://",
      {
        Ref: "WebsocketsApi",
      },
      `.execute-api.us-east-1.`,
      {
        Ref: "AWS::URLSuffix",
      },
      `/${stage}`,
    ],
  ],
}

const environment = {
  WS_ENDPOINT,
}

Written by João Oliveira in his brief moments of clarity.