| name | nodejs-express |
| description | Node.js + Express backend patterns. Use when building REST APIs, middleware, authentication, or backend services with Node.js/Express/TypeScript. |
Node.js + Express Patterns
Project Structure
src/
app.ts # Express app, middleware setup
server.ts # HTTP server, port binding
routes/ # Route definitions (auth.ts, users.ts)
controllers/ # Request handlers
services/ # Business logic
models/ # Prisma/Mongoose models
middleware/ # auth, error, validation
types/ # TypeScript interfaces
utils/ # Helpers
App Setup
import express from 'express'
import helmet from 'helmet'
import cors from 'cors'
import { errorHandler } from './middleware/error'
const app = express()
app.use(helmet())
app.use(cors({ origin: process.env.ALLOWED_ORIGINS?.split(','), credentials: true }))
app.use(express.json({ limit: '1mb' }))
app.use(express.urlencoded({ extended: true }))
app.use('/api/auth', authRouter)
app.use('/api/users', authenticate, usersRouter)
app.get('/health', (req, res) => res.json({ status: 'ok' }))
app.use(errorHandler)
export default app
Controller Pattern (async error handling)
const asyncHandler = (fn: RequestHandler): RequestHandler =>
(req, res, next) => Promise.resolve(fn(req, res, next)).catch(next)
export const getUser = asyncHandler(async (req, res) => {
const user = await userService.findById(Number(req.params.id))
if (!user) return res.status(404).json({ error: 'User not found' })
res.json(user)
})
Auth Middleware
import jwt from 'jsonwebtoken'
export const authenticate: RequestHandler = (req, res, next) => {
const token = req.headers.authorization?.split(' ')[1]
if (!token) return res.status(401).json({ error: 'No token' })
try {
const payload = jwt.verify(token, process.env.JWT_SECRET!) as JwtPayload
req.user = payload
next()
} catch {
res.status(401).json({ error: 'Invalid token' })
}
}
Validation Middleware (Zod)
import { z } from 'zod'
const validate = (schema: z.ZodSchema) => (req: Request, res: Response, next: NextFunction) => {
const result = schema.safeParse(req.body)
if (!result.success) return res.status(400).json({ errors: result.error.flatten() })
req.body = result.data
next()
}
const CreateUserSchema = z.object({
email: z.string().email(),
password: z.string().min(8),
name: z.string().max(100)
})
router.post('/users', validate(CreateUserSchema), createUser)
Error Handler
export const errorHandler: ErrorRequestHandler = (err, req, res, next) => {
console.error(err)
const status = err.status || 500
const message = status < 500 ? err.message : 'Internal server error'
res.status(status).json({ error: message })
}
Rules
- Use
helmet() for security headers
- Use
express-rate-limit on auth endpoints
- Validate ALL input with Zod (never trust req.body directly)
- Wrap all async handlers in asyncHandler (never unhandled promise rejections)
- Keep controllers thin — business logic in services
- Use
process.env for config, validate at startup (crash fast if missing)
- Return 404 for missing resources, 400 for bad input, 401/403 for auth, 500 for server errors