express

Express Core Knowledge

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "express" with this command: npx skills add claude-dev-suite/claude-dev-suite/claude-dev-suite-claude-dev-suite-express

Express Core Knowledge

Deep Knowledge: Use mcp__documentation__fetch_docs with technology: express for comprehensive documentation.

Basic Setup

import express from 'express'; import cors from 'cors'; import helmet from 'helmet';

const app = express();

// Middleware app.use(helmet()); app.use(cors()); app.use(express.json());

// Routes app.use('/api/users', userRoutes);

// Error handler (must be last) app.use(errorHandler);

app.listen(3000);

Route Patterns

import { Router } from 'express';

const router = Router();

router.get('/', async (req, res, next) => { try { const users = await db.users.findMany(); res.json(users); } catch (err) { next(err); } });

router.post('/', async (req, res, next) => { try { const user = await db.users.create(req.body); res.status(201).json(user); } catch (err) { next(err); } });

router.get('/:id', async (req, res, next) => { const user = await db.users.find(req.params.id); if (!user) return res.status(404).json({ error: 'Not found' }); res.json(user); });

Middleware Pattern

// Auth middleware const authenticate = async (req, res, next) => { const token = req.headers.authorization?.split(' ')[1]; if (!token) return res.status(401).json({ error: 'Unauthorized' });

try { req.user = await verifyToken(token); next(); } catch { res.status(401).json({ error: 'Invalid token' }); } };

router.get('/protected', authenticate, handler);

Error Handler

const errorHandler = (err, req, res, next) => { console.error(err.stack); res.status(err.status || 500).json({ error: err.message || 'Internal Server Error' }); };

Production Readiness

Security Configuration

import express from 'express'; import helmet from 'helmet'; import cors from 'cors'; import rateLimit from 'express-rate-limit'; import slowDown from 'express-slow-down'; import hpp from 'hpp';

const app = express();

// Security headers app.use(helmet({ contentSecurityPolicy: { directives: { defaultSrc: ["'self'"], styleSrc: ["'self'", "'unsafe-inline'"], scriptSrc: ["'self'"], }, }, hsts: { maxAge: 31536000, includeSubDomains: true }, }));

// CORS app.use(cors({ origin: process.env.ALLOWED_ORIGINS?.split(',') || [], credentials: true, methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'], }));

// Rate limiting const limiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 100, // 100 requests per window standardHeaders: true, legacyHeaders: false, message: { error: 'Too many requests' }, }); app.use('/api', limiter);

// Slow down repeated requests const speedLimiter = slowDown({ windowMs: 15 * 60 * 1000, delayAfter: 50, delayMs: (hits) => hits * 100, }); app.use('/api', speedLimiter);

// Prevent HTTP Parameter Pollution app.use(hpp());

// Body parsing with limits app.use(express.json({ limit: '10kb' })); app.use(express.urlencoded({ extended: true, limit: '10kb' }));

// Trust proxy (for rate limiting behind reverse proxy) app.set('trust proxy', 1);

Health Checks

// Health endpoint app.get('/health', (req, res) => { res.json({ status: 'healthy', timestamp: new Date().toISOString() }); });

// Readiness endpoint (check dependencies) app.get('/ready', async (req, res) => { try { await db.query('SELECT 1'); res.json({ status: 'ready', database: 'connected' }); } catch (error) { res.status(503).json({ status: 'not ready', database: 'disconnected' }); } });

// Liveness endpoint app.get('/live', (req, res) => { res.status(200).send('OK'); });

Structured Logging

import pino from 'pino-http';

const logger = pino({ level: process.env.LOG_LEVEL || 'info', redact: ['req.headers.authorization', 'req.body.password'], serializers: { req: (req) => ({ method: req.method, url: req.url, query: req.query, params: req.params, }), }, });

app.use(logger);

Monitoring Metrics

Metric Alert Threshold

Request latency p99

500ms

Error rate (5xx)

1%

Memory usage

80%

Event loop lag

100ms

Active handles

1000

Graceful Shutdown

const server = app.listen(PORT);

