Skilllibrary express-node
Guides Express.js API development: middleware chain ordering, Router composition, error-handling middleware, request validation (zod/joi/express-validator), async error propagation, security hardening with helmet/cors, TypeScript integration, graceful shutdown, and PM2 clustering.
install
source · Clone the upstream repo
git clone https://github.com/merceralex397-collab/skilllibrary
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/merceralex397-collab/skilllibrary "$T" && mkdir -p ~/.claude/skills && cp -r "$T/09-backend-api-and-data/express-node" ~/.claude/skills/merceralex397-collab-skilllibrary-express-node && rm -rf "$T"
manifest:
09-backend-api-and-data/express-node/SKILL.mdsource content
Purpose
Guides the design and implementation of Express.js HTTP services—from middleware stack composition through route validation, error handling, TypeScript integration, security header configuration, and production deployment. Ensures Express-specific patterns like middleware ordering, 4-argument error handlers, and async error propagation are applied correctly.
When to use this skill
Use this skill when:
- building or modifying an Express.js API (JavaScript or TypeScript)
- composing middleware chains (body parsing, auth, validation, error handling)
- adding request validation with zod, joi, or express-validator
- configuring security headers with helmet, CORS policies, or rate limiting
- setting up async error handling to prevent unhandled promise rejections
- implementing graceful shutdown for containerized or clustered deployments
- writing Express route tests with supertest
Do not use this skill when
- the project uses a different Node.js framework (Fastify, Hapi, Koa)—adapt or skip
- the task is frontend React/Vue/Angular with no Express backend—prefer frontend skills
- the task is Python web (Flask/FastAPI/Django)—prefer
,flask
, orfastapi
skillspython - the task is purely database schema design—prefer
ororm-patternspostgresql
Operating procedure
- Audit the middleware stack. Verify ordering: body parsers first (
,express.json()
), then security middleware (express.urlencoded()
,helmet()
), then auth middleware, then routes, then the error-handling middleware last.cors() - Organize routes with Router. Each resource or domain gets its own
instance in a separate file. Mount routers withRouter
.app.use("/api/v1/users", userRouter) - Add input validation. Every route that accepts user input must validate before processing. Use zod schemas, joi, or express-validator. Create reusable validation middleware.
- Handle async errors. Wrap async route handlers to catch rejected promises. Use
(require at top) or a manual wrapper:express-async-errors
.const asyncHandler = (fn) => (req, res, next) => Promise.resolve(fn(req, res, next)).catch(next) - Implement the error-handling middleware. Define a 4-argument function
as the LAST middleware. Log the error, set the status code, and return a consistent error response shape.(err, req, res, next) - Configure security. Enable
for security headers. Configure CORS with an explicit origin allowlist. Addhelmet()
to protect against abuse.express-rate-limit - Set up graceful shutdown. Listen for
andSIGTERM
. CallSIGINT
to stop accepting new connections, then wait for in-flight requests to complete beforeserver.close()
.process.exit(0) - Write tests. Use
to test routes without starting a real server. Test happy paths, validation failures, auth rejection, and error handler responses.supertest - Verify production readiness. Confirm
, error stack traces are not leaked to clients, health check endpoint exists, and the process manager (PM2/Docker) is configured for restarts.NODE_ENV=production
Decision rules
- Middleware order matters: parsers → security → auth → routes → error handler. Violating this causes silent failures.
- Always use the 4-argument
signature for error-handling middleware—Express uses the arity to distinguish it.(err, req, res, next) - Never use
after route definitions—body will be unparsed.app.use(express.json()) - Prefer
for route grouping over defining all routes onRouter
directly.app - Return consistent error shapes:
.{ error: string, message: string, statusCode: number } - Use
orhttp-errors
for operational errors in routes.createError(status, message) - Set
when running behind a reverse proxy (nginx, ALB) sotrust proxy
is correct.req.ip - Use
on auth endpoints at minimum; apply globally for public APIs.express-rate-limit - For TypeScript, augment
interface to type custom properties (e.g.,Request
).req.user - Never call
orres.send()
afterres.json()
—the error handler owns the response.next(err)
Output requirements
— ordered list of middleware, mount pointsMiddleware Stack
— router file, method, path, validation, handlerRoute Implementation
— error middleware, error shapes, status codesError Handling
— helmet config, CORS policy, rate limitsSecurity
— test commands, supertest assertions, health checkVerification
References
Read these only when relevant:
— middleware, router, error, async, TypeScript patternsreferences/implementation-patterns.md
— pre-deploy and pre-merge verification itemsreferences/validation-checklist.md
— common Express runtime errors and their fixesreferences/failure-modes.md
Related skills
— OpenAPI/Swagger specification and contract testingapi-contracts
— database integration and connection pooling for Nodepostgresql
— advanced rate limiting and retry strategiesrate-limits-retries
— structured logging for Node.js servicesobservability-logging
— WebSocket integration with Expressrealtime-websocket
Anti-patterns
- Middleware soup. Defining all middleware, routes, and error handlers in a single
file. Extract routers and middleware into separate modules.app.js - Forgotten
. Catching an error in an async handler and sending a response manually instead of callingnext(err)
, bypassing centralized error handling and logging.next(err) - Error handler in the wrong position. Registering the
middleware before routes, so it never receives errors from route handlers.(err, req, res, next) - Blocking the event loop. Running CPU-intensive synchronous operations (crypto, JSON parsing of huge payloads, image processing) in a route handler. Offload to a worker thread or external service.
afterres.send()
. Attempting to send a response twice (e.g., forgettingres.send()
afterreturn
in an if-block), causingres.send()
.ERR_HTTP_HEADERS_SENT- Swallowing async errors. Using
route handlers without a wrapper, causing unhandled promise rejections that crash the process in Node 15+.async - Leaking stack traces. Sending
in production error responses, exposing internal file paths and code structure.err.stack - Global mutable state. Storing request-scoped data in module-level variables, causing data leakage between concurrent requests.
Failure handling
- If a route returns 404 unexpectedly, verify the router is mounted with
and the HTTP method matches (app.use()
vsapp.get
).app.post - If the error handler is never invoked, check that it has exactly 4 parameters and is registered after all routes.
- If
isreq.body
, ensureundefined
middleware is mounted before the route.express.json() - If CORS preflight requests fail, verify
is mounted before routes and thecors()
method is handled.OPTIONS - If the process crashes on unhandled rejection, add
or wrap async handlers with theexpress-async-errors
pattern.asyncHandler - If PM2 shows repeated restarts, check for synchronous throws or unhandled rejections in startup code.