npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

micro-lambda-api

v1.2.4

Published

Micro framework for your lambda applications

Downloads

71

Readme

Micro Lambda API is a small library for AWS Lambda that provides an easy way to use routing with AWS API Gateway and AWS Application Load Balancer services for API.

That library has taken reference to some libraries such as:

  • Lambda API (https://github.com/jeremydaly/lambda-api)
  • Koa Router (https://github.com/ZijianHe/koa-router/)
  • AWS Lambda Router (https://github.com/art-dc/aws-lambda-router)

API Reference

About the library

This library aims to abstract the logic to perform REQUESTs and RESPONSEs in a simple and easy way that allows you to focus on the development of your lambda functionality. It has a LOGGER implementation using JSON to have a better traceability and control of events when it is sent to CloudWatch or another log monitoring platform.

Features

  • Integration with Gateway Lambda Proxy Integrator API using REST API or HTTP API.
  • Integration with ALB Lambda Target Support.
  • Enabling CORS for requests.
  • No external dependencies.
  • You can use the separate Request, Response and Logger.
  • The final middleware router runs after all routes and middleware have completed.
  • Creation of multiple routers with different versions.
  • Enable logger using process.stdout and process.stderr.
  • Typescript support.

Installation

Installation via npm

npm install micro-lambda-api

or yarn

yarn add micro-lambda-api

Getting Started

Using JavaScript

const { Api, ApiRouter, HttpStatus } = require("micro-lambda-api");

const api = new Api();
const router = new ApiRouter({
  basePath: "/users",
});

router.get("/", () => {
  return [];
});
router.post("/", (req, res) => {
  res.status(HttpStatus.NO_CONTENT).send("");
});
router.put("/:id", async (req) => {
  return { id: req.params.id };
});

api.use(router.routes());

exports.handler = async (event, context) => {
  return await api.listen(event, context);
};

Using TypeScript

import {
  Api,
  ApiRequest,
  ApiResponse,
  ApiRouter,
  HttpStatus,
} from "micro-lambda-api";

const api = new Api();
const router = new ApiRouter({
  basePath: "/users",
});

router.get("/", () => {
  return [];
});
router.post("/", (req: ApiRequest, res: ApiResponse) => {
  res.status(HttpStatus.NO_CONTENT).send("");
});
router.put("/:id", async (req: ApiRequest) => {
  return { id: req.params.id };
});

api.use(router.routes());

export async function handler(event: any, context: any) {
  return await api.listen(event, context);
}

Options

The api class has the following options:

| Option | Type | Description | | ------------------- | ------ | ------------------------------- | | logger (Optional) | object | Custom the configuration logger |

The logger option has the following options:

| Option | Type | Description | | -------------------- | -------- | ----------------------------------------------- | | trace (Optional) | boolean | Enable trace to request, response and errors | | pretty (Optional) | boolean | Enable the pretty format to logger. | | handler (Optional) | Function | Method that returns the response of each logger |

Example:

const api = new Api({
  logger: {
    trace: true,
    pretty: false,
    handler: (log) => {
      // process or transform the log
    }
  }
});

Router

Class that defines the different HTTP methods

Basic use:

import { Api, ApiRouter } from "micro-lambda-api";

const api = new Api();
const router = new ApiRouter();

router.get("/", (req, res) => {
  // Process and return data
});

app.use(router.routes()).use(router.middlewares());

Options

| Parameer | Type | Description | | --------------------- | ------ | ---------------------------------- | | basePath (Optional) | String | Prefix of all routes | | version (Optional) | String | Router version number. Example: v2 |

Examples:

const router = new ApiRouter({
  basePath: "/users",
});
const router = new ApiRouter({
  version: "v2",
});
const router = new ApiRouter({
  basePath: "/users",
  version: "v3",
});

Methods ⇒ ApiRouter

Router defines the following 5 methods: get, post, patch, put, delete

router
  .get("/users", async (req, res) => {
    const users = await getUsers();

    return users;
  })
  .post("/users", async (req, res) => {
    await saveUsers();

    return true;
  })
  .patch("/users/:id", async (req, res) => {
    const user = await updateStatus(req.param.id, true);

    return user;
  })
  .put("/users/:id", async (req, res) => {
    const user = await updateUser(user);

    return user;
  })
  .delete("/users/:id", async (req, res) => {
    await deleteUser(req.param.id);

    return true;
  });

When a route is not found it will return the following error: RouteError.

When a method is not found it will return the following error: MethodError.

Middleware => ApiRouter

Middlewares are functions that allow them to be executed before any route.

Example:

router.use((req, res) => {
  req.executionStart = performance.now();
});

Multiple middlewares can be nested

router
  .use((req, res) => {
    // Middleware 1
  })
  .use((req, res) => {
    // Middleware 2
  })
  .use((req, res) => {
    // Middleware 3
  });

NOTE: Remember that middlewares run according to the order that were created.

Router prefixes

When a basePath is defined, a prefix is added to all routes that you define.

Example:

const router = new ApiRouter({ basePath: "/users" });

router
  .get("/", ...) // responds to /users
  .put("/:id", ...) // responds to /users/100
  .post("/:id/save", ...) // responds to /users/100/save

Router responses

There are 2 ways to respond to the execution of a route

Using the response class:

router.get("/users", (req, res) => {
  res.send([
    { id: 1, username: "admin" },
    { id: 2, username: "demo" },
  ]);
});

Using return:

router.get("/users", (req, res) => {
  return [
    { id: 1, username: "admin" },
    { id: 2, username: "demo" },
  ];
});

The response class provides more functionality such as, for example, changing the response status, adding custom headers, enabling cors, and so on

You can also combine the use of response with return:

router.get("/users", (req, res) => {
  res.status(201);
  res.cors();
  res.header("x-custom-header", "my-header");

  return [
    { id: 1, username: "admin" },
    { id: 2, username: "demo" },
  ];
});

Functions

routes()

Returns an array with the routes that were defined.

router.routes();

middlewares()

Returns an array with the middlewares that were defined.

router.middlewares();

Static

getRouteParams(route, path)

A method that is not part of the Router class and is used to get the parameters of a URL.

| Parameter | Type | Description | | --------- | ------ | ------------------------------------ | | route | String | Route with dynamic parameters | | path | String | The url that was invoked by the user |

Example:

const params = getRouteParams("/users/:id/:username", "/users/1/admin");
// return { id: 1, username: "admin" }

Socket

Class that defines the different Actions to Api Gateway WebSocket.

Basic use:

import { Api, ApiSocket } from "micro-lambda-api";

const api = new Api();
const socket = new ApiSocket();

socket.action("message", (req, res) => {
  // Process and return data
});

app.use(socket.actions()).use(socket.middlewares());

Example: Sending a message from the Server to the Application

import { ApiGatewayManagementApi } from "aws-sdk";

socket
  .action("message", async (req, res) => {
    const connectionId = req.connectionId || "";
    const apiWs = new ApiGatewayManagementApi({
      apiVersion: "2018-11-29",
      endpoint: `https://${req.host}/${req.stage}`,
    });

    await apiWs
      .postToConnection({
        ConnectionId: connectionId,
        Data: // My Custom Data in JS Object,
      })
      .promise();

    res.send("Message sent!!!");
  });

Methods ⇒ ApiSocket

Socket defines the following 3 methods: connect, disconnect, action

socket
  .connect(async (req, res) => {
    await suscribe();

    res.send("Connect!!!");
  })
  .disconnect(async (req, res) => {
    await unsuscribe();
    
    res.send("Disconnect!!!");
  })
  .action("message", (req, res) => {
    return "message";
  })
  .action("register", async (req, res) => {
    const user = await register(req.body);

    return user;
  })

When an action is not found it will return the following error: ActionError.

Middleware => ApiSocket

Middlewares are functions that allow them to be executed before any action.

Example:

socket.use((req, res) => {
  req.executionStart = performance.now();
});

Multiple middlewares can be nested

socket
  .use((req, res) => {
    // Middleware 1
  })
  .use((req, res) => {
    // Middleware 2
  })
  .use((req, res) => {
    // Middleware 3
  });

NOTE: Remember that middlewares run according to the order that were created.

Action responses

There are 2 ways to respond to the execution of a action

Using the response class:

socket.action("message", (req, res) => {
  res.send([
    { id: 1, username: "admin" },
    { id: 2, username: "demo" },
  ]);
});

Using return:

socket.action("register", (req, res) => {
  return [
    { id: 1, username: "admin" },
    { id: 2, username: "demo" },
  ];
});

The response class provides more functionality such as, for example, changing the response status, adding custom headers, enabling cors, and so on

You can also combine the use of response with return:

socket.action("message", (req, res) => {
  res.status(201);
  res.cors();
  res.header("x-custom-header", "my-header");

  return [
    { id: 1, username: "admin" },
    { id: 2, username: "demo" },
  ];
});

Request

The request class allows you to parse the event that was invoked through an API Gateway or Application Load Balancer.

So you don't need to use all the functionality of the paths, api, errors, etc., you can use it in your Lambda handler function.

Example:

exports.handler = (event, context) => {
  const request = new ApiRequest(event);

  console.log(request.query);
  console.log(request.params);
  console.log(request.body);
};

NOTE: Remember that while the Request class is independent it uses some functions that are within utils and http.

Properties

  1. id

    Transaction ID Example: dc6128b3-bdcb-464d-a1e1-009b041ff9b9

  2. stage

    If you are working with API Gateway Stages, it will contain a value. Example: develop.

    If you set no stage in the Gateway API by default it will be empty.

  3. method

    Identifies the HTTP Method. Possible values: GET, PUT, POST, etc.

  4. path

    Identify the URL. Example: /users.

  5. query

    Returns in an object all parameters that were passed in the URL query. Example: {id: 100}

  6. params

    By default, return an empty object. If you do not work with the API class to be able to assign it a value must use the function: getRouteParams(route, path).

  7. headers()

    Returns an array of all headers in the request. Header names will be lowercase.

  8. body

    Returns an object with all the parameters returned in the request body. Only works with content-type application/json or application/x-www-form-urlencoded.

    NOTE: Doesn't support binary content. Example: files.

  9. host

    Returns the host that invoked the request.

    If ALB, the DNS will return.

    If API Gateway will return the domain.

  10. ip

    Returns the first ip found in the request.

  11. userAgent

    Returns the User-Agent sent in the header.

  12. proxyIntegration

    Identifies what integration your Lambda has.

    The values to return are: elb or apigw-rest-api or apigw-http-api.

  13. isBase64Encoded

    A Boolean value that identifies whether the body is in base64

  14. log

    Property that allows you to manage the log on each request.

  15. connectionId

    Property that returns the id of the socket connection. This value can be nullable

  16. route

    property that returns the router key to be used with the ApiSocket.

You can add additional parameters if you are working with ApiRouter or ApiSocket this will allow you to have more customization when you are using middlewares.

Response

The response class allows you to configure and return the response that Lambda will return to the Gateway API or Application Load Balancer.

If you don't need to use all the functionality of your paths, api, errors, etc., you can use it in your Lambda handler function.

Example:

exports.handler = (event, context) => {
  ...
  const response = new ApiResponse();

  return response
    .status(200)
    .send({
      id: 1,
      name: "admin",
      status: true
    });
}

Options

Options are only available if you use without Api and Router.

| Option | Type | Description | | ------------- | ------- | ------------------------------------------------------------------ | | cors | boolean | Static value that enable the headers cors in all responses. | | credentials | boolean | Static value that enable the headers credentials in all responses. | | request | object | Parameter optional in the initialization the class. |

Example:

exports.handler = (event, context) => {
  const request = new ApiRequest(event, context);
  ...
  ApiResponse.cors = true;
  ApiResponse.credentials = true;

  const response = new ApiResponse(request);

  response.status(200);
  response.header("my-custom-header", "my-header");

  return response
    .send({
      id: 1,
      name: "admin",
      status: true
    });
}

Methods

status(code)ApiResponse

Sets the status of the response that was returned to the Gateway API or ALB. By default, the response status is 200 and 500 for a general error.

Status 405 is used when the method does not exist and 404 when the route is not found.

For better control you can use the HttpStatus object which has a listing of all http codes that you can return.

Example:

router.get("/", (req, res) => {
  res.status(HttpStatus.OK).send(true);
});

header(key, value)ApiResponse

Use the header function if you want to add custom header that They will be returned to the Gateway API or ALB.

Remember that all keys will be converted to lowercase.

Example:

router.get("/", (req, res) => {
  res.header("X-Custom-Header", "my-custom-header");
  // return to "x-custom-header" in lowercase
  ...
})

cors(options)ApiResponse

Allows you to add custom cors for each request. If you want to work with the cors by default, use the static value cors.

Example:

router.get("/", (req, res) => {
  res.cors({
    credentials: true,
    origin: "https://mydomain.com",
  });
  ...
})

toResponse()

Returns the previous object that will be returned.

Structure:

interface Response {
  isBase64Encoded: boolean;
  statusCode: number;
  statusDescription?: string;
  body: string;
  headers: {
    [key: string]: string | undefined;
  };
}

toResponseError()

Returns the previous object that will be returned.

Structure:

export interface ResponseError {
  status?: HttpStatus;
  code: string;
  message: string;
  data?: {
    [key: string]: unknown;
  };
}

send(payload, isError)

Allows you to send a json object or string that will be transformed to a string that is the data that API Gateway or ALB expects to receive.

The parameter isError is optional is used only by the method error.

res.send("Hola Mundo");

// Return
{
  isBase64Encoded: false;
  statusCode: 200;
  body: "Hola Mundo";
  headers: {
    ...
  };
}

Or

res.send({
  id: 1,
  name: "admin",
  status: true
});

// Return
{
  isBase64Encoded: false;
  statusCode: 200;
  body: "{'user': 1, 'name': 'admin', 'status': true}";
  headers: {
    ...
  };
}

json(body)

A metode that defaults to the conten-type header in application/json And that receives as a parameter a JSON.

res.json({
  id: 1,
  name: "admin",
  status: true,
});

html(body)

A metode that defaults to the conten-type header in text/html; charset=UTF-8 And that receives as a parameter a String.

res.json("<h1>Hello World</h1>");

file(body)

A method that defaults to the conten-type header in tapplication/octet-stream And that receives as a parameter a String.

res.json("This is a file");

error(data)

A metode that receives as a parameter an object with a structure Predefined (ResponseError) that will be sent to the send method

Structure:

interface ResponseError {
  status?: HttpStatus;
  code: string;
  message: string;
  data?: {
    [key: string]: unknown;
  };
}

Remember that if you use the response object only, you must assign the status of the response.

Example:

res.status(400);
res.error(...);

You can use a throw to generate an exception in your route and be captured by the global error handler.

Example:

router.get("/", () => {
  throw Error("Unknown error");
});

// Return
{
  status: 500,
  code: "GENERIC_ERROR",
  message: "Unknown Error",
}

All errors extend from the HttpError class and this In turn of the generic class Error.

class HttpError extends Error {
  constructor(public code: string, public message: string) {
    super(message);
  }
}

finally

An api functionality is 'finally' which is a middleware that is invoked at the end of all executions (paths and middlewares) used if you want to clean variables, connect to database, perform tracing, and so on.

api.finally((req, res) => {
  ...
});

Logger

The logger class allows you to centralize logs in your Lambda using process.stdout or process.stderr.

This component can be used independently.

Example:

const log = Logger.create();

log.trace("Log trace");
log.debug("Log debug");
log.info("Log info");
log.warn("Log warn");
log.error("Log error");
log.fatal("Log fatal");

/*
{"id":"ca4d341d-2c86-41a1-989b-9cd3b25ea05e","level":"trace","time":1612848015979,"timeStamp":"2021-02-09T05:20:15.979Z","message":"Log trace","lambda":{"name":"myFunction","version":"1.0.0","memoryLimitInMB":1024,"arn":"my:arn:myFunction"}}
*/

Configuration

| Option | Type | Description | | -------------------- | -------- | ------------------------------------------------ | | context (Optional) | boolean | Object containing the request id and lambda data | | pretty (Optional) | boolean | Enable the pretty format to logger. | | handler (Optional) | Function | Method that returns the response of each logger |

Example:

Logger.configure({
  context: { ... },
  pretty: false,
  handler: (log) => { ... },
})
const log = Logger.create();

toLog()

Method that returns a json object with the basic structure of the log:

interface Log {
  id: string;
  level: string;
  group?: string;
  message: string;
  data?: unknown;
  time: number;
  timeStamp: string;
  lambda?: LogLambda;
  [key: string]: any;
}

| Option | Type | Description | |-------------------|--------|------------------------------------------------------------| | id | string | Request ID in format UUID V4 | | level | string | Logger level. Example: info, trace, error, etc. | | group (Optional) | string | Grouping logs | | message | string | Log message | | data (Optional) | object | Additional information that can be shared to the log | | time | number | The datetime in long | | timeStamp | string | The datetime in ISO String | | lambda (Optional) | string | Information about lambda. Example: name, memory, arn, etc. | | [key: string] | any | any additional parameters |

Grouping

To group logs you should use the group(name) function

const log = Logger.create();
const grupo1 = Logger.group("group1");
const grupo2 = Logger.group("group2");

grupo1.trace("Log trace");
grupo2.debug("Log debug");

Extras

If you want to add additional parameters when displaying the log.

const log = Logger.create();

log.addExtra("request", {...});
log.getExtra("request");
log.removeExtra("request");
log.clearExtras();

Pretty format

By default all the logs print in json format, enable the option pretty in true is posible return the logs in text.

Example:

Logger.configure({ pretty: true });

const log = Logger.create();

log.trace("This is a log");

// Return => [2021-02-09T05:20:15.985Z] [TRACE] - This is a log.

Contributing

Please read CONTRIBUTING.md for details on our code of conduct, and the process for submitting pull requests to us.

Versioning

The repository use SemVer for versioning. For the versions available, see the tags.

Authors

License

This project is licensed under the MIT License - see the LICENSE file for details.

Support

If you have any problem or suggestion please open an issue here.