协作头脑风暴
brainstorming
在实现前澄清需求、探索设计选择,并形成清晰的开发方向。
koa【开发与工程】Koa is a minimalist Node.js web framework from the team behind Express, built around async/await middleware and a single context object. Use when a user asks to build a REST API or web service with Koa, add routing, body parsing, CORS or error handling to a Koa app, write custom middleware, or migrate from Express or from Koa 2 to Koa 3.
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
Koa (current major version 3.x) is a small framework: it ships the application object, the ctx context (request plus response helpers) and the middleware pipeline, and nothing else. Routing, body parsing, CORS and logging are separate packages you add. Middleware are async functions that call await next(); code before the call runs on the way down, code after it runs on the way back up (the "onion" model).
Koa 3 requires Node.js 18 or newer, no longer accepts generator-function middleware (koa-convert is gone), and builds ctx.query with URLSearchParams. @koa/router 15 requires Node.js 20 or newer.
npm install koa @koa/router @koa/bodyparser @koa/cors koa-logger
npm install -D @types/koa @types/koa-logger @types/koa__cors # TypeScript only
@koa/router ships its own types, so do not install @types/koa__router. Koa itself has no bundled types: add @types/koa. koa-compose comes in as a dependency of Koa; install it explicitly if you import it directly.
import Koa from "koa";
import Router from "@koa/router";
import { bodyParser } from "@koa/bodyparser"; // named export, not default
import cors from "@koa/cors";
import logger from "koa-logger";
const app = new Koa();
const router = new Router({ prefix: "/api" });
// 1. Error handler goes first so it wraps everything below it
app.use(async (ctx, next) => {
try {
await next();
} catch (err) {
ctx.status = err.status || 500;
ctx.body = { error: err.expose ? err.message : "Internal Server Error" };
ctx.app.emit("error", err, ctx); // still reaches app.on("error")
}
});
app.use(logger());
app.use(cors());
app.use(bodyParser()); // JSON and urlencoded by default
router.post("/orders", async (ctx) => {
const { customerId, items } = ctx.request.body;
if (!customerId || !Array.isArray(items)) {
ctx.throw(400, "customerId and items are required");
}
ctx.status = 201;
ctx.body = { id: "ord_1042", customerId, itemCount: items.length };
});
router.get("/orders/:id", async (ctx) => {
ctx.assert(ctx.params.id.startsWith("ord_"), 404, "Order not found");
ctx.body = { id: ctx.params.id };
});
app.use(router.routes()).use(router.allowedMethods()); // 405/OPTIONS handling
app.on("error", (err) => console.error("unhandled", err));
app.listen(3000, () => console.log("listening on :3000"));
ctx.request.body — parsed body (set by the body parser; {} when nothing was parsed). Parsing only runs for POST, PUT and PATCH.ctx.body = value — object or array becomes JSON, string becomes text/HTML, a stream is piped. Setting a body on a status-less response sets 200.ctx.status, ctx.set(name, value), ctx.get(name), ctx.query, ctx.params (router), ctx.state (per-request scratch space shared by middleware).ctx.throw(status, message) creates an HTTP error; with 4xx statuses the message is exposed to the client (err.expose), with 5xx it is not. ctx.assert(condition, status, message) is the one-line form.ctx.throw(status, error, properties) — pass an Error rather than relying on string-only forms.ctx.back() replaces ctx.redirect("back") in Koa 3.app.proxy = true only behind a trusted reverse proxy, so ctx.ip and ctx.protocol honour X-Forwarded-*./:id(\\d+)) no longer works and throws at startup; validate in middleware, router.param() or createParameterValidationMiddleware./users{/:id}. Wildcard capture: /files/*path gives ctx.params.path.apiRouter.use("/users", usersRouter.routes()).router.use(mw) registers middleware for the routes of that router only.import compose from "koa-compose";
const requireAdmin = compose([requireAuth, requireRole("admin"), auditLog]);
router.delete("/users/:id", requireAdmin, deleteUser);
async function requireAuth(ctx, next) {
const token = ctx.get("Authorization").replace(/^Bearer /, "");
if (!token) ctx.throw(401, "Missing bearer token");
ctx.state.user = await verifyToken(token); // your own verification
await next();
}
@koa/bodyparser does not parse multipart. Use @koa/multer (or busboy) for file uploads. Other types are opt-in: bodyParser({ enableTypes: ["json", "form", "text"], jsonLimit: "2mb" }). Defaults: jsonLimit and textLimit 1mb, formLimit 56kb.
User request: "Add a /health route to my Koa app and a header with how long each request took."
app.use(async (ctx, next) => {
const start = process.hrtime.bigint();
await next(); // everything downstream runs here
const ms = Number(process.hrtime.bigint() - start) / 1e6;
ctx.set("X-Response-Time", `${ms.toFixed(1)}ms`);
});
router.get("/health", (ctx) => {
ctx.body = { status: "ok", uptimeSeconds: Math.round(process.uptime()) };
});
Check it: curl -i http://localhost:3000/api/health returns 200, a JSON body such as {"status":"ok","uptimeSeconds":12} and an X-Response-Time: 0.4ms header.
User request: "Convert this Express route to Koa: app.post('/login', (req, res, next) => { ... res.status(401).json({error:'bad credentials'}) })."
router.post("/login", async (ctx) => {
const { email, password } = ctx.request.body;
const user = await users.findByEmail(email);
if (!user || !(await verifyPassword(user, password))) {
ctx.throw(401, "bad credentials"); // replaces res.status(401).json(...) and next(err)
}
ctx.body = { token: await issueToken(user) };
});
Result: the request returns 401 {"error":"bad credentials"} through the error-handling middleware shown above; there is no next(err) call, errors propagate as rejected promises.
await next(). A forgotten await makes downstream work run after the response is sent.ctx.body after the response has been written; use ctx.respond = false only when you write to ctx.res yourself.ctx.query arrays, and update ctx.throw and redirect("back") calls.@koa/router to v13 or newer: remove :param(regex) routes and old * wildcards, and drop @types/koa__router.@koa/bodyparser and @koa/cors (the maintained scoped packages) in new code; koa-bodyparser is the older package.koa-static, a schema validator or koa-session deliberately and pin versions.