Laconia

Laconia

  • Getting Started
  • API
  • Help

›Guides

Introduction

  • Getting Started
  • Motivation
  • Core Concepts
  • Philosophy
  • Examples
  • FAQ
  • Support Me

Laconia vs. Other

  • Claudia
  • Express, Koa, Hapi, etc.
  • Middy
  • Serverless, SAM, Apex, etc.

Guides

  • Injecting Dependencies
  • Unit Testing
  • Adapting Events
  • Creating API Endpoints
  • Retrieving Secrets
  • Retrieving Config
  • Warming Up
  • Invoking Other Lambdas
  • Long Running Tasks
  • Creating Middleware

API Reference

  • API Reference: Intro
  • core
  • event
  • adapter
  • adapter-api
  • invoker
  • config
  • batch
  • middleware-lambda-warmer
  • middleware-serverless-plugin-warmup
Edit

Creating API Endpoints

Overview

Laconia supports parsing and responding to AWS API Gateway Lambda Proxy Integration. Like other AWS Lambda events, Laconia provides two ways for you to adapt API Gateway events into your applications:

  • Creating your own adapter
  • Using Laconia built-in adapters

The principles

Laconia encourages single-purpose functions, which means most of your routing needs must be defined in API Gateway instead of Lambda.

Incoming events should be able to be treated with plain function. The requests that are coming in are function arguments, the response that goes out is a return statement. This means Laconia does not support the typical semantic of req and res from other web frameworks like Express.

Creating an adapter

As Laconia is encouraging you to design your ports first, it is likely that you will need to implement your own adapter. Let's take an example of an API endpoint for creating an arbitrary order. You can implement it with the event package like this:

const laconia = require("@laconia/core");
const { req, res } = require("@laconia/event").apigateway;

// Your core application
const createOrder = async orderDetails => {
  // Creates order...
  throw new Error("Duplicate order Id");
};

const adapter = app => async event => {
  try {
    const r = req(event); // Parse raw API Gateway event
    const orderDetails = r.body; // JSON parse the request body if content-type is application/json
    const output = await app(orderDetails);
    return res(output); // Creates an API Gateway response
  } catch (err) {
    // Perform `JSON.stringify` automatically and set status code to 500
    return res({ error: { message: err.message } }, 500);
  }
};

exports.handler = laconia(adapter(createOrder));

Composition

Laconia believes that intuitive API design is the design that's closest to the programming language used. To support shared code, such as additional headers for CORS across multiple Lambdas, you can do a simple composition like this:

const withCors = next => async (...args) => {
  const response = await next(...args);
  response.headers["Access-Control-Allow-Origin"] = "*";
  return response;
};

const adapter = app => withCors(event => res("hello"));

Error handling

Your application code must not contain any HTTP details, this would include error handling. The error must be handled in the catch block of your adapter. You can check the error object's properties to customise the status code or error body, such as by checking its name.

const adapter = app => event => {
  try {
    return app(req(event).body);
  } catch (err) {
    if (err.name === "ValidationError") {
      return res("ValidationError is thrown, the error was ...", 400);
    } else {
      return res("Unknown error", 500);
    }
  }
};

Using built-in adapter

Laconia also provides built-in adapters to handle simple adapter implementations. When building REST API, the adapter-api only supports GET or POST at the moment as you can only select either body or params input type at one time. Also, please only use the built-in adapters when it matches with your application's ports. Taking the previous example of creating an arbitrary order, you can use the Laconia built-in adapters like this:

const laconia = require("@laconia/core");
const adapterApi = require("@laconia/adapter-api");

// Your core application
const createOrder = async orderDetails => {
  // Creates order...
  throw new Error("Duplicate order Id");
};

const apigateway = adapterApi.apigateway({
  inputType: "body",
  errorMappings: {
    ".*": error => ({
      body: { error: { message: error.message } },
      statusCode: 500
    })
  }
});

exports.handler = laconia(apigateway(app)).register(instances);

Additional headers

To add additional headers like CORS, you can specify responseAdditionalHeaders configuration like this:

const apigateway = adapterApi.apigateway({
  responseAdditionalHeaders: {
    "Access-Control-Allow-Origin": "*"
  }
});

Error handling

The built-in adapter supports a simple mapping from error name thrown from your application, to the response that it should return. The mapping is done by specifying a regex, and the adapter will test the error thrown against each of the regex in the mapping specified.

The following example returns statusCode 400 when ValidationError is returned.

const apigateway = adapterApi.apigateway({
  errorMappings: {
    "Validation.*": error => ({ statusCode: 400 }),
    "Other.*": error => ({ statusCode: 404 })
  }
});

If the sequence of regex testing in your mapping is important, you can also specify a Map instead of an Object.

← Adapting EventsRetrieving Secrets →
  • Overview
  • The principles
  • Creating an adapter
    • Composition
    • Error handling
  • Using built-in adapter
    • Additional headers
    • Error handling
Laconia
Docs
Getting StartedCore ConceptsAPI Reference
Community
Stack OverflowChat
More
TwitterGitHubStar
Copyright © 2022 Wisen Tanasa
Logo designed by Suzie Nam