Skip to main content

express-server

Express server patterns with TypeScript, InversifyJS, and Socket.IO. Use when creating or modifying HTTP endpoints, middleware, route handlers, CORS config, graceful shutdown, or writing supertest integration tests.

설치로 이동

소스 정보

저장소
recca0120/code-quest
최근 소스 활동
2026년 4월 19일 22:05
감지된 SKILL.md 언어
영어
스타
11
포크
2

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
express-server
description
Express server patterns with TypeScript, InversifyJS, and Socket.IO. Use when creating or modifying HTTP endpoints, middleware, route handlers, CORS config, graceful shutdown, or writing supertest integration tests.
# Express Server Skill Stack: Express 4.x, TypeScript, ESM (`"type": "module"`), InversifyJS DI, Socket.IO, cors, helmet. The server is a **thin HTTP layer** — business logic lives in injected services, not in route files. --- ## Project Structure ``` src/ server/ app.ts # Express factory — creates and configures app, no listen() server.ts # Entry point — binds port, handles SIGTERM routes/ # One file per resource, returns express.Router middleware/ # Custom middleware (auth, logging, validation) errors/ # AppError class, error handler middleware ``` --- ## Middleware Ordering Register in this exact order — order is critical: ```ts app.use(helmet()); // 1. Security headers — first app.use(cors(corsOptions)); // 2. CORS — before body parsing app.use(express.json({ limit: '1mb' })); // 3. Body parsers app.use(rateLimiter); // 4. Rate limiting app.use('/static', express.static(staticDir, { maxAge: '1d' })); // 5. Static app.use('/health', healthRouter); // 6. Health — no auth needed app.use('/api', apiRouter); // 7. App routes app.use(errorHandler); // 8. Error handler — always last ``` --- ## Async Error Handling (Express 4) Express 4 does not catch async errors automatically. Wrap handlers or use `express-async-errors`. ```ts // Option A: wrapper (no extra dep) const asyncHandler = (fn: RequestHandler): RequestHandler => (req, res, next) => Promise.resolve(fn(req, res, next)).catch(next); // Option B: import once in app.ts (patches express globally) import 'express-async-errors'; ``` Error middleware — must have exactly four parameters: ```ts app.use((err: unknown, req: Request, res: Response, _next: NextFunction) => { const status = err instanceof AppError ? err.status : 500; const message = err instanceof AppError ? err.message : 'Internal Server Error'; res.status(status).json({ error: message }); }); ``` --- ## Security ```ts import helmet from 'helmet'; import rateLimit from 'express-rate-limit'; app.use(helmet()); const corsOptions: CorsOptions = { origin: process.env.ALLOWED_ORIGINS?.split(',') ?? [], credentials: true, }; const limiter = rateLimit({ windowMs: 60_000, max: 100 }); app.use('/api', limiter); ``` Never use `origin: '*'` in production when `credentials: true`. --- ## Health Check ```ts healthRouter.get('/', (_req, res) => { res.json({ status: 'ok', uptime: process.uptime() }); }); ``` Kubernetes pattern: return `503` during shutdown to drain load balancer traffic before process exits. --- ## Graceful Shutdown ```ts const httpServer = app.listen(port); const shutdown = () => { httpServer.close(() => process.exit(0)); setTimeout(() => process.exit(1), 10_000).unref(); // force-kill after 10s }; process.on('SIGTERM', shutdown); process.on('SIGINT', shutdown); ``` For Socket.IO: call `io.close()` inside `shutdown` before `httpServer.close()`. --- ## InversifyJS Integration Resolve services from the container inside route factories — never `new`: ```ts export function createApiRouter(container: Container): Router { const router = Router(); const svc = container.get(MyService); router.get('/', asyncHandler(async (req, res) => { res.json(await svc.list()); })); return router; } ``` --- ## Testing with Supertest ```ts import request from 'supertest'; import { buildApp } from '../app.js'; const app = buildApp(testContainer); it('GET /health returns 200', async () => { await request(app).get('/health').expect(200); }); ``` Bind a real or stub container — avoid mocking the HTTP layer itself. --- ## Common Pitfalls - Forgetting `next(err)` in Express 4 async handlers causes silent hangs. - Registering error middleware with three params — it silently becomes a regular middleware. - Calling `app.listen()` inside the app factory breaks supertest (port conflicts). - Setting `origin: '*'` with `credentials: true` is rejected by browsers. - Static middleware before `helmet()` can serve files without security headers. - Not calling `io.close()` on shutdown leaves Socket.IO connections hanging. --- Sources: - [Express Error Handling](https://expressjs.com/en/guide/error-handling.html) - [Express Security Best Practices](https://expressjs.com/en/advanced/best-practice-security.html) - [Health Checks and Graceful Shutdown](https://expressjs.com/en/advanced/healthcheck-graceful-shutdown.html) - [Helmet.js](https://helmetjs.github.io/) - [Rate Limiting in Express.js — Better Stack](https://betterstack.com/community/guides/scaling-nodejs/rate-limiting-express/) - [Express Security Best Practices 2025 — Corgea](https://hub.corgea.com/articles/express-security-best-practices-2025)
GitHub에서 보기