lambda

lihbr utils for Netlify

@lihbr/utils-netlify.lambda provides utils for Netlify Functions.

Features

Route requests easily
Rate-limit low-risk endpoints with no overhead
Send consistent responses

Installation

Add @lihbr/utils-netlify.lambda dependency to your project:

yarn add @lihbr/utils-netlify.lambda
npm install @lihbr/utils-netlify.lambda

That's it!

Usage

Just import the module inside your build script:

const lambda = require("@lihbr/utils-netlify.lambda");

Reference

HttpError(status)

status

  • Type: Number
  • Default: 500

A helper class to throw better HTTP error.

try {
  throw new HttpError(401);
} catch (error) {
  console.log(error.message); // Unauthorized
  console.log(error.status); // 401
}

request

route(event, context, callback, controller)

event

  • Netlify event object
  • Type: Object
  • required

context

  • Netlify context object
  • Type: Object
  • required

callback

  • Netlify callback function
  • Type: Function
  • required

controller

  • A controller object, see example
  • Type: Object
  • required

event, context, callback are provided by Netlify, the callback parameter is used to mitigate an inconsistency that happens when using an async handler directly (more info). However with route, your function handlers can still be asynchronous, no worries!

The route function takes care of routing incoming requests to appropriate handlers depending on the request method. This allows to easily have a CRUD capable function.

const lambda = require("@lihbr/utils-netlify.lambda");

const controller = {
  GET: async (event, context) => {
    const data = await getDummyRecords();

    return { statusCode: 200, body: data };
  },
  POST: async (event, context) => {
    await createDummyRecord(event.body);

    return { statusCode: 201, body: "" };
  }
}

exports.handler = (event, context, callback) =>
  lambda.request.route(event, context, callback, controller);

The above controller will handler GET and POST request. A 405 Method Not Allowed response will be returned if another method is used.

throttle(event, namespace, timeout)

event

  • Netlify event object
  • Type: Object
  • required

namespace

  • A namespace to use for throttling
  • Type: String
  • Default: default

timeout

  • Throttle timeout, seconds
  • Type: Number
  • Default: 60

The throttle function uses an in-memory rate-limiting method to provide a simple way to prevent spams on low-risk endpoints using the client IP address. The function throws an HttpError instance when throttling.

const lambda = require("@lihbr/utils-netlify.lambda");

const controller = {
  POST: async (event, context) => {
    try {
      lambda.request.throttle(event, "dummy", 20);
    } catch (err) {
      return { status: 429, body: "" }; // Too Many Requests
    }

    await createDummyRecord(event.body);

    return { statusCode: 201, body: "" };
  }
}

exports.handler = (event, context, callback) =>
  lambda.request.route(event, context, callback, controller);

The above will return a 429 Too Many Requests response if a client calls this function twice within a 20 seconds time frame.

response

The response object contains a lot of methods to send formatted responses.

raw

plain({ status = 200, body = "", headers = {} })

Returns a plain text response with given status and headers.

html({ status = 200, body = "", headers = {} })

Returns an HTML response with given status and headers.

json({ status = 200, body = {}, headers = {} })

Returns a JSON response with given status and headers, also takes care of turning the supplied body argument into JSON string.

const lambda = require("@lihbr/utils-netlify.lambda");

const controller = {
  GET: async (event, context) => {
    return lambda.response.raw.plain({
      status: 200,
      body = "Hello World",
      headers = { foo: "bar" }
    });
    /**
     * {
     *   statusCode: 200,
     *   body: "Hello World",
     *   headers: { foo: "bar" }
     * }
     */
  }
}

exports.handler = (event, context, callback) =>
  lambda.request.route(event, context, callback, controller);
const lambda = require("@lihbr/utils-netlify.lambda");

const controller = {
  GET: async (event, context) => {
    return lambda.response.raw.html({
      status: 401,
      body = "<div>Hello World</div>",
      headers = { foo: "bar" }
    });
    /**
     * {
     *   statusCode: 401,
     *   body: "Hello World",
     *   headers: { foo: "bar", "content-type": "text/html; charset=UTF-8" }
     * }
     */
  }
}

exports.handler = (event, context, callback) =>
  lambda.request.route(event, context, callback, controller);
const lambda = require("@lihbr/utils-netlify.lambda");

const controller = {
  GET: async (event, context) => {
    return lambda.response.raw.json({
      status: 404,
      body = { message: "Hello World" },
      headers = { foo: "bar" }
    });
    /**
     * {
     *   statusCode: 404,
     *   body: "{ \"message\": \"Hello World\" }",
     *   headers: { foo: "bar", "content-type": "application/json" }
     * }
     */
  }
}

exports.handler = (event, context, callback) =>
  lambda.request.route(event, context, callback, controller);

formatted

base({ status, msg = "", data = {}, error = {}, headers = {} })

Returns a Google style formatted JSON response. Low-level function used by the following success and error function.

success({ status, msg = "", data = {}, headers = {} })

Returns a Google style formatted JSON success response.

error({ status, msg = "", error = {}, headers = {} })

Returns a Google style formatted JSON error response.

In all functions, omitting msg will have it defaulting to the according status definition, see examples. Either way, msg will be transformed to lowercase.

const lambda = require("@lihbr/utils-netlify.lambda");

const controller = {
  GET: async (event, context) => {
    return lambda.response.formatted.success({
      status: 200,
      data: { message: "Hello World" }
      headers = { foo: "bar" }
    });
    /**
     * {
     *   statusCode: 200,
     *   body: "{
     *     \"status\": 200,
     *     \"msg\": \"ok\",
     *     \"data\": { \"message\": \"Hello World\" }
     *   }",
     *   headers: { foo: "bar", "content-type": "application/json" }
     * }
     */
  }
}

exports.handler = (event, context, callback) =>
  lambda.request.route(event, context, callback, controller);
const lambda = require("@lihbr/utils-netlify.lambda");

const controller = {
  GET: async (event, context) => {
    return lambda.response.formatted.error({
      status: 401,
      msg: "Not Connected"
      data: { message: "Oh No" }
      headers = { foo: "bar" }
    });
    /**
     * {
     *   statusCode: 401,
     *   body: "{
     *     \"status\": 401,
     *     \"msg\": \"not connected\",
     *     \"error\": { \"message\": \"Oh No\" }
     *   }",
     *   headers: { foo: "bar", "content-type": "application/json" }
     * }
     */
  }
}

exports.handler = (event, context, callback) =>
  lambda.request.route(event, context, callback, controller);

special

empty({ status = 200, headers = {} })

Returns an empty response with given status and headers.

redirect({ status = 302, href, headers = {} })

Redirect client to provided href location.

const lambda = require("@lihbr/utils-netlify.lambda");

const controller = {
  GET: async (event, context) => {
    return lambda.response.special.empty({
      status: 404,
      headers = { foo: "bar" }
    });
    /**
     * {
     *   statusCode: 404,
     *   body: "",
     *   headers: { foo: "bar" }
     * }
     */
  }
}

exports.handler = (event, context, callback) =>
  lambda.request.route(event, context, callback, controller);
const lambda = require("@lihbr/utils-netlify.lambda");

const controller = {
  GET: async (event, context) => {
    return lambda.response.special.redirect({
      status: 301,
      href = "https://example.com",
      headers = { foo: "bar" }
    });
    /**
     * {
     *   statusCode: 301,
     *   body: "",
     *   headers: { foo: "bar", "location": "https://example.com" }
     * }
     */
  }
}

exports.handler = (event, context, callback) =>
  lambda.request.route(event, context, callback, controller);
Edit this page on GitHub Updated at Thu, Apr 21, 2022