{"id":25653119,"url":"https://github.com/juliantellez/lambcycle","last_synced_at":"2025-06-23T10:33:13.219Z","repository":{"id":52080280,"uuid":"163797801","full_name":"juliantellez/lambcycle","owner":"juliantellez","description":"🐑🛵  A declarative lambda middleware with life cycle hooks   🐑🛵","archived":false,"fork":false,"pushed_at":"2021-05-07T17:55:53.000Z","size":1405,"stargazers_count":94,"open_issues_count":11,"forks_count":3,"subscribers_count":2,"default_branch":"develop","last_synced_at":"2025-05-17T19:17:36.708Z","etag":null,"topics":["aws","aws-lambda","lambda","lambda-functions","lambda-handler","lifecycle","middleware","serverless"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/juliantellez.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":"contributing.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-01-02T05:29:27.000Z","updated_at":"2024-01-13T23:59:19.000Z","dependencies_parsed_at":"2022-09-07T04:40:48.449Z","dependency_job_id":null,"html_url":"https://github.com/juliantellez/lambcycle","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/juliantellez/lambcycle","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/juliantellez%2Flambcycle","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/juliantellez%2Flambcycle/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/juliantellez%2Flambcycle/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/juliantellez%2Flambcycle/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/juliantellez","download_url":"https://codeload.github.com/juliantellez/lambcycle/tar.gz/refs/heads/develop","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/juliantellez%2Flambcycle/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":260690134,"owners_count":23047052,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aws","aws-lambda","lambda","lambda-functions","lambda-handler","lifecycle","middleware","serverless"],"created_at":"2025-02-23T19:30:56.249Z","updated_at":"2025-06-23T10:33:08.199Z","avatar_url":"https://github.com/juliantellez.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/juliantellez/lambcycle\" target=\"_blank\"\u003e\n    \u003cimg alt=\"lambcycle\" src=\"https://raw.githubusercontent.com/juliantellez/lambcycle/master/assets/lambcycle-logo.svg?sanitize=true\" width=\"144\"\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  Lambcycle is a declarative \u003ca href=\"https://aws.amazon.com/lambda/\" target=\"_blank\"\u003elambda\u003c/a\u003e middleware. Its main purpose is to let you focus on the specifics of your application by providing a configuration cycle.\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  🐑🛵 Read the introductory blog post \u003ca href=\"https://medium.com/dazn-tech/handling-complexity-in-lambda-functions-e7acfbeb920a\" target=\"_blank\"\u003ehere\u003c/a\u003e 🐑🛵.\n\u003c/p\u003e\n\n\u003c!---links---\u003e\n\n\u003cp align=\"center\"\u003e\n\u003c!---npm---\u003e\n\u003ca href=\"https://www.npmjs.com/package/lambcycle\" target=\"_blank\"\u003e\n    \u003cimg src=\"https://img.shields.io/npm/v/lambcycle.svg?style=flat-square\" style=\"padding:3px\"\u003e\n\u003c/a\u003e\n\n\u003c!---npm downloads---\u003e\n\u003ca href=\"https://npmcharts.com/compare/lambcycle?minimal=true\" target=\"_blank\"\u003e\n    \u003cimg src=\"https://img.shields.io/npm/dm/lambcycle.svg?style=flat-square\" style=\"padding:3px\"\u003e\n\u003c/a\u003e\n\n\u003c!---travis master build---\u003e\n\u003ca href=\"https://travis-ci.org/juliantellez/lambcycle/\" target=\"_blank\"\u003e\n    \u003cimg src=\"https://img.shields.io/travis/juliantellez/lambcycle/master.svg?style=flat-square\" style=\"padding:3px\"\u003e\n\u003c/a\u003e\n\n\u003c!---install size---\u003e\n\u003ca href=\"https://packagephobia.now.sh/result?p=lambcycle\" target=\"_blank\"\u003e\n    \u003cimg src=\"https://packagephobia.now.sh/badge?p=lambcycle\"style=\"padding:3px\"\u003e\n\u003c/a\u003e\n\n\u003c!---npm dependencies---\u003e\n\u003ca href=\"https://david-dm.org/juliantellez/lambcycle\" target=\"_blank\"\u003e\n    \u003cimg src=\"https://david-dm.org/juliantellez/lambcycle/status.svg\" style=\"padding:3px\"\u003e\n\u003c/a\u003e\n\n\u003c!---npm dev-dependencies---\u003e\n\u003ca href=\"https://david-dm.org/juliantellez/lambcycle?type=dev\" target=\"_blank\"\u003e\n    \u003cimg src=\"https://david-dm.org/juliantellez/lambcycle/dev-status.svg\" style=\"padding:3px\"\u003e\n\u003c/a\u003e\n\n\u003c!---coveralls---\u003e\n\u003ca href=\"https://coveralls.io/github/juliantellez/lambcycle\" target=\"_blank\"\u003e\n    \u003cimg src=\"https://coveralls.io/repos/github/juliantellez/lambcycle/badge.svg?branch=master\" style=\"padding:3px\"\u003e\n\u003c/a\u003e\n\n\u003c!---npm dependency updates---\u003e\n\u003ca href=\"https://snyk.io/test/github/juliantellez/lambcycle?targetFile=package.json\" target=\"_blank\"\u003e\n    \u003cimg src=\"https://snyk.io/test/github/juliantellez/lambcycle/badge.svg?targetFile=package.json\" alt=\"Known Vulnerabilities\" style=\"max-width:100%; padding:3px;\"\u003e\n\u003c/a\u003e\n\n\u003c!---serverless---\u003e\n\u003ca href=\"http://www.serverless.com\" target=\"_blank\"\u003e\n    \u003cimg src=\"http://public.serverless.com/badges/v3.svg\" alt=\"Serverless\" style=\"max-width:100%; padding:3px;\"\u003e\n\u003c/a\u003e\n\n\u003c!---MIT License---\u003e\n\u003ca href=\"https://opensource.org/licenses/MIT\" target=\"_blank\"\u003e\n    \u003cimg src=\"http://img.shields.io/badge/license-MIT-blue.svg?style=flat\" alt=\"MIT License\" style=\"max-width:100%; padding:3px;\"\u003e\n\u003c/a\u003e\n\n\u003c!---FOSSA---\u003e\n\u003ca href=\"https://app.fossa.io/projects/git%2Bgithub.com%2Fjuliantellez%2Flambcycle?ref=badge_shield\" target=\"_blank\"\u003e\n    \u003cimg src=\"https://app.fossa.io/api/projects/git%2Bgithub.com%2Fjuliantellez%2Flambcycle.svg?type=shield\" alt=\"FOSSA Status\" style=\"max-width:100%; padding:3px;\"\u003e\n\u003c/a\u003e\n\u003c/p\u003e\n\n - [Install](#install)\n - [Introduction](#Introduction)\n - [Handler Lifecycle](#Handler-lifecycle)\n - [Error Handling](#Error-handling)\n - [Plugins](#plugins)\n - [Creating a Plugin](#creating-a-plugin)\n - [Using a Plugin](#using-a-plugin)\n - [DevX](#devx)\n - [About the project](#about-the-project)\n - [Contributing](#contributing)\n - [License](#license)\n\n# Install\n\n```bash\n# with npm\nnpm install --save lambcycle\n\n# with yarn\nyarn add lambcycle\n```\n\n# Introduction\n\nLambcycle 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 🐑 🛵.\n\nCheckout the following example or follow the link to\n[🎉 see some actual code 🎉 ](https://github.com/juliantellez/lambcycle/tree/develop/examples).\n\n\n```javascript\n// with es6\n\nimport Joi from \"joi\";\nimport lambcycle from \"lambcycle\";\n\nimport pinoPlugin from './myPinoPlugin'\nimport joiPlugin from './myJoiPlugin'\nimport bodyParserPlugin from './myBodyParserPlugin'\n\nimport applicationLogic from \"./mycode\";\n\nconst processData = async (event, context) =\u003e {\n  // beautiful application logic ...\n\n  const manipulateData = event =\u003e {\n    // ...\n  };\n\n  return await applicationLogic(manipulateData(event), context);\n};\n\nconst schema = Joi.object()\n  .keys({\n    username: Joi.string().alphanum().min(5).required(),\n    password: Joi.string().regex(/^[a-zA-Z0-9]{5,30}$/),\n    email: Joi.string().email({ minDomainAtoms: 2 })\n  });\n\nconst handler = lambcycle(processData).register([\n  pinoPlugin,\n  bodyParserPlugin,\n  joiPlugin(schema)\n]);\n\nexport default handler;\n```\n\n# Handler lifecycle\n\nThe 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.\n\n\u003cp align=\"center\"\u003e\n\u003cimg src=\"./assets/lambcycle-lifecycle.png\" height=500\u003e\n\u003c/p\u003e\n\nLambcycle 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.\n\n- The first extension point is `Request` which occurs immediately after the lambda is called. You can use this step for parsing, validation, etc...\nNote: 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.\n\n- 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.\n\n- The `Handler`, where your beautiful business logic lives.\n\n- Next up is the `Post Handler`, use this extension to validate and/or cache the output of your business logic.\n\n- `Error` is an implicit extension for logging and tracing.\n\n- And finally `Pre Response`, your chance to format a response to the consumer (which could be data or an error).\n\n# Error Handling\nThe 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.\nHave a look at the [Wrapper Interface](https://github.com/juliantellez/lambcycle/blob/master/src/Interfaces/IWrapper.ts) to see what's available for reporting.\n\n`HINT: pretty much everything.`\n\n```javascript\nimport lambcycle from 'lambcycle'\nimport notifyError from './myErrorNofifier'\n\nconst appLogic = async(event, context) =\u003e {\n    const {error, data} = await amazingJob()\n    if(error) {\n        throw error\n    }\n}\n\nconst errorNotifier = {\n    plugin: {\n        onError: async (handler) =\u003e {\n            /**\n             * See IWrapper interface\n            */\n            await notifyError(handler.error)\n        }\n    }\n}\n\nconst handler = lambcycle(appLogic).register([errorNotifier])\n\nexport default handler;\n```\n\n\n# Plugins\n\n- [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.\n- [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!\n\n# Creating a plugin \n\nA plugin is an object that can attach its hooks to one or more event cycles, it may provide its own configuration object.\n\n```typescript\ntype IPluginHookFunction = (\n    wrapper: IWrapper,\n    config: object,\n    handleError?: Callback\n) =\u003e void;\n```\n\n```typescript\nimport * as Sentry from '@sentry/node';\nimport MyAwesomeIntegration from './MyAwesomeIntegration'\n\nconst sentryPlugin = (config) =\u003e {\n    Sentry.init({\n        dsn: `https://config.key@sentry.io/${config.project}`,\n        integrations: [new MyAwesomeIntegration()]\n    });\n\n    return {\n        config,\n        plugin: {\n            onPreResponse: async (handlerWrapper, config) =\u003e {\n                Sentry.captureMessage('some percentile log perhaps?')\n            },\n            onError: async (handlerWrapper, config) =\u003e {\n                Sentry.captureException(handlerWrapper.error);\n            }\n        }\n    }\n}\n\nexport default sentryPlugin;\n```\n\n# Using a plugin\n\nLet'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 ㊙️\n\n```typescript\nimport lambcycle from 'lambcycle'\nimport sentryPlugin from './sentryPlugin'\n\nconst myApplicationLogic = async (event, context) =\u003e {\n    await someLogic()\n}\n\nconst handler = lambcycle(myApplicationLogic)\n.register([\n    sentryPlugin({\n        key: process.env.SENTRY_KEY,\n        project: process.env.SENTRY_PROJECT,\n    })\n]);\n\nexport default handler;\n```\n\n# DevX\n\nLambcycle ships with type definitions, making the dev experience smoother 🚀 (VScode only).\n\n![typeintellisense](https://user-images.githubusercontent.com/4896851/51274743-db4db500-19c7-11e9-903c-cb50d127d933.gif)\n\n\n# About the project\n\nThis 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).\n\n# Contributing\nAs 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).\n\n\n# License\n[MIT License](https://github.com/juliantellez/lambcycle/blob/master/LICENSE)\n\n\n\u003ca href=\"https://app.fossa.io/projects/git%2Bgithub.com%2Fjuliantellez%2Flambcycle?ref=badge_large\" target=\"_blank\"\u003e\n    \u003cimg src=\"https://app.fossa.io/api/projects/git%2Bgithub.com%2Fjuliantellez%2Flambcycle.svg?type=large\" alt=\"FOSSA Status\" style=\"max-width:100%; padding:3px;\"\u003e\n\u003c/a\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjuliantellez%2Flambcycle","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjuliantellez%2Flambcycle","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjuliantellez%2Flambcycle/lists"}