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

@politico/lambda

v1.0.9

Published

A utility library to simplify setting up lambda functions

Downloads

110

Readme

@politico/lambda

This library is meant to make it easier to spin up multi-use Lambda microservices integrated in an API Gateway. While it can be desireable to break Lambda functions down into their smallest possible forms, it's often more convenient to create Lambdas that do multiple things. For example, you might want to build a Lambda that reads from and writes to a database. Your project might even have several types of records and it's simplest to have one Lambda that handles CRUD operations for all of them. If you're used to something like Express, you may think about solving this problem by routing requests to different handler functions, but integrating Express in a Lambda environment can be a hassle. This library seeks to provide an Express-like routing experience while staying true to the simplicity of a Lambda environment.

Routing

In a simple case, setting up a Lambda router might look something like this:

import { createRouter, log, ok } from '@politico/lambda';

const router = createRouter({ log });

router.get('/hello/world', {
  handler: () => ok({
    response: {
      message: 'Hello world!',
    },
  }),
});

export const handler = (event) => router.handleLambdaApiGateway(event);

Here we're:

  1. creating a router that's set up to log requests to stdout,
  2. registering a route handler to respond to GET requests made to the /hello/world endpoint, and
  3. exporting a function called handler that can operate as the entrypoint for a Lambda function.

With that, you should be able to create your Lambda, connect it as an integration for an API Gateway, and ping your endpoint. Note that setting up logging is optional. If you don't pass in a log object then logging will be disabled, or you can pass in your own custom logger as long as it has a logLevel function that accepts a log level and additional arguments to log.

Adding Auth

This library supports token-based authentication out of the box, relying on an external service acting as the token authority. To configure your router with authentication, you can provide auth options like this:

import { createRouter, log } from '@politico/lambda';

const router = createRouter({
  logger: log,
  auth: {
    appName: 'my-app-name',
    secret: 'ssssshhhhh',
    signinUrl: 'https://auth.com/signin',
    roles: [
      'user',
      'admin',
    ],
  },
});

This then lets you configure routes with minimum roles set, like so:

router.get('/hello/world', {
  handler: myHandler,
  minimumRole: 'admin',
});

Now only users with the admin role can access this endpoint!

By how does the router know which roles a user has, or even who the user is? That's where the external token service comes in. The router will look for a user's token either as a bearer token in an Authorization header or as a newsroomAuthToken cookie. If it finds a token, it tries to parse it as a JWT using the provided secret. If the token is expired, it tries to refresh it by sending it to the token service at the provided signinUrl. If the token can be neither verified nor refreshed, authentication fails.

The router assumes that the payload of the token will contain information about the user as well as a set of permissions keyed by app name. The router will attempt to look up app-specific permissions using the provided appName as the key of the permissions object. A user may have multiple permissions (or "roles") for the same app.

Once a user's roles are established for the specified app, the router can check their authorization when attempting to access specific endpoints. This is where the router's roles configuration comes into play. This array of roles specifies two things: the set of roles supported by this app, and the relative "level" of each role. A user will be able to access an endpoint with a configured minimumRole if either (1) they have the exact specified role, or (2) they have at least one role of a strictly greater level than the level of the configured minimum role for that endpoint.

There are two ways you can specify the roles supported by a router. First, you can be fully explicit and specify each role's name and level:

import { createRouter, log } from '@politico/lambda';

const router = createRouter({
  logger: log,
  auth: {
    appName: 'my-app-name',
    secret: 'ssssshhhhh',
    signinUrl: 'https://auth.com/signin',
    roles: [
      { name: 'user-type-1', level: 0 },
      { name: 'user-type-2', level: 0 },
      { name: 'admin', level: 1 },
    ],
  },
});

This can be helpful for more complex role schemes. Notice that in the above example, both roles user-type-1 and user-type-2 have the level of 0 while admin has a level of 1. This means that if an endpoint has a minimum role of user-type-1, users with only the role user-type-2 won't be able to access it, and vice versa. Users with the admin role will be able to access both because the level of admin is greater than the levels of the user roles.

Often, an app's role scheme is more simply hierarchical and there are several increasing levels of access. As a convenience, you can specify roles as a simple array of names in this case:

import { createRouter, log } from '@politico/lambda';

const router = createRouter({
  logger: log,
  auth: {
    appName: 'my-app-name',
    secret: 'ssssshhhhh',
    signinUrl: 'https://auth.com/signin',
    roles: [
      'user',
      'admin',
    ],
  },
});

Here, the level of each role is assumed to be its index in the array of roles, so user has a level of 0 and admin has a role of 1.

Integrating with API Gateway

A common and convenient way to deploy Lambda microservices is as integrations behind an API Gateway that handles routing to multiple services. When using path-based routing within a Lambda microservice, this can create some complications, because whatever path prefix the API Gateway uses to route the request to the appropriate Lambda is included in the request event that gets passed to the Lambda.

The router supports removing a path prefix for its internal routing purposes automatically. To configure this, set the servicePathPrefix option:

import { createRouter, log, ok } from '@politico/lambda';

const router = createRouter({
  log,
  servicePathPrefix: '/service/my-service',
});

Now, any request that comes in will get /service/my-service stripped off of the start of the request path. Note that the router does not enforce that requests match this prefix; it simply removes the prefix if it's there.

Other Utilities

This library is built to make creating Lambda microservices easier, so it includes a few additional helpers.

  • loadEnvValue helps load values from the environment that will be type-safe as strings
  • ok and error help format Lambda handler responses
  • runDevServer runs an Express server that mimics an API Gateway integration for local development (note that this shouldn't be used in production)