2.0.0 • Published 4 years ago

@yotie/micron v2.0.0

Weekly downloads
-
License
MIT
Repository
github
Last release
4 years ago

Writing production-ready lambda services can require quite a bit of boilerplate. micron is here to help improve that experience by providing powerful helpers that allow you to create expressive and hyper-composable serverless functions. This was designed to work seamlessly on Vercel.

Getting Started

Setup

Install the package

$ yarn add @yotie/micron

Create a simple lambda

import { micron } from '@yotie/micron';

export default micron(({ ok }: MicronParams) => {
  return ok({ success: true, hello: 'world' });
});

Example Usage with contrast

BEFORE MICRON

import checkAuth from './checkAuth';

export default function(req: Request, res: Response) {
  try {
    if (!req.method.toUpperCase().equals('POST'))
      return res.status(405).send('Method Unsupported');

    const auth = checkAuth(req.headers['Authorization']);
    if (!auth.isValid) return res.status(401).send('Unauthorized');

    console.log('Logged in with', auth.user);
    return res.status(200).json({
      success: true,
      user: auth.user,
      body: req.body
    });
  } catch(err) {
    return res.status(500).json({ success: false})
  }
}

WITH MICRON

import { createLambda, post } from '@yotie/micron';
import authMiddleWare from './auth';

export default createLambda(
  post(({ req, body, ok, error }) => {
      const { user } = req.auth;
      console.log('Logged in with', user);

      return ok({ success: true, body, user });
  }),
  { middlewares: [authMiddleWare, ...moreMiddleWare]}
);

micron improves the signal-to-noise ratio in your code which increases it readability, while reducing duplication and boilerplate.

API

Type: MicronParams

Vercel provides a useful list of helpers inside of the Request and Response objects passed to the lambda. We've enhanced the experience a bit more by including an additional set of helpers, making it accessible via the MicronParams which is passed on to your functions. While leveraging micron, your serverless functions will change from the default method signature:

(req: IncomingMessage, res: ServerResponse) => res: ServerResponse

to leveraging the MicronLambda function signature:

(params: MicronParams) => res: ServerResponse

Here is a complete list of all the properties contained in the MicronParams:

PropertyTypeDecription
reqRequestThe incoming Request object
resResponseThe outgoing Response object
bodyRequestBodyAn object containing the body sent by the request
cookiesRequestCookiesAn object containing the cookies sent by the request
queryRequestQueryAn object containing the request's query string
okResponseHelperReturns a 200 HTTP response with your payload
brotliResponseHelperReturns a 200 HTTP response with your payload compressed in br encoding
badRequestResponseHelperReturns a 400 HTTP response with your payload
unauthorizedResponseHelperReturns a 401 HTTP response with your payload
notFoundResponseHelperReturns a 404 HTTP response with your payload
errorResponseHelperReturns a 500 HTTP response with your payload

The different ResponseHelpers are simple functions that allow you to return a ServerResponse with a preconfigured http status code. Leveraging these functions help enhance the readability and maintainability of your serverless projects by cutting down on some of the bloat. Instead of doing...

return res.status(500).json({ message: 'Catastrophic Failure' });

...you can simplify your code to this:

return error({ message: 'Catastrophic Failure' });

These functions accept String, Array, Object, Buffer as valid inputs which will be passed on as the response body.

micron(fn)

The simplest way of creating a lambda is with the micron helper. This wraps your function in a global exception handler and will add light-weight request logging capabilites.

Usage

export default micron(({ req, ok }: MicronParams) => {
  return ok({
    success: true,
    requestType: req.method
  });
});

Note: lambdas created with the micron function accept requests from any HTTP method type. To restrict the HTTP method that your lambda allows, use one of the following: get, post, put, patch, del

get(fn)

Usage

import { get } from '@yotie/micron';

export default get(({ ok }) => {
  return ok({ success: true });
});

post(fn)

Usage

import { post } from '@yotie/micron';

export default post(({ body, ok }) => {
  return ok({ success: true, payload: body });
});

put(fn)

Usage

import { put } from '@yotie/micron';

export default put(({ ok }) => {
  return ok({ success: true });
});

