{"id":13761974,"url":"https://github.com/neg4n/next-api-compose","last_synced_at":"2025-04-15T17:00:27.911Z","repository":{"id":46807442,"uuid":"406170590","full_name":"neg4n/next-api-compose","owner":"neg4n","description":"🧬 Simple, dependency free, error aware and powerful utility to compose chain of multiple middleware into one Next.js API Route.","archived":false,"fork":false,"pushed_at":"2023-12-02T15:42:05.000Z","size":905,"stargazers_count":37,"open_issues_count":2,"forks_count":5,"subscribers_count":1,"default_branch":"development","last_synced_at":"2025-04-14T05:14:52.106Z","etag":null,"topics":["app-router-nextjs","express","hacktoberfest","next","next13","nextjs","nextjs-api","nextjs-api-routes","nextjs-plugin","nextjs-typescript","pages-router","typescript"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/next-api-compose","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/neg4n.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":"2021-09-14T00:27:09.000Z","updated_at":"2024-12-31T12:29:36.000Z","dependencies_parsed_at":"2023-10-17T03:11:05.000Z","dependency_job_id":"e140e43b-386c-4c9d-a08b-f9f8cd5b1377","html_url":"https://github.com/neg4n/next-api-compose","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/neg4n%2Fnext-api-compose","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/neg4n%2Fnext-api-compose/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/neg4n%2Fnext-api-compose/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/neg4n%2Fnext-api-compose/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/neg4n","download_url":"https://codeload.github.com/neg4n/next-api-compose/tar.gz/refs/heads/development","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249116231,"owners_count":21215142,"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":["app-router-nextjs","express","hacktoberfest","next","next13","nextjs","nextjs-api","nextjs-api-routes","nextjs-plugin","nextjs-typescript","pages-router","typescript"],"created_at":"2024-08-03T14:00:32.288Z","updated_at":"2025-04-15T17:00:27.873Z","avatar_url":"https://github.com/neg4n.png","language":"TypeScript","funding_links":[],"categories":["miscellaneous"],"sub_categories":[],"readme":"\u003cpicture\u003e\n  \u003csource media=\"(prefers-color-scheme: dark)\" srcset=\"https://raw.githubusercontent.com/neg4n/next-api-compose/development/.github/assets/code_dark.png\"\u003e\n  \u003csource media=\"(prefers-color-scheme: light)\" srcset=\"https://raw.githubusercontent.com/neg4n/next-api-compose/development/.github/assets/code_light.png\"\u003e\n  \u003cimg align=\"right\" width=\"500px\" height=\"500px\" alt=\"next-api-compose example code theme aware\" src=\"https://raw.githubusercontent.com/neg4n/next-api-compose/development/.github/assets/code_dark.png\"/\u003e\n\u003c/picture\u003e\n\n# Next.js API Compose \u0026middot; [![version](https://badgen.net/npm/v/next-api-compose)](https://www.npmjs.com/package/next-api-compose) [![npm bundle size](https://badgen.net/bundlephobia/minzip/next-api-compose)](https://bundlephobia.com/package/next-api-compose)\n\n## Introduction\n\nThis library provides a hassle-free way of composing multiple middleware functions into one [Next.js API Route Handler][next-api-route-handlers]'s method in the **[App Directory][next-app-router]** router.\n\n\u003e [!IMPORTANT]\n\u003e The `2.0.0` version of the library supports both [app][next-app-router] and [pages][next-app-router] directory oriented API utilities. If you're still using Pages Router and you want to migrate from versions below `2.0.0`, please read [migration guide](./.github/MIGRATE_V2.md) and ocassionally consider checking out [intro to the App Router][next-app-router-intro].\n\n## Features\n\n- [x] 😇 Simple and powerful API\n- [x] 🚀 Designed both for Pages Router and App Router\n- [x] 🧪 Production-ready. 100% test coverage, even type inference is tested!\n- [x] 🥷 Excellent TypeScript support\n- [x] 🧬 Maintaining order of middleware chain\n- [x] 📦 No dependencies, small footprint\n\n## Installing and basic usage\n\nInstall the package by running:\n\n```sh\nnpm i next-api-compose -S\n# or\nyarn add next-api-compose\n# or\npnpm i next-api-compose\n```\n\nthen create an API Route Handler in the **[App Directory][next-app-router-intro]**:\n\nin TypeScript **(recommended)**\n\n```ts\nimport type { NextRequest } from \"next/server\";\nimport { compose } from \"next-api-compose\";\n\nfunction someMiddleware(request: NextRequest \u0026 { foo: string }) {\n  request.foo = \"bar\";\n}\n\nconst { GET } = compose({\n  GET: [\n    [someMiddleware],\n    (request /* This is automatically inferred */) =\u003e {\n      return new Response(request.foo);\n      //                         ^ (property) foo: string - autocomplete works here\n    },\n  ],\n});\n\nexport { GET };\n```\n\nin JavaScript:\n\n```js\nimport { compose } from \"next-api-compose\";\n\nfunction someMiddleware(request) {\n  request.foo = \"bar\";\n}\n\nconst { GET } = compose({\n  GET: [\n    [someMiddleware],\n    (request) =\u003e {\n      return new Response(request.foo);\n    },\n  ],\n});\n\nexport { GET };\n```\n\n## Error handling\n\nHandling errors both in middleware and in the main handler is as simple as providing `sharedErrorHandler` to the `compose` function's second parameter _(a.k.a compose settings)_. Main goal of the shared error handler is to provide clear and easy way to e.g. send the error metadata to Sentry or other error tracking service.\n\nBy default, shared error handler looks like this:\n\n```ts\nsharedErrorHandler: {\n  handler: undefined;\n  // ^^^^ This is the handler function. By default there is no handler, so the error is being just thrown.\n  includeRouteHandler: false;\n  // ^^^^^^^^^^^^^^^^ This toggles whether the route handler itself should be included in a error handled area.\n  //                  By default only middlewares are being caught by the sharedErrorHandler\n}\n```\n\n... and some usage example:\n\n```ts\n// [...]\nfunction errorMiddleware() {\n  throw new Error(\"foo\");\n}\n\nconst { GET } = compose(\n  {\n    GET: [\n      [errorMiddleware],\n      () =\u003e {\n        // Unreachable code due to errorMiddleware throwing an error and halting the chain\n        return new Response(JSON.stringify({ foo: \"bar\" }));\n      },\n    ],\n  },\n  {\n    sharedErrorHandler: {\n      handler: (_method, error) =\u003e {\n        return new Response(JSON.stringify({ error: error.message }), {\n          status: 500,\n        });\n      },\n    },\n  }\n);\n// [...]\n```\n\nwill return `{\"error\": \"foo\"}` along with `500` status code instead of throwing an error.\n\n## Theory and caveats\n\n1. Unfortunately there is no way to dynamically export named ESModules _(or at least I did not find a way)_ so you have to use `export { GET, POST }` syntax instead of something like `export compose(...)` if you're composing GET and POST methods :(\n\n2. Middleware is executed as specified in the per-method array, so if you want to execute middleware in a specific order, you have to be careful about it. Early returned `new Response()` halts the middleware chain.\n\n## Contributors\n\n\u003c!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section --\u003e\n\u003c!-- prettier-ignore-start --\u003e\n\u003c!-- markdownlint-disable --\u003e\n\u003ctable\u003e\n  \u003ctbody\u003e\n    \u003ctr\u003e\n      \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\u003ca href=\"https://neg4n.dev/\"\u003e\u003cimg src=\"https://avatars.githubusercontent.com/u/57688858?v=4?s=100\" width=\"100px;\" alt=\"Igor\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eIgor\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/neg4n/next-api-compose/commits?author=neg4n\" title=\"Code\"\u003e💻\u003c/a\u003e \u003ca href=\"https://github.com/neg4n/next-api-compose/commits?author=neg4n\" title=\"Tests\"\u003e⚠️\u003c/a\u003e \u003ca href=\"#example-neg4n\" title=\"Examples\"\u003e💡\u003c/a\u003e\u003c/td\u003e\n      \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\u003ca href=\"https://github.com/mgrabka\"\u003e\u003cimg src=\"https://avatars.githubusercontent.com/u/116151164?v=4?s=100\" width=\"100px;\" alt=\"Maksymilian Grabka\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eMaksymilian Grabka\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/neg4n/next-api-compose/commits?author=mgrabka\" title=\"Tests\"\u003e⚠️\u003c/a\u003e \u003ca href=\"https://github.com/neg4n/next-api-compose/commits?author=mgrabka\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n      \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\u003ca href=\"https://github.com/kacper3123\"\u003e\u003cimg src=\"https://avatars.githubusercontent.com/u/89151689?v=4?s=100\" width=\"100px;\" alt=\"kacper3123\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003ekacper3123\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/neg4n/next-api-compose/commits?author=kacper3123\" title=\"Documentation\"\u003e📖\u003c/a\u003e\u003c/td\u003e\n    \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\n\u003c!-- markdownlint-restore --\u003e\n\u003c!-- prettier-ignore-end --\u003e\n\n\u003c!-- ALL-CONTRIBUTORS-LIST:END --\u003e\n\n## License and acknowledgements\n\nThe project is licensed under The MIT License. Thanks for all the contributions! Feel free to open an issue or a pull request even if it is just a question 🙌\n\n[next-api-route-handlers]: https://nextjs.org/docs/app/building-your-application/routing/route-handlers\n[next-app-router-intro]: https://nextjs.org/docs/app/building-your-application/routing#the-app-router\n[next-app-router]: https://nextjs.org/docs/app\n[next-pages-router]: https://nextjs.org/docs/pages\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fneg4n%2Fnext-api-compose","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fneg4n%2Fnext-api-compose","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fneg4n%2Fnext-api-compose/lists"}