Claude-skill-registry express
Express.js framework patterns including routing, middleware, request/response handling, and Express-specific APIs. Use when working with Express routes, middleware, or Express applications.
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/express-squirrelfishcityhall-claude-code-kit" ~/.claude/skills/majiayu000-claude-skill-registry-express && rm -rf "$T"
manifest:
skills/data/express-squirrelfishcityhall-claude-code-kit/SKILL.mdsource content
Express.js Framework Patterns
Purpose
Essential Express.js patterns for building scalable backend APIs, emphasizing clean routing, middleware composition, and proper request/response handling.
When to Use This Skill
- Creating or modifying Express routes
- Building middleware (auth, validation, error handling)
- Working with Express Request/Response objects
- Implementing BaseController pattern
- Error handling in Express
Clean Route Pattern
Routes Only Route
Routes should ONLY:
- ✅ Define route paths
- ✅ Register middleware
- ✅ Delegate to controllers
Routes should NEVER:
- ❌ Contain business logic
- ❌ Access database directly
- ❌ Implement validation logic
- ❌ Format complex responses
import { Router } from 'express'; import { UserController } from '../controllers/UserController'; import { SSOMiddlewareClient } from '../middleware/SSOMiddleware'; const router = Router(); const controller = new UserController(); // Clean delegation - no business logic router.get('/:id', SSOMiddlewareClient.verifyLoginStatus, async (req, res) => controller.getUser(req, res) ); router.post('/', SSOMiddlewareClient.verifyLoginStatus, async (req, res) => controller.createUser(req, res) ); export default router;
BaseController Pattern
Implementation
import * as Sentry from '@sentry/node'; import { Response } from 'express'; export abstract class BaseController { protected handleError( error: unknown, res: Response, context: string, statusCode = 500 ): void { Sentry.withScope((scope) => { scope.setTag('controller', this.constructor.name); scope.setTag('operation', context); Sentry.captureException(error); }); res.status(statusCode).json({ success: false, error: { message: error instanceof Error ? error.message : 'An error occurred', code: statusCode, }, }); } protected handleSuccess<T>( res: Response, data: T, message?: string, statusCode = 200 ): void { res.status(statusCode).json({ success: true, message, data, }); } protected async withTransaction<T>( name: string, operation: string, callback: () => Promise<T> ): Promise<T> { return await Sentry.startSpan({ name, op: operation }, callback); } protected addBreadcrumb( message: string, category: string, data?: Record<string, any> ): void { Sentry.addBreadcrumb({ message, category, level: 'info', data }); } }
Using BaseController
import { Request, Response } from 'express'; import { BaseController } from './BaseController'; import { UserService } from '../services/userService'; import { createUserSchema } from '../validators/userSchemas'; export class UserController extends BaseController { private userService: UserService; constructor() { super(); this.userService = new UserService(); } async getUser(req: Request, res: Response): Promise<void> { try { this.addBreadcrumb('Fetching user', 'user_controller', { userId: req.params.id }); const user = await this.userService.findById(req.params.id); if (!user) { return this.handleError( new Error('User not found'), res, 'getUser', 404 ); } this.handleSuccess(res, user); } catch (error) { this.handleError(error, res, 'getUser'); } } async createUser(req: Request, res: Response): Promise<void> { try { const validated = createUserSchema.parse(req.body); const user = await this.withTransaction( 'user.create', 'db.query', () => this.userService.create(validated) ); this.handleSuccess(res, user, 'User created successfully', 201); } catch (error) { this.handleError(error, res, 'createUser'); } } }
Middleware Patterns
Authentication
import { Request, Response, NextFunction } from 'express'; import jwt from 'jsonwebtoken'; import { config } from '../config/unifiedConfig'; export class SSOMiddlewareClient { static verifyLoginStatus(req: Request, res: Response, next: NextFunction): void { const token = req.cookies.refresh_token; if (!token) { return res.status(401).json({ error: 'Not authenticated' }); } try { const decoded = jwt.verify(token, config.tokens.jwt); res.locals.claims = decoded; res.locals.effectiveUserId = decoded.sub; next(); } catch (error) { res.status(401).json({ error: 'Invalid token' }); } } }
Audit with AsyncLocalStorage
import { Request, Response, NextFunction } from 'express'; import { AsyncLocalStorage } from 'async_hooks'; import { v4 as uuidv4 } from 'uuid'; export interface AuditContext { userId: string; userName?: string; requestId: string; timestamp: Date; } export const auditContextStorage = new AsyncLocalStorage<AuditContext>(); export function auditMiddleware(req: Request, res: Response, next: NextFunction): void { const context: AuditContext = { userId: res.locals.effectiveUserId || 'anonymous', userName: res.locals.claims?.preferred_username, timestamp: new Date(), requestId: req.id || uuidv4(), }; auditContextStorage.run(context, () => next()); } export function getAuditContext(): AuditContext | null { return auditContextStorage.getStore() || null; }
Error Boundary
import { Request, Response, NextFunction } from 'express'; import * as Sentry from '@sentry/node'; export function errorBoundary( error: Error, req: Request, res: Response, next: NextFunction ): void { const statusCode = error.statusCode || 500; Sentry.captureException(error); res.status(statusCode).json({ success: false, error: { message: error.message, code: error.name, }, }); } // Async wrapper export function asyncErrorWrapper( handler: (req: Request, res: Response, next: NextFunction) => Promise<any> ) { return async (req: Request, res: Response, next: NextFunction) => { try { await handler(req, res, next); } catch (error) { next(error); } }; }
Middleware Ordering
Critical Order
import express from 'express'; import * as Sentry from '@sentry/node'; const app = express(); // 1. Sentry request handler (FIRST) app.use(Sentry.Handlers.requestHandler()); // 2. Body/cookie parsing app.use(express.json()); app.use(cookieParser()); // 3. Routes app.use('/api/users', userRoutes); // 4. Error handler (AFTER routes) app.use(errorBoundary); // 5. Sentry error handler (LAST) app.use(Sentry.Handlers.errorHandler());
Rules:
- Sentry request handler FIRST
- Body/cookie parsers before routes
- Error handlers AFTER all routes
- Sentry error handler LAST
Request/Response Handling
Typed Requests
interface CreateUserRequest { email: string; name: string; password: string; } async function createUser( req: Request<{}, {}, CreateUserRequest>, res: Response ): Promise<void> { const { email, name, password } = req.body; // Typed }
Response Patterns
// Success (200) res.json({ success: true, data: user }); // Created (201) res.status(201).json({ success: true, data: user }); // Error (400/500) res.status(400).json({ success: false, error: { message: 'Invalid input' } });
HTTP Status Codes
| Code | Use Case |
|---|---|
| 200 | Success (GET, PUT) |
| 201 | Created (POST) |
| 204 | No Content (DELETE) |
| 400 | Bad Request |
| 401 | Unauthorized |
| 403 | Forbidden |
| 404 | Not Found |
| 500 | Server Error |
Common Mistakes
1. Business Logic in Routes
// ❌ Never do this router.post('/submit', async (req, res) => { // 100+ lines of logic const user = await db.user.create(req.body); const workflow = await processWorkflow(user); res.json(workflow); }); // ✅ Do this router.post('/submit', (req, res) => controller.submit(req, res));
2. Wrong Middleware Order
// ❌ Error handler before routes app.use(errorBoundary); app.use('/api', routes); // Won't catch errors // ✅ Error handler after routes app.use('/api', routes); app.use(errorBoundary);
3. No Error Handling
// ❌ Unhandled errors crash server router.get('/user/:id', async (req, res) => { const user = await userService.get(req.params.id); // May throw res.json(user); }); // ✅ Proper error handling async getUser(req: Request, res: Response): Promise<void> { try { const user = await this.userService.get(req.params.id); this.handleSuccess(res, user); } catch (error) { this.handleError(error, res, 'getUser'); } }
Common Imports
// Express core import express, { Request, Response, NextFunction, Router } from 'express'; // Middleware import cookieParser from 'cookie-parser'; import cors from 'cors'; // Sentry import * as Sentry from '@sentry/node'; // Utilities import { AsyncLocalStorage } from 'async_hooks';
Best Practices
- Keep Routes Clean - Routes only route, delegate to controllers
- Use BaseController - Consistent error handling and response formatting
- Proper Middleware Order - Sentry → Parsers → Routes → Error handlers
- Type Everything - Use TypeScript for Request/Response types
- Handle All Errors - Use try-catch in controllers, error boundaries globally
Related Skills:
- nodejs - Core Node.js patterns and async handling
- backend-dev-guidelines - Complete backend architecture guide
- prisma - Database patterns with Prisma ORM