const gracefulShutdown = async (signal: string) => { console.log(${signal} received, shutting down gracefully);

server.close(async () => { console.log('HTTP server closed'); await db.disconnect(); process.exit(0); });

// Force shutdown after 30s setTimeout(() => { console.error('Forced shutdown'); process.exit(1); }, 30000); };

process.on('SIGTERM', () => gracefulShutdown('SIGTERM')); process.on('SIGINT', () => gracefulShutdown('SIGINT'));

Error Handler (Production)

const errorHandler = (err, req, res, next) => { // Log error req.log.error({ err, method: req.method, url: req.url, body: req.body, });

// Don't leak error details in production const statusCode = err.statusCode || err.status || 500; const message = statusCode === 500 && process.env.NODE_ENV === 'production' ? 'Internal Server Error' : err.message;

res.status(statusCode).json({ error: message, ...(process.env.NODE_ENV !== 'production' && { stack: err.stack }), }); };

// Must be last middleware app.use(errorHandler);

Checklist

  • Helmet security headers enabled

  • CORS properly configured

  • Rate limiting on API endpoints

  • Body size limits configured

  • Input validation (express-validator/joi)

  • HPP protection enabled

  • Structured logging (no console.log)

  • Health/readiness/liveness endpoints

  • Graceful shutdown handling

  • Error details hidden in production

  • HTTPS/TLS in production

  • Trust proxy configured (if behind LB)

When NOT to Use This Skill

  • Enterprise Architecture: Use NestJS for dependency injection, decorators, and modular design

  • Maximum Performance: Use Fastify for schema-based validation and faster throughput

  • Edge Runtimes: Use Hono for Cloudflare Workers, Vercel Edge, or edge-first design

  • Type-Safe APIs: Consider tRPC with Express or use Fastify/NestJS for better TypeScript integration

  • WebSocket Implementation: Use dedicated WebSocket skill (coming soon)

  • GraphQL: Defer to graphql-expert for Apollo Server setup

  • Database Queries: Use prisma-expert or sql-expert for ORM/query specifics

Anti-Patterns

Anti-Pattern Why It's Bad Correct Approach

Using app.get('*') before specific routes Catches all requests, routes unreachable Place catch-all routes last

Not using next() in middleware Request hangs, no response sent Always call next() or send response

Synchronous error throwing without try-catch Crashes server Wrap in try-catch, use next(err)

Using res.send() multiple times "Headers already sent" error Send response once per request

Not setting trust proxy behind load balancer Wrong client IP, rate limiting fails Set app.set('trust proxy', 1)

Parsing body without size limits DoS vulnerability Set limit: '10kb' in body parsers

Using app.use(express.static()) without path Serves entire filesystem Specify explicit public directory

Mixing callback and promise styles Inconsistent error handling Use async/await consistently

Quick Troubleshooting

Issue Likely Cause Solution

"Cannot set headers after sent" Multiple res.send() calls Ensure only one response per request

Middleware not executing Registered after routes Move app.use() before route definitions

404 for all routes Routes defined after app.listen()

Define routes before calling listen()

Request hangs indefinitely Middleware missing next()

Add next() or send response

CORS errors despite cors middleware Middleware order issue Place app.use(cors()) before routes

Rate limiting not working Behind proxy without trust proxy Set app.set('trust proxy', 1)

Body parser returns undefined Wrong content-type header Ensure Content-Type: application/json

Static files not serving Wrong path configuration Use absolute path: express.static(path.join(__dirname, 'public'))

WebSocket Integration

Note: For WebSocket functionality, consider using a dedicated WebSocket library alongside Express.

Setup with ws

import express from 'express'; import { createServer } from 'http'; import { WebSocketServer, WebSocket } from 'ws';

const app = express(); const server = createServer(app); const wss = new WebSocketServer({ server });

// Connection handling wss.on('connection', (ws: WebSocket, req) => { const userId = new URL(req.url!, http://${req.headers.host}).searchParams.get('userId'); console.log(Client connected: ${userId});

ws.on('message', (data) => { const message = JSON.parse(data.toString()); handleMessage(ws, message); });

ws.on('close', () => { console.log(Client disconnected: ${userId}); });

ws.on('error', (error) => { console.error('WebSocket error:', error); }); });

server.listen(3000);

Authentication

import { WebSocketServer } from 'ws'; import jwt from 'jsonwebtoken';

const wss = new WebSocketServer({ server, verifyClient: async ({ req }, done) => { const token = req.url?.split('token=')[1]; if (!token) return done(false, 401, 'Unauthorized');

try {
  const decoded = jwt.verify(token, process.env.JWT_SECRET!);
  (req as any).user = decoded;
  done(true);
} catch {
  done(false, 401, 'Invalid token');
}

}, });

Room Management

const rooms = new Map<string, Set<WebSocket>>();

function joinRoom(ws: WebSocket, roomId: string) { if (!rooms.has(roomId)) { rooms.set(roomId, new Set()); } rooms.get(roomId)!.add(ws); }

function leaveRoom(ws: WebSocket, roomId: string) { rooms.get(roomId)?.delete(ws); if (rooms.get(roomId)?.size === 0) { rooms.delete(roomId); } }

function broadcastToRoom(roomId: string, message: object, exclude?: WebSocket) { const clients = rooms.get(roomId); if (!clients) return;

const data = JSON.stringify(message); clients.forEach((client) => { if (client !== exclude && client.readyState === WebSocket.OPEN) { client.send(data); } }); }

Heartbeat & Connection Health

const HEARTBEAT_INTERVAL = 30000;

wss.on('connection', (ws: WebSocket) => { (ws as any).isAlive = true;

ws.on('pong', () => { (ws as any).isAlive = true; }); });

const heartbeat = setInterval(() => { wss.clients.forEach((ws) => { if ((ws as any).isAlive === false) { return ws.terminate(); } (ws as any).isAlive = false; ws.ping(); }); }, HEARTBEAT_INTERVAL);

wss.on('close', () => clearInterval(heartbeat));

Message Protocol

interface WSMessage { type: string; payload: unknown; timestamp: number; }

function handleMessage(ws: WebSocket, message: WSMessage) { switch (message.type) { case 'join_room': joinRoom(ws, message.payload as string); break; case 'leave_room': leaveRoom(ws, message.payload as string); break; case 'broadcast': broadcastToRoom( (message.payload as any).roomId, (message.payload as any).data ); break; default: ws.send(JSON.stringify({ type: 'error', payload: 'Unknown message type' })); } }

Scaling with Redis

import { createClient } from 'redis';

const pub = createClient({ url: process.env.REDIS_URL }); const sub = pub.duplicate();

await pub.connect(); await sub.connect();

// Subscribe to channel await sub.subscribe('ws:broadcast', (message) => { const data = JSON.parse(message); wss.clients.forEach((client) => { if (client.readyState === WebSocket.OPEN) { client.send(JSON.stringify(data)); } }); });

// Publish from any server instance function publishMessage(channel: string, message: object) { pub.publish(channel, JSON.stringify(message)); }

Reference Documentation

  • Middleware Patterns

  • Error Handling

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

cron-scheduling

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

token-optimization

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

express

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

webrtc

No summary provided by upstream source.

Repository SourceNeeds Review