hoangvvo / Next Connect
Programming Languages
Projects that are alternatives of or similar to Next Connect
next-connect
The smol method routing and middleware for other frameworks). Powered by trouter.
Features
- Compatible with Express.js middleware and router => Drop-in replacement for Express.js.
- Lightweight (~ 3KB) => Suitable for serverless environment.
- 5x faster than Express.js with no overhead
- Works with async handlers (with error catching)
- TypeScript support
Installation
npm install next-connect
// or
yarn add next-connect
Usage
next-connect
is often used in API Routes:
// pages/api/hello.js
import nc from "next-connect";
const handler = nc()
.use(someMiddleware())
.get((req, res) => {
res.send("Hello world");
})
.post((req, res) => {
res.json({ hello: "world" });
})
.put(async (req, res) => {
res.end("async/await is also supported!");
})
.patch(async (req, res) => {
throw new Error("Throws me around! Error can be caught and handled.");
});
export default handler;
For quick migration from match multiple routes recipe.
For usage in pages with getServerSideProps
, see .run
.
See an example in nextjs-mongodb-app (CRUD, Authentication with Passport, and more)
TypeScript
By default, the base interfaces of req
and res
are IncomingMessage
and ServerResponse
. When using in API Routes, you would set them to NextApiRequest
and NextApiResponse
by providing the generics to the factory function like so:
import { NextApiRequest, NextApiResponse } from "next";
import nc from "next-connect";
const handler = nc<NextApiRequest, NextApiResponse>();
In each handler, you can also define custom properties to req
and res
(such as req.user
or res.cookie
) like so:
interface ExtendedRequest {
user: string;
}
interface ExtendedResponse {
cookie(name: string, value: string): void;
}
handler.post<ExtendedRequest, ExtendedResponse>((req, res) => {
req.user = "Anakin";
res.cookie("sid", "8108");
});
API
The API is similar to Express.js with several differences:
- It does not include any helper methods or template engine (you can incorporate them using middleware).
- It does not support error-handling middleware pattern. Use
options.onError
instead.
It is more like good ol' connect (hence the name) with method routing.
nc(options)
Initialize an instance of next-connect
.
options.onError
Accepts a function as a catch-all error handler; executed whenever a middleware throws an error.
By default, it responds with status code 500
and an error message if any.
function onError(err, req, res, next) {
logger.log(err);
res.status(500).end(err.toString());
// OR: you may want to continue
next();
}
const handler = nc({ onError });
handler
.use((req, res, next) => {
throw new Error("oh no!");
// or use next
next(Error("oh no"));
})
.use((req, res) => {
// this will run if next() is called in onError
res.end("error no more");
});
options.onNoMatch
Accepts a function of (req, res)
as a handler when no route is matched.
By default, it responds with 404
status and not found
body.
function onNoMatch(req, res) {
res.status(404).end("page is not found... or is it");
}
const handler = nc({ onNoMatch });
options.attachParams
Passing true
will attach params
object to req
. By default, it does not set to req.params
.
const handler = nc({ attachParams: true });
handler.get("/users/:userId/posts/:postId", (req, res) => {
// Visiting '/users/12/posts/23' will render '{"userId":"12","postId":"23"}'
res.send(req.params);
});
.use(base, ...fn)
base
(optional) - match all route to the right of base
or match all if omitted.
fn
(s) are functions of (req, res[, next])
or an instance of next-connect
, where it will act as a sub application.
// Mount a middleware function
handler.use((req, res, next) => {
req.hello = "world";
next(); // call to proceed to next in chain
});
// Or include a base
handler.use("/foo", fn); // Only run in /foo/**
// Mount an instance of next-connect
const common = nc().use(midd1).use("/", midd2); // good for common middlewares
const auth = nc().use("/dashboard", checkAuth);
const subapp = nc().get(getHandle).post("/baz", postHandle).put("/", putHandle);
handler
// `midd1` and `midd2` runs everywhere
.use(common)
// `checkAuth` only runs on /dashboard/*
.use(auth)
// `getHandle` runs on /foo/*
// `postHandle` runs on /foo/baz
// `putHandle` runs on /foo
.use("/foo", subapp);
// You can use a library too.
handler.use(passport.initialize());
.METHOD(pattern, ...fns)
METHOD
is a HTTP method (GET
, HEAD
, POST
, PUT
, PATCH
, DELETE
, OPTIONS
, TRACE
) in lowercase.
pattern
(optional) - match all route based on supported pattern or match all if omitted.
fn
(s) are functions of (req, res[, next])
. This is ideal to be used in API Routes.
handler.get("/api/user", (req, res, next) => {
res.json(req.user);
});
handler.post("/api/users", (req, res, next) => {
res.end("User created");
});
handler.put("/api/user/:id", (req, res, next) => {
// https://nextjs.org/docs/routing/dynamic-routes
res.end(`User ${req.query.id} updated`);
});
handler.get((req, res, next) => {
res.end("This matches whatever route");
});
However, since Next.js already handles routing (including dynamic routes), we often omit pattern
in .METHOD
.
.all(pattern, ...fns)
Same as .METHOD but accepts any methods.
.run(req, res)
Runs req
and res
the middleware and returns a promise. It will not render 404
on not found or onError
on error.
This can be useful in getServerSideProps
.
// page/index.js
export async function getServerSideProps({ req, res }) {
const handler = nc().use(passport.initialize()).post(postMiddleware);
try {
await handler.run(req, res);
} catch (e) {
// handle the error
}
// do something with the upgraded req and res
return {
props: { user: req.user },
};
}
Recipes
Next.js
Match multiple routes
If you created the file /api/<specific route>.js
folder, the handler will only run on that specific route.
If you need to create all handlers for all routes in one file (similar to Express.js
). You can use Optional catch all API routes.
// pages/api/[[...slug]].js
import nc from "next-connect";
const handler = nc({ attachParams: true })
.use("/api/hello", someMiddleware())
.get("/api/user/:userId", (req, res) => {
res.send(`Hello ${req.params.userId}`);
});
export default handler;
While this allows quick migration from Express.js, consider seperating routes into different files (/api/user/[userId].js
, /api/hello.js
) in the future.
Using in other frameworks
next-connect
supports any frameworks and runtimes that support (req, res) => void
handler.
Micro
const { send } = require("micro");
const nc = require("next-connect");
module.exports = nc()
.use(middleware)
.get((req, res) => {
res.end("Hello World!");
})
.post((req, res) => {
send(res, 200, { hello: "world" });
});
Vercel
const nc = require("next-connect");
module.exports = nc()
.use(middleware)
.get((req, res) => {
res.send("Hello World!");
})
.post((req, res) => {
res.json({ hello: "world" });
});
Node.js HTTP / HTTP2 Server
const http = require("http");
// const http2 = require('http2');
const nc = require("next-connect");
const handler = nc()
.use(middleware)
.get((req, res) => {
res.end("Hello world");
})
.post((req, res) => {
res.setHeader("content-type", "application/json");
res.end(JSON.stringify({ hello: "world" }));
});
http.createServer(handler).listen(PORT);
// http2.createServer(handler).listen(PORT);
Contributing
Please see my contributing.md.