Search code examples
node.jsaws-lambdajsonschemaserverless-frameworkmiddy

Response body validation using middy


This is the return statement of my lambda function:

  return {
    statusCode: 201,
    body: JSON.stringify({item: 'apple', price: 1}),
  }

How can I use middy to validate that the body is an object with properties: item and price?

The body should always be returned as a string (JSON.stringify) otherwise the lambda function fails on runtime.

Therefore schema validation here fails (I skipped checking for item and price properties for now):

const responseSchema = transpileSchema({
  type: 'object',
  required: ['body', 'statusCode'],
  properties: {
    body: {
      type: 'object'
    },
    statusCode: {
      type: 'number'
    }
  }
})

But this works nonetheless:

const responseSchema = transpileSchema({
  type: 'object',
  required: ['body', 'statusCode'],
  properties: {
    body: {
      type: 'string'
    },
    statusCode: {
      type: 'number'
    }
  }
})

This is my middleware sequence

import middy from '@middy/core'
import cors from '@middy/http-cors'
import globalErrorHandler from './globalErrorHandler.js'
import httpJsonBodyParser from '@middy/http-json-body-parser'
import validator from '@middy/validator'

    return middy()
    .use(cors())
    .use(httpJsonBodyParser())
    .use(validator({ eventSchema }))
    .use(validator({ responseSchema }))
    .use(globalErrorHandler())
    .handler(handler)
const globalErrorHandler = () => {
    const onError = async (request) => {
        const { error } = request;
        return {
            statusCode: error.statusCode|| 500,
            body: JSON.stringify({ error:{...error, cause: error.cause} }),
        };
    }
    return { onError };
}

export default globalErrorHandler;

Solution

  • I resolved by issue by using the http-response-serializer from middy. Upon lambda JSON response (not string), the validator will validate the response body and then the middy http-response-serializer middleware serializes the lambda response.

    The validator did not seem to serialize the response under the hood.

    Lambda function

      return {
        statusCode: 201,
        body: Object({item:{...params.Item}}),
      }
    

    Middleware

    middy()
            .use(cors())
            .use(httpResponseSerializer({
                serializers: [
                    {
                        regex: /^application\/xml$/,
                        serializer: ({ body }) => `<message>${body}</message>`
                    },
                    {
                        regex: /^application\/json$/,
                        serializer: ({ body }) => JSON.stringify(body)
                    },
                    {
                        regex: /^text\/plain$/,
                        serializer: ({ body }) => body
                    }
                ],
                defaultContentType: 'application/json'
            }))
            .use(validator({responseSchema}))
            .handler(handler)