Configurable Middlewares

In order to make middlewares configurable, they are generally exported as a function that accepts a configuration object. This function should then return the middleware object with before, after, and onError as keys.

E.g.

// customMiddleware.js

const defaults = {}

const customMiddleware = (opts) => {
  const options = { ...defaults, ...opts }

  const customMiddlewareBefore = async (request) => {
    const { event, context } = request
    // ...
  }

  const customMiddlewareAfter = async (request) => {
    const { response } = request
    // ...
    request.response = response
  }

  const customMiddlewareOnError = async (request) => {
    if (typeof request.response === 'undefined') return
    await customMiddlewareAfter(request)
  }

  return {
    before: customMiddlewareBefore,
    after: customMiddlewareAfter,
    onError: customMiddlewareOnError
  }
}

export default customMiddleware

With this convention in mind, using a middleware will always look like the following example:

import middy from '@middy/core'
import customMiddleware from 'customMiddleware.js'

const lambdaHandler = async (event, context) => {
  // do stuff
  return {}
}

export const handler = middy()
  .use(
    customMiddleware({
      option1: 'foo',
      option2: 'bar'
    })
  )
  .handler(lambdaHandler)

Exporting an option validator

Alongside the factory function, export a named option validator so consumers can catch typos and type mismatches in their config. Use the shared validateOptions helper from @middy/util. Schemas use a JSON-Schema-compatible subset — see Validating options for the full list of supported keywords.

import { validateOptions } from '@middy/util'

const optionSchema = {
  type: 'object',
  required: ['option1'],
  properties: {
    option1: { type: 'string' },
    option2: { type: 'string' },
  },
  additionalProperties: false,
}

export const customValidateOptions = (options) =>
  validateOptions('custom-middleware', optionSchema, options)

For AWS-SDK-backed middlewares, inline the common fields (AwsClient, awsClientOptions, cacheKey, cacheExpiry, etc.) directly in your properties — see @middy/ssm, @middy/s3, @middy/dynamodb for the pattern.