internal/skills/content/express/SKILL.md
Express.js framework guardrails, patterns, and best practices for AI-assisted development. Use when working with Express.js projects, or when the user mentions Express. Provides middleware patterns, routing, error handling, and REST API guidelines.
npx skillsauth add ar4mirez/samuel expressInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Applies to: Express.js 4.x/5.x, TypeScript/JavaScript, REST APIs, Web Servers, Microservices
helmet, cors, express-rate-limitdotenv for environment configuration (never hardcode secrets)zod or joi for request validation at every API boundarymorgan or pino for structured loggingexpress.json({ limit: '10mb' }) to prevent oversized payloadsnext(error)req/res referencesAppError class with statusCode and optional errors arraynext(error) in async handlers -- never swallow errors404 with a dedicated not-found middleware after all routeshelmet() for security headers* in production)httpOnly, secure, sameSitecompression middleware for response compressionreadTimeout, writeTimeout)my-api/
├── src/
│ ├── app.ts # Express app setup (middleware, routes, error handling)
│ ├── server.ts # Entry point (listen, graceful shutdown)
│ ├── config/ # Environment and app configuration
│ │ └── index.ts
│ ├── controllers/ # Route handlers (thin, delegate to services)
│ │ └── user.controller.ts
│ ├── middlewares/ # Custom middleware
│ │ ├── auth.middleware.ts
│ │ ├── error.middleware.ts
│ │ ├── validate.middleware.ts
│ │ └── rateLimit.middleware.ts
│ ├── routes/ # Route definitions (grouped by resource)
│ │ ├── index.ts
│ │ └── user.routes.ts
│ ├── services/ # Business logic (no req/res)
│ │ └── user.service.ts
│ ├── repositories/ # Data access layer
│ │ └── user.repository.ts
│ ├── models/ # Data models / Prisma schema
│ │ └── user.model.ts
│ ├── schemas/ # Zod validation schemas
│ │ └── user.schema.ts
│ ├── types/ # TypeScript type definitions
│ │ └── index.ts
│ └── utils/ # Shared utilities
│ ├── errors.ts # AppError class
│ └── logger.ts # Logger setup
├── tests/
│ └── user.test.ts
├── .env.example
├── package.json
└── tsconfig.json
| Layer | Knows About | Never References | |-------|-------------|-----------------| | Routes | Controllers, middleware | Services, repositories | | Controllers | Services, types | Repositories, database | | Services | Repositories, types | req/res, Express | | Repositories | Database client, types | Services, controllers |
import express, { Application } from 'express';
import cors from 'cors';
import helmet from 'helmet';
import morgan from 'morgan';
import { errorHandler } from './middlewares/error.middleware';
import { notFoundHandler } from './middlewares/notFound.middleware';
import routes from './routes';
const app: Application = express();
// Security
app.use(helmet());
app.use(cors({ origin: process.env.CORS_ORIGIN || '*', credentials: true }));
// Parsing
app.use(express.json({ limit: '10mb' }));
app.use(express.urlencoded({ extended: true }));
// Logging (skip in test)
if (process.env.NODE_ENV !== 'test') {
app.use(morgan('combined'));
}
// Health check
app.get('/health', (_req, res) => {
res.json({ status: 'ok', timestamp: new Date().toISOString() });
});
// API routes
app.use('/api/v1', routes);
// Error handling (order matters: 404 first, then error handler)
app.use(notFoundHandler);
app.use(errorHandler);
export default app;
import 'dotenv/config';
import app from './app';
import { logger } from './utils/logger';
const PORT = process.env.PORT || 3000;
const server = app.listen(PORT, () => {
logger.info(`Server running on port ${PORT}`);
});
// Graceful shutdown
const shutdown = () => {
logger.info('Shutting down gracefully...');
server.close(() => process.exit(0));
setTimeout(() => process.exit(1), 10000); // Force after 10s
};
process.on('SIGTERM', shutdown);
process.on('SIGINT', shutdown);
process.on('unhandledRejection', (reason: Error) => {
logger.error('Unhandled Rejection:', reason);
throw reason;
});
process.on('uncaughtException', (error: Error) => {
logger.error('Uncaught Exception:', error);
process.exit(1);
});
// src/routes/index.ts
import { Router } from 'express';
import userRoutes from './user.routes';
import authRoutes from './auth.routes';
const router = Router();
router.use('/auth', authRoutes);
router.use('/users', userRoutes);
export default router;
// src/routes/user.routes.ts
import { Router } from 'express';
import { UserController } from '../controllers/user.controller';
import { authMiddleware } from '../middlewares/auth.middleware';
import { validate } from '../middlewares/validate.middleware';
import { createUserSchema, updateUserSchema } from '../schemas/user.schema';
const router = Router();
const controller = new UserController();
router.get('/', controller.getAll);
router.get('/:id', controller.getById);
router.post('/', validate(createUserSchema), controller.create);
router.put('/:id', authMiddleware, validate(updateUserSchema), controller.update);
router.delete('/:id', authMiddleware, controller.delete);
export default router;
/api/v1//users, /products)helmet() -- security headerscors() -- cross-origin requestsexpress.json() -- body parsingmorgan() -- request loggingnotFoundHandler -- catch unmatched routeserrorHandler -- centralized error handling// src/utils/errors.ts
export class AppError extends Error {
constructor(
message: string,
public statusCode: number = 500,
public errors?: any[]
) {
super(message);
this.name = 'AppError';
Error.captureStackTrace(this, this.constructor);
}
}
// src/middlewares/error.middleware.ts
import { Request, Response, NextFunction } from 'express';
import { AppError } from '../utils/errors';
import { logger } from '../utils/logger';
export const errorHandler = (
error: Error, req: Request, res: Response, _next: NextFunction
) => {
logger.error('Error:', { message: error.message, path: req.path, method: req.method });
if (error instanceof AppError) {
return res.status(error.statusCode).json({
status: 'error',
message: error.message,
...(error.errors && { errors: error.errors }),
});
}
res.status(500).json({
status: 'error',
message: process.env.NODE_ENV === 'production'
? 'Internal server error'
: error.message,
});
};
// src/middlewares/validate.middleware.ts
import { Request, Response, NextFunction } from 'express';
import { ZodSchema, ZodError } from 'zod';
import { AppError } from '../utils/errors';
export const validate = (schema: ZodSchema) => {
return (req: Request, _res: Response, next: NextFunction) => {
try {
schema.parse({ body: req.body, query: req.query, params: req.params });
next();
} catch (error) {
if (error instanceof ZodError) {
const errors = error.errors.map((e) => ({
field: e.path.join('.'),
message: e.message,
}));
next(new AppError('Validation failed', 400, errors));
} else {
next(error);
}
}
};
};
app (not server) for supertest -- avoids port conflictsimport request from 'supertest';
import app from '../src/app';
describe('GET /api/v1/users', () => {
it('should return 200 with user list', async () => {
const response = await request(app)
.get('/api/v1/users')
.expect('Content-Type', /json/)
.expect(200);
expect(response.body.data).toBeDefined();
expect(response.body.meta).toBeDefined();
});
it('should return 400 for invalid query params', async () => {
await request(app)
.get('/api/v1/users?page=-1')
.expect(400);
});
});
describe blocksbeforeEach/afterEach for database cleanup# Development
npm run dev # Start with hot-reload (ts-node-dev)
# Build
npm run build # Compile TypeScript
npm start # Run compiled JS
# Testing
npm test # Run all tests
npm run test:watch # Watch mode
npm run test:cov # With coverage
# Quality
npm run lint # ESLint
npm run lint:fix # Auto-fix lint issues
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"resolveJsonModule": true,
"declaration": true,
"sourceMap": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
{
"scripts": {
"dev": "ts-node-dev --respawn --transpile-only src/server.ts",
"build": "tsc",
"start": "node dist/server.js",
"test": "jest",
"lint": "eslint src/**/*.ts"
}
}
| Package | Purpose |
|---------|---------|
| express | Web framework |
| cors | Cross-origin resource sharing |
| helmet | Security headers |
| morgan | HTTP request logging |
| dotenv | Environment variables |
| zod | Input validation |
| Package | Purpose |
|---------|---------|
| jsonwebtoken | JWT authentication |
| bcryptjs | Password hashing |
| express-rate-limit | Rate limiting |
| Package | Purpose |
|---------|---------|
| @prisma/client | ORM / database client |
| Package | Purpose |
|---------|---------|
| typescript | Type system |
| ts-node-dev | Dev server with hot reload |
| jest | Test runner |
| supertest | HTTP testing |
| @types/express | Express type definitions |
For detailed code examples and advanced patterns, see:
development
Zig language guardrails, patterns, and best practices for AI-assisted development. Use when working with Zig files (.zig), build.zig, or when the user mentions Zig. Provides comptime patterns, allocator conventions, C interop guidelines, and testing standards specific to this project's coding standards.
tools
WordPress framework guardrails, patterns, and best practices for AI-assisted development. Use when working with WordPress projects, or when the user mentions WordPress. Provides theme development, plugin architecture, REST API, blocks, and security guidelines.
tools
Toolkit for interacting with and testing local web applications using Playwright. Supports verifying frontend functionality, debugging UI behavior, capturing browser screenshots, and viewing browser logs. Use when testing web apps, automating browser interactions, or debugging frontend issues.
tools
Suite of tools for creating elaborate, multi-component web applications using modern frontend technologies (React, Tailwind CSS, shadcn/ui). Use for complex projects requiring state management, routing, or shadcn/ui components - not for simple single-file HTML/JSX pages.