Express.js HTTP APIs AI Skill Guide
Overview & Engine Architecture
Express is a minimal Node HTTP framework built around middleware chains and routers. Request flows top-to-bottom; the first matching route wins unless next() continues. Agents keep middleware ordered correctly (parsers before handlers, error middleware last), wrap async routes so rejections reach the error handler, and validate input before business logic.
req -> middleware... -> router -> handler
\-> next(err) -> error middleware -> res
When to use this skill
- Building REST/JSON APIs on Node
- Splitting apps into domain routers
- Fixing hanging requests from unhandled async errors
- Adding auth, logging, and rate-limit middleware
Operational directives
- Mount
express.json() / urlencoded only where needed; cap body size.
- Put four-arg error middleware
(err, req, res, next) after all routes.
- Wrap async handlers or use a helper so rejected promises call
next(err).
- Use
Router() per domain; keep app.js / server.js thin.
- Never trust
req.body shape - validate with zod/joi or similar.
App sketch
import express from "express";
const app = express();
app.use(express.json({ limit: "100kb" }));
const items = express.Router();
items.get("/", (_req, res) => {
res.json([{ id: 1, sku: "A" }]);
});
items.post("/", (req, res, next) => {
Promise.resolve()
.then(() => {
const sku = req.body?.sku;
if (typeof sku !== "string" || !sku) {
const err = new Error("sku required");
err.status = 400;
throw err;
}
res.status(201).json({ id: 2, sku });
})
.catch(next);
});
app.use("/items", items);
app.use((err, _req, res, _next) => {
const status = err.status ?? 500;
res.status(status).json({ error: err.message ?? "internal error" });
});
app.listen(3000);
Commands
npm install express
node --watch src/server.js
NODE_ENV=production node src/server.js
Common pitfalls
| Pitfall |
Why it hurts |
Fix |
Async throw without next |
Hanging or crash |
Async wrapper / catch |
| Error middleware before routes |
Never runs |
Mount last |
Giant monolithic app.js |
Hard to test |
Domain routers |
| Trusting proxy headers blindly |
Spoofed IPs |
trust proxy + known hops |
Best practices
- Centralize CORS, helmet, and request logging at the edge.
- Return consistent error JSON shapes for clients.
- Prefer explicit status codes (
201, 204, 409).
- Test with supertest against the
app export (not listen in tests).
Limitations
- Express 4 vs 5 middleware and path syntax differ - match package major.
- WebSockets need
ws or Socket.IO alongside Express.
- TypeScript types (
@types/express) must align with the Express major version.
Related skills
@nodejs - runtime, ESM, process hygiene
@graphql-apis - GraphQL layers often mounted on Express
@prisma - persistence behind route handlers
1---2name: express3description: Operational skill for Express.js: routers, middleware order, error handlers, async wrappers, validation, and production app structure.4---56# Express.js HTTP APIs AI Skill Guide78## Overview & Engine Architecture910Express is a minimal Node HTTP framework built around middleware chains and routers. Request flows top-to-bottom; the first matching route wins unless `next()` continues. Agents keep middleware ordered correctly (parsers before handlers, error middleware last), wrap async routes so rejections reach the error handler, and validate input before business logic.1112```13req -> middleware... -> router -> handler14 \-> next(err) -> error middleware -> res15```1617## When to use this skill1819- Building REST/JSON APIs on Node20- Splitting apps into domain routers21- Fixing hanging requests from unhandled async errors22- Adding auth, logging, and rate-limit middleware2324## Operational directives25261. Mount `express.json()` / urlencoded only where needed; cap body size.272. Put four-arg error middleware `(err, req, res, next)` after all routes.283. Wrap async handlers or use a helper so rejected promises call `next(err)`.294. Use `Router()` per domain; keep `app.js` / `server.js` thin.305. Never trust `req.body` shape - validate with zod/joi or similar.3132## App sketch3334```js35import express from "express";3637const app = express();38app.use(express.json({ limit: "100kb" }));3940const items = express.Router();4142items.get("/", (_req, res) => {43 res.json([{ id: 1, sku: "A" }]);44});4546items.post("/", (req, res, next) => {47 Promise.resolve()48 .then(() => {49 const sku = req.body?.sku;50 if (typeof sku !== "string" || !sku) {51 const err = new Error("sku required");52 err.status = 400;53 throw err;54 }55 res.status(201).json({ id: 2, sku });56 })57 .catch(next);58});5960app.use("/items", items);6162app.use((err, _req, res, _next) => {63 const status = err.status ?? 500;64 res.status(status).json({ error: err.message ?? "internal error" });65});6667app.listen(3000);68```6970## Commands7172```bash73npm install express74node --watch src/server.js75NODE_ENV=production node src/server.js76```7778## Common pitfalls7980| Pitfall | Why it hurts | Fix |81| --- | --- | --- |82| Async throw without `next` | Hanging or crash | Async wrapper / catch |83| Error middleware before routes | Never runs | Mount last |84| Giant monolithic `app.js` | Hard to test | Domain routers |85| Trusting proxy headers blindly | Spoofed IPs | `trust proxy` + known hops |8687## Best practices8889- Centralize CORS, helmet, and request logging at the edge.90- Return consistent error JSON shapes for clients.91- Prefer explicit status codes (`201`, `204`, `409`).92- Test with supertest against the `app` export (not `listen` in tests).9394## Limitations9596- Express 4 vs 5 middleware and path syntax differ - match package major.97- WebSockets need `ws` or Socket.IO alongside Express.98- TypeScript types (`@types/express`) must align with the Express major version.99100## Related skills101102- `@nodejs` - runtime, ESM, process hygiene103- `@graphql-apis` - GraphQL layers often mounted on Express104- `@prisma` - persistence behind route handlers