API Gateway (HTTP)

API Gateway HTTP API (v2) is the modern, cheaper, faster API Gateway flavour. Use this page when your Lambda is the integration target of an HTTP API.

AWS documentation

What AWS sends

A v2 payload event has lowercase headers, a requestContext.http.method, a rawPath, a rawQueryString, and a body that is a string (or base64 string when isBase64Encoded is true). Cookies arrive in event.cookies as an array.

The response must be { statusCode, headers, body, cookies?, isBase64Encoded? } or a string (passed through verbatim).

MiddlewareWhy
@middy/http-header-normalizerLowercase header keys (v2 already lowercases, but defensive parity with v1 callers)
@middy/http-event-normalizerEnsure queryStringParameters and pathParameters are objects, not undefined
@middy/http-json-body-parserParse event.body to an object; 415 if Content-Type is wrong
@middy/validatorJSON Schema validation for request and response
@middy/http-corsOr configure CORS at the gateway level and skip this
@middy/http-security-headersHSTS, X-Content-Type-Options, etc.
@middy/http-error-handlerMap thrown http-errors to clean responses (put last)
@middy/http-content-encodinggzip/br responses based on Accept-Encoding

Minimal example

import middy from '@middy/core'
import httpJsonBodyParser from '@middy/http-json-body-parser'
import httpErrorHandler from '@middy/http-error-handler'

const lambdaHandler = async (event) => {
  return {
    statusCode: 200,
    body: JSON.stringify({ method: event.requestContext.http.method, body: event.body })
  }
}

export const handler = middy()
  .use(httpJsonBodyParser())
  .use(httpErrorHandler())
  .handler(lambdaHandler)

Production example

import middy from '@middy/core'
import httpEventNormalizer from '@middy/http-event-normalizer'
import httpHeaderNormalizer from '@middy/http-header-normalizer'
import httpJsonBodyParser from '@middy/http-json-body-parser'
import httpSecurityHeaders from '@middy/http-security-headers'
import httpCors from '@middy/http-cors'
import httpContentEncoding from '@middy/http-content-encoding'
import httpErrorHandler from '@middy/http-error-handler'
import validator from '@middy/validator'
import { transpileSchema } from '@middy/validator/transpile'

const schema = {
  type: 'object',
  properties: {
    body: {
      type: 'object',
      properties: { email: { type: 'string', format: 'email' } },
      required: ['email']
    }
  }
}

const lambdaHandler = async (event, context, { signal }) => {
  return { statusCode: 201, body: JSON.stringify({ email: event.body.email }) }
}

export const handler = middy({
  timeoutEarlyResponse: () => ({ statusCode: 408 })
})
  .use(httpHeaderNormalizer())
  .use(httpEventNormalizer())
  .use(httpJsonBodyParser())
  .use(httpSecurityHeaders())
  .use(httpCors({ origin: 'https://app.example.com' }))
  .use(httpContentEncoding())
  .use(validator({ eventSchema: transpileSchema(schema) }))
  .use(httpErrorHandler())
  .handler(lambdaHandler)

Multiple routes in one Lambda

Use @middy/http-router to dispatch by method + path within a single function. Useful for monolithic Lambda APIs.

Recipes

Common gotchas

  • event.body is a string by default. Always pair with httpJsonBodyParser (or your own parser) before reading fields.
  • CORS at gateway vs middleware. API Gateway HTTP API can handle CORS for you in the API config. If you do that, do not also .use(httpCors()) or you will double-set headers.
  • Lowercase headers everywhere. v2 ships lowercase already; do not rely on event.headers['Content-Type'] with capitalization.
  • Path params under event.pathParameters. Not under event.params.
  • Cookies are arrays. event.cookies is string[]; in the response use cookies: string[], not Set-Cookie headers.

Last updated: