TypeScript Express 서버

TypeScript로 작성된 Express 서버 보일러플레이트. 타입 정의, 에러 핸들러, REST 라우터 포함.

Gist
import express, { Request, Response, NextFunction } from 'express';

const app = express();
const PORT = Number(process.env.PORT ?? 3000);

app.use(express.json());
app.use(express.urlencoded({ extended: true }));

interface User {
  id: number;
  name: string;
  email: string;
  createdAt: string;
}

const users: Map<number, User> = new Map();
let nextId = 1;

app.get('/health', (_req: Request, res: Response) => {
  res.json({ status: 'ok', uptime: process.uptime() });
});

app.get('/users', (_req: Request, res: Response) => {
  res.json(Array.from(users.values()));
});

app.get('/users/:id', (req: Request, res: Response) => {
  const user = users.get(Number(req.params.id));
  if (!user) return res.status(404).json({ error: 'User not found' });
  res.json(user);
});

app.post('/users', (req: Request, res: Response) => {
  const { name, email } = req.body as Pick<User, 'name' | 'email'>;
  if (!name || !email) {
    return res.status(400).json({ error: 'name and email are required' });
  }
  const user: User = { id: nextId++, name, email, createdAt: new Date().toISOString() };
  users.set(user.id, user);
  res.status(201).json(user);
});

app.put('/users/:id', (req: Request, res: Response) => {
  const id = Number(req.params.id);
  if (!users.has(id)) return res.status(404).json({ error: 'User not found' });
  const existing = users.get(id)!;
  const updated = { ...existing, ...req.body, id };
  users.set(id, updated);
  res.json(updated);
});

app.delete('/users/:id', (req: Request, res: Response) => {
  const id = Number(req.params.id);
  if (!users.delete(id)) return res.status(404).json({ error: 'User not found' });
  res.status(204).send();
});

app.use((err: Error, _req: Request, res: Response, _next: NextFunction) => {
  console.error(err.stack);
  res.status(500).json({ error: 'Internal server error' });
});

app.listen(PORT, () => console.log(`Server running on http://localhost:${PORT}`));