{"id":13423805,"url":"https://github.com/hoangvvo/next-connect","last_synced_at":"2025-05-14T11:08:50.483Z","repository":{"id":37412636,"uuid":"214889807","full_name":"hoangvvo/next-connect","owner":"hoangvvo","description":"The TypeScript-ready, minimal router and middleware layer for Next.js, Micro, Vercel, or Node.js http/http2","archived":false,"fork":false,"pushed_at":"2024-02-15T06:39:41.000Z","size":644,"stargazers_count":1654,"open_issues_count":43,"forks_count":66,"subscribers_count":10,"default_branch":"main","last_synced_at":"2025-05-12T17:43:19.906Z","etag":null,"topics":["connect","javascript","micro","middleware","nextjs","router","typescript"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/next-connect","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/hoangvvo.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","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}},"created_at":"2019-10-13T20:33:30.000Z","updated_at":"2025-05-04T14:54:49.000Z","dependencies_parsed_at":"2024-04-09T23:58:33.136Z","dependency_job_id":null,"html_url":"https://github.com/hoangvvo/next-connect","commit_stats":{"total_commits":167,"total_committers":13,"mean_commits":"12.846153846153847","dds":0.6586826347305389,"last_synced_commit":"e5ac7faf380a73114a2b6b07b0a43a01f8c1be36"},"previous_names":[],"tags_count":35,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hoangvvo%2Fnext-connect","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hoangvvo%2Fnext-connect/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hoangvvo%2Fnext-connect/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hoangvvo%2Fnext-connect/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hoangvvo","download_url":"https://codeload.github.com/hoangvvo/next-connect/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254129481,"owners_count":22019628,"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":["connect","javascript","micro","middleware","nextjs","router","typescript"],"created_at":"2024-07-31T00:00:42.927Z","updated_at":"2025-05-14T11:08:50.456Z","avatar_url":"https://github.com/hoangvvo.png","language":"TypeScript","funding_links":[],"categories":["Extensions","TypeScript","miscellaneous","📦 Legacy \u0026 Inactive Projects"],"sub_categories":[],"readme":"# next-connect\n\n[![npm](https://badgen.net/npm/v/next-connect)](https://www.npmjs.com/package/next-connect)\n[![CircleCI](https://circleci.com/gh/hoangvvo/next-connect.svg?style=svg)](https://circleci.com/gh/hoangvvo/next-connect)\n[![codecov](https://codecov.io/gh/hoangvvo/next-connect/branch/main/graph/badge.svg?token=LGIyl3Ti4H)](https://codecov.io/gh/hoangvvo/next-connect)\n[![minified size](https://badgen.net/bundlephobia/min/next-connect)](https://bundlephobia.com/result?p=next-connect)\n[![download/year](https://badgen.net/npm/dy/next-connect)](https://www.npmjs.com/package/next-connect)\n\nThe promise-based method routing and middleware layer for [Next.js](https://nextjs.org/) [API Routes](#nextjs-api-routes), [Edge API Routes](#nextjs-edge-api-routes), [Middleware](#nextjs-middleware), [Next.js App Router](#nextjs-app-router), and [getServerSideProps](#nextjs-getserversideprops).\n\n## Features\n\n- Async middleware\n- [Lightweight](https://bundlephobia.com/scan-results?packages=express,next-connect,koa,micro) =\u003e Suitable for serverless environment\n- [way faster](https://github.com/hoangvvo/next-connect/tree/main/bench) than Express.js. Compatible with Express.js via [a wrapper](#expressjs-compatibility).\n- Works with async handlers (with error catching)\n- TypeScript support\n\n## Installation\n\n```sh\nnpm install next-connect@next\n```\n\n## Usage\n\nAlso check out the [examples](./examples/) folder.\n\n### Next.js API Routes\n\n`next-connect` can be used in [API Routes](https://nextjs.org/docs/api-routes/introduction).\n\n```typescript\n// pages/api/user/[id].ts\nimport type { NextApiRequest, NextApiResponse } from \"next\";\nimport { createRouter, expressWrapper } from \"next-connect\";\nimport cors from \"cors\";\n\nconst router = createRouter\u003cNextApiRequest, NextApiResponse\u003e();\n\nrouter\n  // Use express middleware in next-connect with expressWrapper function\n  .use(expressWrapper(passport.session()))\n  // A middleware example\n  .use(async (req, res, next) =\u003e {\n    const start = Date.now();\n    await next(); // call next in chain\n    const end = Date.now();\n    console.log(`Request took ${end - start}ms`);\n  })\n  .get((req, res) =\u003e {\n    const user = getUser(req.query.id);\n    res.json({ user });\n  })\n  .put((req, res) =\u003e {\n    if (req.user.id !== req.query.id) {\n      throw new ForbiddenError(\"You can't update other user's profile\");\n    }\n    const user = await updateUser(req.body.user);\n    res.json({ user });\n  });\n\nexport const config = {\n  runtime: \"edge\",\n};\n\nexport default router.handler({\n  onError: (err, req, res) =\u003e {\n    console.error(err.stack);\n    res.status(err.statusCode || 500).end(err.message);\n  },\n});\n```\n\n### Next.js Edge API Routes\n\n`next-connect` can be used in [Edge API Routes](https://nextjs.org/docs/api-routes/edge-api-routes)\n\n```typescript\n// pages/api/user/[id].ts\nimport type { NextFetchEvent, NextRequest } from \"next/server\";\nimport { createEdgeRouter } from \"next-connect\";\nimport cors from \"cors\";\n\nconst router = createEdgeRouter\u003cNextRequest, NextFetchEvent\u003e();\n\nrouter\n  // A middleware example\n  .use(async (req, event, next) =\u003e {\n    const start = Date.now();\n    await next(); // call next in chain\n    const end = Date.now();\n    console.log(`Request took ${end - start}ms`);\n  })\n  .get((req) =\u003e {\n    const id = req.nextUrl.searchParams.get(\"id\");\n    const user = getUser(id);\n    return NextResponse.json({ user });\n  })\n  .put((req) =\u003e {\n    const id = req.nextUrl.searchParams.get(\"id\");\n    if (req.user.id !== id) {\n      throw new ForbiddenError(\"You can't update other user's profile\");\n    }\n    const user = await updateUser(req.body.user);\n    return NextResponse.json({ user });\n  });\n\nexport default router.handler({\n  onError: (err, req, event) =\u003e {\n    console.error(err.stack);\n    return new NextResponse(\"Something broke!\", {\n      status: err.statusCode || 500,\n    });\n  },\n});\n```\n\n### Next.js App Router\n\n`next-connect` can be used in [Next.js 13 Route Handler](https://beta.nextjs.org/docs/routing/route-handlers). The way handlers are written is almost the same to [Next.js Edge API Routes](#nextjs-edge-api-routes) by using `createEdgeRouter`.\n\n```typescript\n// app/api/user/[id]/route.ts\n\nimport type { NextFetchEvent, NextRequest } from \"next/server\";\nimport { createEdgeRouter } from \"next-connect\";\nimport cors from \"cors\";\n\ninterface RequestContext {\n  params: {\n    id: string;\n  };\n}\n\nconst router = createEdgeRouter\u003cNextRequest, RequestContext\u003e();\n\nrouter\n  // A middleware example\n  .use(async (req, event, next) =\u003e {\n    const start = Date.now();\n    await next(); // call next in chain\n    const end = Date.now();\n    console.log(`Request took ${end - start}ms`);\n  })\n  .get((req) =\u003e {\n    const id = req.params.id;\n    const user = getUser(id);\n    return NextResponse.json({ user });\n  })\n  .put((req) =\u003e {\n    const id = req.params.id;\n    if (req.user.id !== id) {\n      throw new ForbiddenError(\"You can't update other user's profile\");\n    }\n    const user = await updateUser(req.body.user);\n    return NextResponse.json({ user });\n  });\n\nexport async function GET(request: NextRequest, ctx: RequestContext) {\n  return router.run(request, ctx);\n}\n\nexport async function PUT(request: NextRequest, ctx: RequestContext) {\n  return router.run(request, ctx);\n}\n```\n\n### Next.js Middleware\n\n`next-connect` can be used in [Next.js Middleware](https://nextjs.org/docs/advanced-features/middleware)\n\n```typescript\n// middleware.ts\nimport { NextResponse } from \"next/server\";\nimport type { NextRequest, NextFetchEvent } from \"next/server\";\nimport { createEdgeRouter } from \"next-connect\";\n\nconst router = createEdgeRouter\u003cNextRequest, NextFetchEvent\u003e();\n\nrouter.use(async (request, event, next) =\u003e {\n  // logging request example\n  console.log(`${request.method} ${request.url}`);\n  return next();\n});\n\nrouter.get(\"/about\", (request) =\u003e {\n  return NextResponse.redirect(new URL(\"/about-2\", request.url));\n});\n\nrouter.use(\"/dashboard\", (request) =\u003e {\n  if (!isAuthenticated(request)) {\n    return NextResponse.redirect(new URL(\"/login\", request.url));\n  }\n  return NextResponse.next();\n});\n\nrouter.all(() =\u003e {\n  // default if none of the above matches\n  return NextResponse.next();\n});\n\nexport function middleware(request: NextRequest, event: NextFetchEvent) {\n  return router.run(request, event);\n}\n\nexport const config = {\n  matcher: [\n    /*\n     * Match all request paths except for the ones starting with:\n     * - api (API routes)\n     * - _next/static (static files)\n     * - _next/image (image optimization files)\n     * - favicon.ico (favicon file)\n     */\n    \"/((?!api|_next/static|_next/image|favicon.ico).*)\",\n  ],\n};\n```\n\n### Next.js getServerSideProps\n\n`next-connect` can be used in [getServerSideProps](https://nextjs.org/docs/basic-features/data-fetching/get-server-side-props).\n\n```jsx\n// pages/users/[id].js\nimport { createRouter } from \"next-connect\";\n\nexport default function Page({ user, updated }) {\n  return (\n    \u003cdiv\u003e\n      {updated \u0026\u0026 \u003cp\u003eUser has been updated\u003c/p\u003e}\n      \u003cdiv\u003e{JSON.stringify(user)}\u003c/div\u003e\n      \u003cform method=\"POST\"\u003e{/* User update form */}\u003c/form\u003e\n    \u003c/div\u003e\n  );\n}\n\nconst router = createRouter()\n  .use(async (req, res, next) =\u003e {\n    // this serve as the error handling middleware\n    try {\n      return await next();\n    } catch (e) {\n      return {\n        props: { error: e.message },\n      };\n    }\n  })\n  .get(async (req, res) =\u003e {\n    const user = await getUser(req.params.id);\n    if (!user) {\n      // https://nextjs.org/docs/api-reference/data-fetching/get-server-side-props#notfound\n      return { props: { notFound: true } };\n    }\n    return { props: { user } };\n  })\n  .put(async (req, res) =\u003e {\n    const user = await updateUser(req);\n    return { props: { user, updated: true } };\n  });\n\nexport async function getServerSideProps({ req, res }) {\n  return router.run(req, res);\n}\n```\n\n## API\n\nThe following APIs are rewritten in term of `NodeRouter` (`createRouter`), but they apply to `EdgeRouter` (`createEdgeRouter`) as well.\n\n### router = createRouter()\n\nCreate an instance Node.js router.\n\n### router.use(base, ...fn)\n\n`base` (optional) - match all routes to the right of `base` or match all if omitted. (Note: If used in Next.js, this is often omitted)\n\n`fn`(s) can either be:\n\n- functions of `(req, res[, next])`\n- **or** a router instance\n\n```javascript\n// Mount a middleware function\nrouter1.use(async (req, res, next) =\u003e {\n  req.hello = \"world\";\n  await next(); // call to proceed to the next in chain\n  console.log(\"request is done\"); // call after all downstream handler has run\n});\n\n// Or include a base\nrouter2.use(\"/foo\", fn); // Only run in /foo/**\n\n// mount an instance of router\nconst sub1 = createRouter().use(fn1, fn2);\nconst sub2 = createRouter().use(\"/dashboard\", auth);\nconst sub3 = createRouter()\n  .use(\"/waldo\", subby)\n  .get(getty)\n  .post(\"/baz\", posty)\n  .put(\"/\", putty);\nrouter3\n  // - fn1 and fn2 always run\n  // - auth runs only on /dashboard\n  .use(sub1, sub2)\n  // `subby` runs on ANY /foo/waldo?/*\n  // `getty` runs on GET /foo/*\n  // `posty` runs on POST /foo/baz\n  // `putty` runs on PUT /foo\n  .use(\"/foo\", sub3);\n```\n\n### router.METHOD(pattern, ...fns)\n\n`METHOD` is an HTTP method (`GET`, `HEAD`, `POST`, `PUT`, `PATCH`, `DELETE`, `OPTIONS`, `TRACE`) in lowercase.\n\n`pattern` (optional) - match routes based on [supported pattern](https://github.com/lukeed/regexparam#regexparam-) or match any if omitted.\n\n`fn`(s) are functions of `(req, res[, next])`.\n\n```javascript\nrouter.get(\"/api/user\", (req, res, next) =\u003e {\n  res.json(req.user);\n});\nrouter.post(\"/api/users\", (req, res, next) =\u003e {\n  res.end(\"User created\");\n});\nrouter.put(\"/api/user/:id\", (req, res, next) =\u003e {\n  // https://nextjs.org/docs/routing/dynamic-routes\n  res.end(`User ${req.params.id} updated`);\n});\n\n// Next.js already handles routing (including dynamic routes), we often\n// omit `pattern` in `.METHOD`\nrouter.get((req, res, next) =\u003e {\n  res.end(\"This matches whatever route\");\n});\n```\n\n\u003e **Note**\n\u003e You should understand Next.js [file-system based routing](https://nextjs.org/docs/routing/introduction). For example, having a `router.put(\"/api/foo\", handler)` inside `page/api/index.js` _does not_ serve that handler at `/api/foo`.\n\n### router.all(pattern, ...fns)\n\nSame as [.METHOD](#methodpattern-fns) but accepts _any_ methods.\n\n### router.handler(options)\n\nCreate a handler to handle incoming requests.\n\n**options.onError**\n\nAccepts a function as a catch-all error handler; executed whenever a handler throws an error.\nBy default, it responds with a generic `500 Internal Server Error` while logging the error to `console`.\n\n```javascript\nfunction onError(err, req, res) {\n  logger.log(err);\n  // OR: console.error(err);\n\n  res.status(500).end(\"Internal server error\");\n}\n\nexport default router.handler({ onError });\n```\n\n**options.onNoMatch**\n\nAccepts a function of `(req, res)` as a handler when no route is matched.\nBy default, it responds with a `404` status and a `Route [Method] [Url] not found` body.\n\n```javascript\nfunction onNoMatch(req, res) {\n  res.status(404).end(\"page is not found... or is it!?\");\n}\n\nexport default router.handler({ onNoMatch });\n```\n\n### router.run(req, res)\n\nRuns `req` and `res` through the middleware chain and returns a **promise**. It resolves with the value returned from handlers.\n\n```js\nrouter\n  .use(async (req, res, next) =\u003e {\n    return (await next()) + 1;\n  })\n  .use(async () =\u003e {\n    return (await next()) + 2;\n  })\n  .use(async () =\u003e {\n    return 3;\n  });\n\nconsole.log(await router.run(req, res));\n// The above will print \"6\"\n```\n\nIf an error in thrown within the chain, `router.run` will reject. You can also add a try-catch in the first middleware to catch the error before it rejects the `.run()` call:\n\n```js\nrouter\n  .use(async (req, res, next) =\u003e {\n    return next().catch(errorHandler);\n  })\n  .use(thisMiddlewareMightThrow);\n\nawait router.run(req, res);\n```\n\n## Common errors\n\nThere are some pitfalls in using `next-connect`. Below are things to keep in mind to use it correctly.\n\n1. **Always** `await next()`\n\nIf `next()` is not awaited, errors will not be caught if they are thrown in async handlers, leading to `UnhandledPromiseRejection`.\n\n```javascript\n// OK: we don't use async so no need to await\nrouter\n  .use((req, res, next) =\u003e {\n    next();\n  })\n  .use((req, res, next) =\u003e {\n    next();\n  })\n  .use(() =\u003e {\n    throw new Error(\"💥\");\n  });\n\n// BAD: This will lead to UnhandledPromiseRejection\nrouter\n  .use(async (req, res, next) =\u003e {\n    next();\n  })\n  .use(async (req, res, next) =\u003e {\n    next();\n  })\n  .use(async () =\u003e {\n    throw new Error(\"💥\");\n  });\n\n// GOOD\nrouter\n  .use(async (req, res, next) =\u003e {\n    await next(); // next() is awaited, so errors are caught properly\n  })\n  .use((req, res, next) =\u003e {\n    return next(); // this works as well since we forward the rejected promise\n  })\n  .use(async () =\u003e {\n    throw new Error(\"💥\");\n    // return new Promise.reject(\"💥\");\n  });\n```\n\nAnother issue is that the handler would resolve before all the code in each layer runs.\n\n```javascript\nconst handler = router\n  .use(async (req, res, next) =\u003e {\n    next(); // this is not returned or await\n  })\n  .get(async () =\u003e {\n    // simulate a long task\n    await new Promise((resolve) =\u003e setTimeout(resolve, 1000));\n    res.send(\"ok\");\n    console.log(\"request is completed\");\n  })\n  .handler();\n\nawait handler(req, res);\nconsole.log(\"finally\"); // this will run before the get layer gets to finish\n\n// This will result in:\n// 1) \"finally\"\n// 2) \"request is completed\"\n```\n\n2. **DO NOT** reuse the same instance of `router` like the below pattern:\n\n```javascript\n// api-libs/base.js\nexport default createRouter().use(a).use(b);\n\n// api/foo.js\nimport router from \"api-libs/base\";\nexport default router.get(x).handler();\n\n// api/bar.js\nimport router from \"api-libs/base\";\nexport default router.get(y).handler();\n```\n\nThis is because, in each API Route, the same router instance is mutated, leading to undefined behaviors.\nIf you want to achieve something like that, you can use `router.clone` to return different instances with the same routes populated.\n\n```javascript\n// api-libs/base.js\nexport default createRouter().use(a).use(b);\n\n// api/foo.js\nimport router from \"api-libs/base\";\nexport default router.clone().get(x).handler();\n\n// api/bar.js\nimport router from \"api-libs/base\";\nexport default router.clone().get(y).handler();\n```\n\n3. **DO NOT** use response function like `res.(s)end` or `res.redirect` inside `getServerSideProps`.\n\n```javascript\n// page/index.js\nconst handler = createRouter()\n  .use((req, res) =\u003e {\n    // BAD: res.redirect is not a function (not defined in `getServerSideProps`)\n    // See https://github.com/hoangvvo/next-connect/issues/194#issuecomment-1172961741 for a solution\n    res.redirect(\"foo\");\n  })\n  .use((req, res) =\u003e {\n    // BAD: `getServerSideProps` gives undefined behavior if we try to send a response\n    res.end(\"bar\");\n  });\n\nexport async function getServerSideProps({ req, res }) {\n  await router.run(req, res);\n  return {\n    props: {},\n  };\n}\n```\n\n3. **DO NOT** use `handler()` directly in `getServerSideProps`.\n\n```javascript\n// page/index.js\nconst router = createRouter().use(foo).use(bar);\nconst handler = router.handler();\n\nexport async function getServerSideProps({ req, res }) {\n  await handler(req, res); // BAD: You should call router.run(req, res);\n  return {\n    props: {},\n  };\n}\n```\n\n## Contributing\n\nPlease see my [contributing.md](CONTRIBUTING.md).\n\n## License\n\n[MIT](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhoangvvo%2Fnext-connect","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhoangvvo%2Fnext-connect","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhoangvvo%2Fnext-connect/lists"}