Blog For Devs

Routes handling with Apex and AWS LambdaReq

author Dumitru GlavanDumitru Glavan (@doomhz)

Deploying Lambda functions with Apex is fairly easy. Following their one page guide will set you up and running on Lambda in minutes. More than that, there a bunch of code examples available that will help you set up a proper Javascript environment for your functions.

The only thing I was missing is a tiny lib to handle APIGateway HTTP requests and routing. Every time I had to write helper functions that parse the incoming parameters and respond accordingly to the APIGateway or direct Lambda invocations. I finally managed to hide all this complexity in a small, reusable package called LambdaReq. I’m doing my best to keep the API and the examples up to date.

Routing HTTP requests with APIGateway

Parsing data from APIGateway and responding with the right format can’t get simpler than this. Each request handler gets a callback with direct access to the event parameters. req.params will gather all the data from event.queryStringParameters, event.pathParameters and event.body.

import LambdaReq from 'lambda-req'

const lambda = new LambdaReq()

lambda.get('/myFunc', (req)=> Promise.resolve({ hello: req.params.username }))
lambda.post('/myFunc', (req)=> Promise.resolve({ hello: req.params.username }))
lambda.put('/myFunc', (req)=> Promise.resolve({ hello: req.params.username }))
lambda.delete('/myFunc', (req)=> Promise.resolve({ hello: req.params.username }))
lambda.options('/myFunc', (req)=> Promise.resolve({ hello: req.params.username }))

export default lambda.invoke

Shorthand REST methods are available for setting up routes.

Handle direct invocations

Handling maintenance tasks (i.e. migrations, cronjobs) and internal Lambda happens through the .proxy method. Each proxy handler has a unique name, passed as a command prop on the Lambda event.

import LambdaReq from 'lambda-req'

const lambda = new LambdaReq()

lambda.proxy('cleanup', (req)=> {
  const { params } = req
  const result = await db.cleanup(params.table)
  return { result }
})

export default lambda.invoke

Simply, invoke the cleanup task with $ echo -n '{ "command": "cleanup", "params": { "table": "users" } }' | apex invoke myFunc.

Call other Lambdas

LambdaProxy calls other Lambdas on the same network.

import LambdaReq, { LambdaProxy, LambdaReqError } from 'lambda-req'

const lambda = new LambdaReq()

lambda.get('/myFunc', (req)=> {
  const { params } = req
  
  const proxy = new LambdaProxy()
  
  return proxy.invoke('mySecondFunc', 'cleanup', params)
  .then((response)=> {
    return { message: 'Proxy response from mySecondFunc', response }
  })
  .catch((err)=> {
    console.error(err)
    throw new LambdaReqError({
      message: {
        error: {
          code: 'lambdaInvocationError',
          message: 'mySecondFunc Lambda is unresponsive.'
        }
      }
    })
  })
})

export default lambda.invoke

LambdaProxy.invoke will return a JSON response.

Return a response or throw errors

A route handler can return any result on success that will be stringified as a body for APIGateway HTTP responses. Promise results are also supported.

import { LambdaReqError } from 'lambda-req'

lambda.get('/myFunc/{id}', (req)=> {
  const { params } = req
  const user = await db.findUser(params.id)
  if (!user) {
    throw new LambdaReqError({
      message: { 
        error: {
          code: 'userNotFound'
        }
      },
      status: 404
    })
  }
  return { user }
})

Handlers can throw errors or reject Promises, they will be properly parsed by the router. LambdaReqError error types can set a message and a custom response status for the APIGateway.

More features

Support for SNS and SQS is coming next. Any help and feedback is highly appreciated. More examples are here. A complete API is available on GitHub.