patch(fn)

Usage

import { patch } from '@yotie/micron';

export default patch(({ ok }) => {
  return ok({ success: true });
});

del(fn)

Usage

import { del } from '@yotie/micron';

export default del(({ ok }) => {
  return ok({ success: true });
});

match({})

Usage

import { match } from '@yotie/micron';

export default match({
  async post({ body, ok, error }) {
    const user = await createUser(body);
    return ok(user);
  },
  async get({ query, ok, notFound }) {
    const user = await getUser(query.id);
    if(!user?.id) return notFound();

    return ok(user);
  }
})

createLambda(fn, opts)

Usage

import { get } from '@yotie/micron';
import { traceMiddleware, } from './middlewares';


export default createLambda(
  get(({ ok }) => {
    // some business logic here
    return ok({ success: true });
  }),
  {
    cors: { origin: 'https://example.com, http://localhost:3000' },
    middlewares: [traceMiddleware]
  }
);

Parameters

NameTypeDefault value
fnLambda-
optsLambdaOptionssee defaults for LambdaOptions

LambdaOptions

NameTypeDefault value
cors?CorsOptionssee defaults for CorsOptions
middlewares?MicronMiddleware[][]

CORS

CorsOptions

ParametertypedefaultDescription
originstring*
maxAgeNumber86400
allowMethodsstring[]GET, PUT, POST, PATCH, DELETE, OPTIONS
allowHeadersstring[] X-Requested-With, Access-Control-Allow-Origin, X-HTTP-Method-Override, Content-Type, Authorization, Accept
allowCredentialsBooleantrue
exposeHeadersstring[][]

Note: the origin can support multiple domains being set as well as glob patters


Middlewares

Middleware functions are functions that have access to the request object (req) and the response object (res) to perform some task before the main lambda executes.

  • Execute any code.
  • Make changes to the request and the response objects.
  • End the request-response cycle, example: return badRequest();

createMiddleware(fn, next)

Usage

import { createMiddleware } from '@yotie/micron';

export const auth = createMiddleware(({ req, unauthorized }, next) => {
  const token = req.headers['Authorization'];
  if (!token) return unauthorized();

  req.auth = { user: 'exampleUser' }
  console.log('User is allowed to access this lambda');

  return next();
});

You can also use the micron helper to build out your middlewares w/o using the createMiddleware helper.

Note: Middlewares must have the following signature fn => (req, res) => fn(req, res)

//auth.js

import { micron, NowLambda } from '@yotie/micron';
import { isValid } from './_utils';

const auth = (lambda: NowLambda) => {
  return micron({ req, res, body, unauthorized } => {
    const token = req.headers['Authorization'];

    if (!isValid(token)) return unautorized();

    console.log('User is allowed to access this lambda');
    return lambda(req, res);
  });
}

Testing

import { micron, mockLambda } from '@yotie/micron';

test('Successful api behaviour scenario', async () => {
  const lambda = micron(({ ok }) => ok({ success: true }));
  const { fetch } = await mockLambda(lambda);

  const res = await fetch('?q=searchQuery');
  const { success } = await res.json();

  expect(res.ok).toBe(true);
  expect(success).toBe(true);
});

TODO

  • Create Logo ✅
  • Create banner ✅
  • Documentation
    • Improve intro and Getting started ✅
    • Motivation and design principles 🚧
    • Complete list of helpers from MicronParams ✅
    • MicronHelpers and their scenarios 🚧
      • micron
      • get 🚧
      • put 🚧
      • post 🚧
      • del 🚧
      • match 🚧
    • Document createLambda and use cases 🚧
    • CORS and networking configuration ✅
    • Middlewares 🚧
      • flexibility of our middleware pattern ✅
    • Testing and Mocking
      • add query params serialization ✅
    • Contributing
  • Test more negative cases
  • Add file upload support
  • Split project into monorepo
    • micron
    • micron-mock
    • micron-vercel
    • micron-netlify
    • micron-middleware-auth0
    • micron-middleware-magiclink

Authors

  • Ashley Narcisse @darkfadr
  • Kennet Postigo @kennetpostigo