Gathering detailed insights and metrics for next-connect
Gathering detailed insights and metrics for next-connect
Gathering detailed insights and metrics for next-connect
Gathering detailed insights and metrics for next-connect
The TypeScript-ready, minimal router and middleware layer for Next.js, Micro, Vercel, or Node.js http/http2
npm install next-connect
Module System
Min. Node Version
Typescript Support
Node Version
NPM Version
1,641 Stars
184 Commits
65 Forks
9 Watching
5 Branches
11 Contributors
Updated on 25 Nov 2024
Minified
Minified + Gzipped
TypeScript (97.72%)
JavaScript (1.58%)
Shell (0.7%)
Cumulative downloads
Total Downloads
Last day
0.6%
16,126
Compared to previous day
Last week
-0.8%
83,496
Compared to previous week
Last month
13.5%
357,965
Compared to previous month
Last year
-11.7%
4,426,359
Compared to previous year
The promise-based method routing and middleware layer for Next.js API Routes, Edge API Routes, Middleware, Next.js App Router, and getServerSideProps.
1npm install next-connect@next
Also check out the examples folder.
next-connect
can be used in API Routes.
1// pages/api/user/[id].ts 2import type { NextApiRequest, NextApiResponse } from "next"; 3import { createRouter, expressWrapper } from "next-connect"; 4import cors from "cors"; 5 6const router = createRouter<NextApiRequest, NextApiResponse>(); 7 8router 9 // Use express middleware in next-connect with expressWrapper function 10 .use(expressWrapper(passport.session())) 11 // A middleware example 12 .use(async (req, res, next) => { 13 const start = Date.now(); 14 await next(); // call next in chain 15 const end = Date.now(); 16 console.log(`Request took ${end - start}ms`); 17 }) 18 .get((req, res) => { 19 const user = getUser(req.query.id); 20 res.json({ user }); 21 }) 22 .put((req, res) => { 23 if (req.user.id !== req.query.id) { 24 throw new ForbiddenError("You can't update other user's profile"); 25 } 26 const user = await updateUser(req.body.user); 27 res.json({ user }); 28 }); 29 30export const config = { 31 runtime: "edge", 32}; 33 34export default router.handler({ 35 onError: (err, req, res) => { 36 console.error(err.stack); 37 res.status(err.statusCode || 500).end(err.message); 38 }, 39});
next-connect
can be used in Edge API Routes
1// pages/api/user/[id].ts 2import type { NextFetchEvent, NextRequest } from "next/server"; 3import { createEdgeRouter } from "next-connect"; 4import cors from "cors"; 5 6const router = createEdgeRouter<NextRequest, NextFetchEvent>(); 7 8router 9 // A middleware example 10 .use(async (req, event, next) => { 11 const start = Date.now(); 12 await next(); // call next in chain 13 const end = Date.now(); 14 console.log(`Request took ${end - start}ms`); 15 }) 16 .get((req) => { 17 const id = req.nextUrl.searchParams.get("id"); 18 const user = getUser(id); 19 return NextResponse.json({ user }); 20 }) 21 .put((req) => { 22 const id = req.nextUrl.searchParams.get("id"); 23 if (req.user.id !== id) { 24 throw new ForbiddenError("You can't update other user's profile"); 25 } 26 const user = await updateUser(req.body.user); 27 return NextResponse.json({ user }); 28 }); 29 30export default router.handler({ 31 onError: (err, req, event) => { 32 console.error(err.stack); 33 return new NextResponse("Something broke!", { 34 status: err.statusCode || 500, 35 }); 36 }, 37});
next-connect
can be used in Next.js 13 Route Handler. The way handlers are written is almost the same to Next.js Edge API Routes by using createEdgeRouter
.
1// app/api/user/[id]/route.ts 2 3import type { NextFetchEvent, NextRequest } from "next/server"; 4import { createEdgeRouter } from "next-connect"; 5import cors from "cors"; 6 7interface RequestContext { 8 params: { 9 id: string; 10 }; 11} 12 13const router = createEdgeRouter<NextRequest, RequestContext>(); 14 15router 16 // A middleware example 17 .use(async (req, event, next) => { 18 const start = Date.now(); 19 await next(); // call next in chain 20 const end = Date.now(); 21 console.log(`Request took ${end - start}ms`); 22 }) 23 .get((req) => { 24 const id = req.params.id; 25 const user = getUser(id); 26 return NextResponse.json({ user }); 27 }) 28 .put((req) => { 29 const id = req.params.id; 30 if (req.user.id !== id) { 31 throw new ForbiddenError("You can't update other user's profile"); 32 } 33 const user = await updateUser(req.body.user); 34 return NextResponse.json({ user }); 35 }); 36 37export async function GET(request: NextRequest, ctx: RequestContext) { 38 return router.run(request, ctx); 39} 40 41export async function PUT(request: NextRequest, ctx: RequestContext) { 42 return router.run(request, ctx); 43}
next-connect
can be used in Next.js Middleware
1// middleware.ts 2import { NextResponse } from "next/server"; 3import type { NextRequest, NextFetchEvent } from "next/server"; 4import { createEdgeRouter } from "next-connect"; 5 6const router = createEdgeRouter<NextRequest, NextFetchEvent>(); 7 8router.use(async (request, event, next) => { 9 // logging request example 10 console.log(`${request.method} ${request.url}`); 11 return next(); 12}); 13 14router.get("/about", (request) => { 15 return NextResponse.redirect(new URL("/about-2", request.url)); 16}); 17 18router.use("/dashboard", (request) => { 19 if (!isAuthenticated(request)) { 20 return NextResponse.redirect(new URL("/login", request.url)); 21 } 22 return NextResponse.next(); 23}); 24 25router.all(() => { 26 // default if none of the above matches 27 return NextResponse.next(); 28}); 29 30export function middleware(request: NextRequest, event: NextFetchEvent) { 31 return router.run(request, event); 32} 33 34export const config = { 35 matcher: [ 36 /* 37 * Match all request paths except for the ones starting with: 38 * - api (API routes) 39 * - _next/static (static files) 40 * - _next/image (image optimization files) 41 * - favicon.ico (favicon file) 42 */ 43 "/((?!api|_next/static|_next/image|favicon.ico).*)", 44 ], 45};
next-connect
can be used in getServerSideProps.
1// pages/users/[id].js 2import { createRouter } from "next-connect"; 3 4export default function Page({ user, updated }) { 5 return ( 6 <div> 7 {updated && <p>User has been updated</p>} 8 <div>{JSON.stringify(user)}</div> 9 <form method="POST">{/* User update form */}</form> 10 </div> 11 ); 12} 13 14const router = createRouter() 15 .use(async (req, res, next) => { 16 // this serve as the error handling middleware 17 try { 18 return await next(); 19 } catch (e) { 20 return { 21 props: { error: e.message }, 22 }; 23 } 24 }) 25 .get(async (req, res) => { 26 const user = await getUser(req.params.id); 27 if (!user) { 28 // https://nextjs.org/docs/api-reference/data-fetching/get-server-side-props#notfound 29 return { props: { notFound: true } }; 30 } 31 return { props: { user } }; 32 }) 33 .put(async (req, res) => { 34 const user = await updateUser(req); 35 return { props: { user, updated: true } }; 36 }); 37 38export async function getServerSideProps({ req, res }) { 39 return router.run(req, res); 40}
The following APIs are rewritten in term of NodeRouter
(createRouter
), but they apply to EdgeRouter
(createEdgeRouter
) as well.
Create an instance Node.js router.
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)
fn
(s) can either be:
(req, res[, next])
1// Mount a middleware function 2router1.use(async (req, res, next) => { 3 req.hello = "world"; 4 await next(); // call to proceed to the next in chain 5 console.log("request is done"); // call after all downstream handler has run 6}); 7 8// Or include a base 9router2.use("/foo", fn); // Only run in /foo/** 10 11// mount an instance of router 12const sub1 = createRouter().use(fn1, fn2); 13const sub2 = createRouter().use("/dashboard", auth); 14const sub3 = createRouter() 15 .use("/waldo", subby) 16 .get(getty) 17 .post("/baz", posty) 18 .put("/", putty); 19router3 20 // - fn1 and fn2 always run 21 // - auth runs only on /dashboard 22 .use(sub1, sub2) 23 // `subby` runs on ANY /foo/waldo?/* 24 // `getty` runs on GET /foo/* 25 // `posty` runs on POST /foo/baz 26 // `putty` runs on PUT /foo 27 .use("/foo", sub3);
METHOD
is an HTTP method (GET
, HEAD
, POST
, PUT
, PATCH
, DELETE
, OPTIONS
, TRACE
) in lowercase.
pattern
(optional) - match routes based on supported pattern or match any if omitted.
fn
(s) are functions of (req, res[, next])
.
1router.get("/api/user", (req, res, next) => { 2 res.json(req.user); 3}); 4router.post("/api/users", (req, res, next) => { 5 res.end("User created"); 6}); 7router.put("/api/user/:id", (req, res, next) => { 8 // https://nextjs.org/docs/routing/dynamic-routes 9 res.end(`User ${req.params.id} updated`); 10}); 11 12// Next.js already handles routing (including dynamic routes), we often 13// omit `pattern` in `.METHOD` 14router.get((req, res, next) => { 15 res.end("This matches whatever route"); 16});
Note You should understand Next.js file-system based routing. For example, having a
router.put("/api/foo", handler)
insidepage/api/index.js
does not serve that handler at/api/foo
.
Same as .METHOD but accepts any methods.
Create a handler to handle incoming requests.
options.onError
Accepts a function as a catch-all error handler; executed whenever a handler throws an error.
By default, it responds with a generic 500 Internal Server Error
while logging the error to console
.
1function onError(err, req, res) { 2 logger.log(err); 3 // OR: console.error(err); 4 5 res.status(500).end("Internal server error"); 6} 7 8export default router.handler({ onError });
options.onNoMatch
Accepts a function of (req, res)
as a handler when no route is matched.
By default, it responds with a 404
status and a Route [Method] [Url] not found
body.
1function onNoMatch(req, res) { 2 res.status(404).end("page is not found... or is it!?"); 3} 4 5export default router.handler({ onNoMatch });
Runs req
and res
through the middleware chain and returns a promise. It resolves with the value returned from handlers.
1router 2 .use(async (req, res, next) => { 3 return (await next()) + 1; 4 }) 5 .use(async () => { 6 return (await next()) + 2; 7 }) 8 .use(async () => { 9 return 3; 10 }); 11 12console.log(await router.run(req, res)); 13// The above will print "6"
If 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:
1router 2 .use(async (req, res, next) => { 3 return next().catch(errorHandler); 4 }) 5 .use(thisMiddlewareMightThrow); 6 7await router.run(req, res);
There are some pitfalls in using next-connect
. Below are things to keep in mind to use it correctly.
await next()
If next()
is not awaited, errors will not be caught if they are thrown in async handlers, leading to UnhandledPromiseRejection
.
1// OK: we don't use async so no need to await 2router 3 .use((req, res, next) => { 4 next(); 5 }) 6 .use((req, res, next) => { 7 next(); 8 }) 9 .use(() => { 10 throw new Error("💥"); 11 }); 12 13// BAD: This will lead to UnhandledPromiseRejection 14router 15 .use(async (req, res, next) => { 16 next(); 17 }) 18 .use(async (req, res, next) => { 19 next(); 20 }) 21 .use(async () => { 22 throw new Error("💥"); 23 }); 24 25// GOOD 26router 27 .use(async (req, res, next) => { 28 await next(); // next() is awaited, so errors are caught properly 29 }) 30 .use((req, res, next) => { 31 return next(); // this works as well since we forward the rejected promise 32 }) 33 .use(async () => { 34 throw new Error("💥"); 35 // return new Promise.reject("💥"); 36 });
Another issue is that the handler would resolve before all the code in each layer runs.
1const handler = router 2 .use(async (req, res, next) => { 3 next(); // this is not returned or await 4 }) 5 .get(async () => { 6 // simulate a long task 7 await new Promise((resolve) => setTimeout(resolve, 1000)); 8 res.send("ok"); 9 console.log("request is completed"); 10 }) 11 .handler(); 12 13await handler(req, res); 14console.log("finally"); // this will run before the get layer gets to finish 15 16// This will result in: 17// 1) "finally" 18// 2) "request is completed"
router
like the below pattern:1// api-libs/base.js 2export default createRouter().use(a).use(b); 3 4// api/foo.js 5import router from "api-libs/base"; 6export default router.get(x).handler(); 7 8// api/bar.js 9import router from "api-libs/base"; 10export default router.get(y).handler();
This is because, in each API Route, the same router instance is mutated, leading to undefined behaviors.
If you want to achieve something like that, you can use router.clone
to return different instances with the same routes populated.
1// api-libs/base.js 2export default createRouter().use(a).use(b); 3 4// api/foo.js 5import router from "api-libs/base"; 6export default router.clone().get(x).handler(); 7 8// api/bar.js 9import router from "api-libs/base"; 10export default router.clone().get(y).handler();
res.(s)end
or res.redirect
inside getServerSideProps
.1// page/index.js 2const handler = createRouter() 3 .use((req, res) => { 4 // BAD: res.redirect is not a function (not defined in `getServerSideProps`) 5 // See https://github.com/hoangvvo/next-connect/issues/194#issuecomment-1172961741 for a solution 6 res.redirect("foo"); 7 }) 8 .use((req, res) => { 9 // BAD: `getServerSideProps` gives undefined behavior if we try to send a response 10 res.end("bar"); 11 }); 12 13export async function getServerSideProps({ req, res }) { 14 await router.run(req, res); 15 return { 16 props: {}, 17 }; 18}
handler()
directly in getServerSideProps
.1// page/index.js 2const router = createRouter().use(foo).use(bar); 3const handler = router.handler(); 4 5export async function getServerSideProps({ req, res }) { 6 await handler(req, res); // BAD: You should call router.run(req, res); 7 return { 8 props: {}, 9 }; 10}
Please see my contributing.md.
No vulnerabilities found.
Reason
no binaries found in the repo
Reason
license file detected
Details
Reason
Found 1/21 approved changesets -- score normalized to 0
Reason
0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0
Reason
no effort to earn an OpenSSF best practices badge detected
Reason
security policy file not detected
Details
Reason
project is not fuzzed
Details
Reason
SAST tool is not run on all commits -- score normalized to 0
Details
Reason
13 existing vulnerabilities detected
Details
Score
Last Scanned on 2024-11-18
The Open Source Security Foundation is a cross-industry collaboration to improve the security of open source software (OSS). The Scorecard provides security health metrics for open source projects.
Learn More@next-cors/next-connect
next-cors middleware for next-connect
union
A hybrid buffered / streaming middleware kernel backwards compatible with connect.
http-auth-connect
Connect framework integration with http-auth module.
request-ip
A small Node.js module to retrieve the request's IP address