{"id":42967553,"url":"https://github.com/brunomorency/simple-lambda-router","last_synced_at":"2026-01-31T00:08:56.496Z","repository":{"id":57360343,"uuid":"95124848","full_name":"brunomorency/simple-lambda-router","owner":"brunomorency","description":"Small router utility for lambda functions handling HTTP events from multiple resources and methods","archived":false,"fork":false,"pushed_at":"2023-03-22T19:39:16.000Z","size":50,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-10-19T10:12:13.509Z","etag":null,"topics":["aws","aws-lambda","lambda","nodejs","router","routing","serverless","serverless-application-model"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","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/brunomorency.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-06-22T14:35:06.000Z","updated_at":"2023-03-22T19:39:23.000Z","dependencies_parsed_at":"2024-10-27T18:40:34.925Z","dependency_job_id":null,"html_url":"https://github.com/brunomorency/simple-lambda-router","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/brunomorency/simple-lambda-router","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brunomorency%2Fsimple-lambda-router","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brunomorency%2Fsimple-lambda-router/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brunomorency%2Fsimple-lambda-router/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brunomorency%2Fsimple-lambda-router/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/brunomorency","download_url":"https://codeload.github.com/brunomorency/simple-lambda-router/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brunomorency%2Fsimple-lambda-router/sbom","scorecard":{"id":255412,"data":{"date":"2025-08-11","repo":{"name":"github.com/brunomorency/simple-lambda-router","commit":"a3b5984229e606ef766ea61339ecf3e48b81fb07"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3,"checks":[{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Code-Review","score":0,"reason":"Found 0/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}}]},"last_synced_at":"2025-08-17T09:35:35.144Z","repository_id":57360343,"created_at":"2025-08-17T09:35:35.144Z","updated_at":"2025-08-17T09:35:35.144Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28923821,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-30T22:32:35.345Z","status":"ssl_error","status_checked_at":"2026-01-30T22:32:31.927Z","response_time":66,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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","nodejs","router","routing","serverless","serverless-application-model"],"created_at":"2026-01-31T00:08:55.833Z","updated_at":"2026-01-31T00:08:56.491Z","avatar_url":"https://github.com/brunomorency.png","language":"JavaScript","readme":"# Use Case\nWhen building an serverless application on AWS Lambda driven by events from API Gateway, there are several popular patterns:\n\n1. Every single HTTP endpoint and method gets its own dedicated lambda function implementing it\n1. There's a single HTTP endpoint sending all requests to one big lambda function\n1. You have a couple resources defined in API gateway which send requests to a set of lambda functions each handling a scope of your logic. For example, there could be a function handling all `GET`/`POST`/`PUT`/`DELETE` requests related to a `users` entity and another to handle requests for a `todos` entity.\n\nFor case 2 and 3 above, your lambda function will need to route the incoming request to the appropriate code to respond to it. *This is what I built this little library for*.\n\n## Bonus functionality\n\nIn addition to routing requests to the right handler, this package adds a few useful things:\n\n- **Promises**: Uses promises for async logic. Your handler code is expected to return a `Promise` when called.\n- **Error handling**: The router catches exceptions from the handler code result in a clean 500 error. The module also exports a `RouteError` class that is useful to reject requests with a specific status code.\n- **Response headers**: You can define default headers to be included in responses for all requests (e.g. CORS headers) and add more headers for specific requests in the handler code.\n- **Input validation**: You can define body and query parameters validation rules and the router will apply them. If body or query parameters don't match your validation rules, the request is rejected with appropriate error code and your handler code isn't executed. The way validation works allows setting default value for optional input.\n- **Request body parsing**: If the incoming request has a `Content-Type` header indicating the body is JSON data. It will be parsed into a JS object before it is passed to you handler code.\n\n## Changes in version 2\n\nThe functionality remains pretty much the same but the package has been refactored to work with the ES module syntax rather than CommonJS modules. If you upgrade, make sure you use `import` rather than `require` to get elements from this package and your handler files should also be ES modules.\n# How It Works\n\n## Lambda Function Handler\nThe package exports a `Route` function. It takes routing configuration and its return value is the handler of your lambda function. For example, if you have a Lambda function defined in your SAM template whose index property is set to `index.handler` as exported by code in file `index.mjs`, this is what that `index.mjs` file would look like:\n\n#### Routing based on `request.resource`:\n```javascript\nimport { Route } from 'simple-lambda-router'\nexport const handler = Route(\n  {\n    resources: {\n      '\u003cHTTP_METHOD\u003e:\u003cAPI_RESOURCE\u003e': \u003cFILE_HANDLING_THAT_REQUEST\u003e\n    },\n    headers: {\n      '\u003cSOME_HTTP_RESPONSE_HEADER\u003e': '\u003cHEADER_VALUE\u003e'\n    }\n  },\n  \u003cOTHER_ARGUMENTS_TO_BE_PASSED_TO_YOUR_REQUEST_HANDLERS\u003e\n)\n```\n\n#### Routing based on `request.path`:\n```javascript\nimport { Route } from 'simple-lambda-router'\nexport const handler = Route(\n  {\n    paths: {\n      '\u003cHTTP_METHOD\u003e:\u003cAPI_PATH\u003e': \u003cFILE_HANDLING_THAT_REQUEST\u003e\n    },\n    headers: {\n      '\u003cSOME_HTTP_RESPONSE_HEADER\u003e': '\u003cHEADER_VALUE\u003e'\n    }\n  },\n  \u003cOTHER_ARGUMENTS_TO_BE_PASSED_TO_YOUR_REQUEST_HANDLERS\u003e\n)\n```\n\n## Route Handler Files\nFiles defined as handlers as defined in arguments to `Route()` above must export a function called `handler` and may also export a `validate` object as follows:\n\n```javascript\nimport joi from 'joi'\n\nexport function handler(request, context, \u003cOTHER_ARGUMENTS_FROM_CALL_TO_ROUTER.ROUTE\u003e) {\n  return new Promise((resolve, reject) =\u003e {\n    resolve({\n      statusCode: 200,\n      body: {\n        someProp: 'the value'\n      },\n      headers: {\n        '\u003cSOME_HTTP_RESPONSE_HEADER\u003e': '\u003cHEADER_VALUE\u003e'\n      }\n    })\n  })\n}\n\nexport const validate = {\n  queryStringParameters: \u003cQUERYSTRING_VALIDATION_RULES\u003e,\n  body: \u003cBODY_VALIDATION_RULES\u003e,\n}\n\n```\n\n### `handler`\nThe `handler` function gets the `request` and `context` arguments as if they were handling the lambda function directly. It also gets all other arguments passed after the config in `Route()` (see first snippet above).\n\nYour handler function must return a Promise. It resolves to an object that must contains a `statusCode` and `body` property, it may also contain a `headers` property:\n\n- If `body` is an object, it will go through `JSON.stringify()` before being sent back as the response body.\n- HTTP headers defined in the `headers` property are included in the response in addition to those included in the initial `Route()` call. If the same HTTP header is defined in both places, the value in the promise resolution of the handler has precedence.\n\nIf your Promise is rejected or has uncaught exceptions, the router will send a `500 Internal Server Error` response back to lambda. You can reject with a specific HTTP error as follows:\n\n```javascript\nimport { RouteError } from 'simple-lambda-router'\nexport function handler(request, context) {\n  return new Promise((resolve, reject) =\u003e {\n    reject(new RouteError({\n      statusCode: 403,\n      message: 'Check with your admin to get access to this.'\n    }))\n  })\n}\n```\n\n### `validate`\nThe validate object lets you define validation rules for the request body and/or query parameters. Validation is super easy to use with the [Joi Object schema validation library](https://github.com/hapijs/joi) but you can use anything as long as you wrap it in a `validate` method compatible with [the Joi validate method](https://github.com/hapijs/joi/blob/master/API.md#validatevalue-schema-options-callback).\n\n```javascript\nimport joi from 'joi'\n\nexport const handler = (request, context) =\u003e {\n  ...\n}\n\nexport const validate = {\n  // Validating using Joi library\n  queryStringParameters: joi.object({\n    status: joi.string().valid('opened','completed').required(),\n  }),\n  // Custom body validation/augment code\n  body: {\n    validate: (requestBody) =\u003e {\n      let error = null\n      if (!('requiredProp' in requestBody)) {\n        error = {\n          details: [\n            { message: 'Request body must contain a \"requiredProp\" property.' }\n          ]\n        }\n      }\n      let value = Object.assign({}, { propWithDefaultVal: 'default val' }, requestBody)\n      return { error, value }\n    }\n  }\n}\n\n```\n\n## Routing based on `request.path`\n\nIf your API gateway has a [proxy resource with a greedy path variable `{proxy+}`](http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-set-up-simple-proxy.html), the `request.resource` value will be that greedy path and the `request.path` will be the actual path with no extracted parameters showing in `request.pathParameters`. \n\n\u003e **Example**:\n\u003e    \n\u003e Your API declares a `/{proxy+}` resource and receives a `GET /items/34`. Upon receiving that request, API Gateway sends an object to your Lambda function with a `resource` property set to `/{proxy+}` and `path` property set to `/items/34`.\n\nThis package allows you to define path-based routing. In other words, the routing logic looks at `/items/34` from the example above, not `/{proxy+}` as it would with resource-based routing. Note that your path route can contain named parameter the same way they are defined in an API Gateway resource path. Those parameters will be extracted and added in the `pathParameters` property of the request object sent to your handler.\n\n\u003e **Example** [ctnd]:\n\u003e  \n\u003e A path key of `GET:/items/{id}` will match the request from the example and your handler will have `request.pathParameters.id` set to `34`\n\n## Chaining handlers\n\nIt is possible to handle requests to a path or resource through a chain of handlers instead of a single handler file. This is useful if many endpoints have common validations (e.g. handlers for `/items/4`, `/items/4/links`, and `/items/4/foo` would all need to check if item 4 actually exist and return a 404 if it doesn't). \n\nYou chain handlers by setting the handlers as an array of files. Each step in the handler chain follows the same structure as a single-file handler (it returns an object with a `handler` function returning a Promise and an optional `validate` object). Validation rules are enforced for each step of the chain and each step gets the value the previous step resolves to as one if its arguments.\n\nSee examples under `items-fn` or the `test-07-chained-handlers.js` file for more details.\n\n# Examples\nExample in the `examples` directory are for the previous version of this package which followed CommonJS modules syntax. Looking at tests may be more useful.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrunomorency%2Fsimple-lambda-router","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbrunomorency%2Fsimple-lambda-router","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrunomorency%2Fsimple-lambda-router/lists"}