https://github.com/juliantellez/lambcycle
🐑🛵 A declarative lambda middleware with life cycle hooks 🐑🛵
https://github.com/juliantellez/lambcycle
aws aws-lambda lambda lambda-functions lambda-handler lifecycle middleware serverless
Last synced: 2 months ago
JSON representation
🐑🛵 A declarative lambda middleware with life cycle hooks 🐑🛵
- Host: GitHub
- URL: https://github.com/juliantellez/lambcycle
- Owner: juliantellez
- License: mit
- Created: 2019-01-02T05:29:27.000Z (over 6 years ago)
- Default Branch: develop
- Last Pushed: 2021-05-07T17:55:53.000Z (about 4 years ago)
- Last Synced: 2025-04-16T03:18:00.530Z (2 months ago)
- Topics: aws, aws-lambda, lambda, lambda-functions, lambda-handler, lifecycle, middleware, serverless
- Language: TypeScript
- Homepage:
- Size: 1.34 MB
- Stars: 94
- Watchers: 2
- Forks: 3
- Open Issues: 11
-
Metadata Files:
- Readme: readme.md
- Contributing: contributing.md
- License: LICENSE
- Code of conduct: CODE_OF_CONDUCT.md
Awesome Lists containing this project
README
Lambcycle is a declarative lambda middleware. Its main purpose is to let you focus on the specifics of your application by providing a configuration cycle.
🐑🛵 Read the introductory blog post here 🐑🛵.- [Install](#install)
- [Introduction](#Introduction)
- [Handler Lifecycle](#Handler-lifecycle)
- [Error Handling](#Error-handling)
- [Plugins](#plugins)
- [Creating a Plugin](#creating-a-plugin)
- [Using a Plugin](#using-a-plugin)
- [DevX](#devx)
- [About the project](#about-the-project)
- [Contributing](#contributing)
- [License](#license)# Install
```bash
# with npm
npm install --save lambcycle# with yarn
yarn add lambcycle
```# Introduction
Lambcycle is a middleware for lambda functions. It defines a configurable life-cycle and allows you to focus on your application's logic. It has a "Feature as Plugin" approach, so you can easily create your own plugins or reuse your favorite packages with very little effort 🐑 🛵.
Checkout the following example or follow the link to
[🎉 see some actual code 🎉 ](https://github.com/juliantellez/lambcycle/tree/develop/examples).```javascript
// with es6import Joi from "joi";
import lambcycle from "lambcycle";import pinoPlugin from './myPinoPlugin'
import joiPlugin from './myJoiPlugin'
import bodyParserPlugin from './myBodyParserPlugin'import applicationLogic from "./mycode";
const processData = async (event, context) => {
// beautiful application logic ...const manipulateData = event => {
// ...
};return await applicationLogic(manipulateData(event), context);
};const schema = Joi.object()
.keys({
username: Joi.string().alphanum().min(5).required(),
password: Joi.string().regex(/^[a-zA-Z0-9]{5,30}$/),
email: Joi.string().email({ minDomainAtoms: 2 })
});const handler = lambcycle(processData).register([
pinoPlugin,
bodyParserPlugin,
joiPlugin(schema)
]);export default handler;
```# Handler lifecycle
The lifecycle provides a clear guideline to reason about your needs. Every step of the cycle can handle or throw errors making it easy to log, report or debug.
![]()
Lambcycle enhances lambda functions with a few extension points (see graph), each of which can be used to interact with the event in a decomposed manner.
- The first extension point is `Request` which occurs immediately after the lambda is called. You can use this step for parsing, validation, etc...
Note: If you are thinking of auth, please consider a [lambda authoriser](https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-use-lambda-authorizer.html) instead.- The `Pre Handler` extension comes in handy when you need to adapt data to fit an interface. It is also a great place for fetching secrets.
- The `Handler`, where your beautiful business logic lives.
- Next up is the `Post Handler`, use this extension to validate and/or cache the output of your business logic.
- `Error` is an implicit extension for logging and tracing.
- And finally `Pre Response`, your chance to format a response to the consumer (which could be data or an error).
# Error Handling
The error object is a first class citizen that will stop the cycle and execute any error plugins declared in the register, it will then proceed to call the lambda handler's callback.
Have a look at the [Wrapper Interface](https://github.com/juliantellez/lambcycle/blob/master/src/Interfaces/IWrapper.ts) to see what's available for reporting.`HINT: pretty much everything.`
```javascript
import lambcycle from 'lambcycle'
import notifyError from './myErrorNofifier'const appLogic = async(event, context) => {
const {error, data} = await amazingJob()
if(error) {
throw error
}
}const errorNotifier = {
plugin: {
onError: async (handler) => {
/**
* See IWrapper interface
*/
await notifyError(handler.error)
}
}
}const handler = lambcycle(appLogic).register([errorNotifier])
export default handler;
```# Plugins
- [BodyParser](https://github.com/juliantellez/lambcycle/tree/master/src/Plugins/BodyParser): Parse incoming request bodies before your handler, available under the `handler.event.body` property.
- [Joi](https://github.com/juliantellez/lambcycle/tree/master/src/Plugins/Joi): Object schema description language and validator for JavaScript objects. Validate requests without the pain!# Creating a plugin
A plugin is an object that can attach its hooks to one or more event cycles, it may provide its own configuration object.
```typescript
type IPluginHookFunction = (
wrapper: IWrapper,
config: object,
handleError?: Callback
) => void;
``````typescript
import * as Sentry from '@sentry/node';
import MyAwesomeIntegration from './MyAwesomeIntegration'const sentryPlugin = (config) => {
Sentry.init({
dsn: `https://[email protected]/${config.project}`,
integrations: [new MyAwesomeIntegration()]
});return {
config,
plugin: {
onPreResponse: async (handlerWrapper, config) => {
Sentry.captureMessage('some percentile log perhaps?')
},
onError: async (handlerWrapper, config) => {
Sentry.captureException(handlerWrapper.error);
}
}
}
}export default sentryPlugin;
```# Using a plugin
Let's reuse the example above. Make sure your lambdas follow the [Principle of least privilege](https://en.wikipedia.org/wiki/Principle_of_least_privilege) and your secrets stay SECRET ㊙️
```typescript
import lambcycle from 'lambcycle'
import sentryPlugin from './sentryPlugin'const myApplicationLogic = async (event, context) => {
await someLogic()
}const handler = lambcycle(myApplicationLogic)
.register([
sentryPlugin({
key: process.env.SENTRY_KEY,
project: process.env.SENTRY_PROJECT,
})
]);export default handler;
```# DevX
Lambcycle ships with type definitions, making the dev experience smoother 🚀 (VScode only).

# About the project
This project has been built with lots of ❤️ and [Typescript](https://www.typescriptlang.org) 🤣. It embraces the middleware pattern and uses types for consistency and documentation. If this approach seems familiar to you is because it was inspired by the awesome [hapijs](https://hapijs.com/api#request-lifecycle).
# Contributing
As you can see the possibilities are endless when it comes to plugins! Everyone is welcome to [contribute](https://github.com/juliantellez/lambcycle/blob/develop/contributing.md)! Feel free to create [issues](https://github.com/juliantellez/labmcycle/issues) or [prs](https://github.com/juliantellez/labmcycle/pulls).# License
[MIT License](https://github.com/juliantellez/lambcycle/blob/master/LICENSE)