Express.js REST API mit Claude Code: TypeScript Backend 2026
Express.js bleibt der meistgenutzte Node.js-Framework — flexibel, erprobt, mit riesigem Ökosystem. Claude Code kennt alle Express-Patterns: TypeScript-Setup, Middleware-Stacks, Validierung, JWT-Auth, Error-Handling und OpenAPI-Dokumentation für produktionsreife Backends.
TypeScript-Setup und Projektstruktur
SetupExpress mit TypeScript einrichten
# Prompt: "Express.js REST API mit TypeScript, Prisma, Zod — Production-Setup"
npm install express helmet cors morgan compression
npm install -D typescript @types/express @types/node ts-node-dev
// src/app.ts — Express App Factory
import express, { Express } from 'express'
import helmet from 'helmet'
import cors from 'cors'
import morgan from 'morgan'
import compression from 'compression'
export function createApp(): Express {
const app = express()
// Security-Middleware
app.use(helmet())
app.use(cors({ origin: process.env.FRONTEND_URL, credentials: true }))
app.use(compression())
app.use(morgan('combined'))
app.use(express.json({ limit: '10mb' }))
// Routes
app.use('/api/v1/users', usersRouter)
app.use('/api/v1/projects', projectsRouter)
app.use('/health', (_, res) => res.json({ status: 'ok', ts: Date.now() }))
// Error Handler (MUSS letzter Middleware sein)
app.use(errorHandler)
return app
}
// src/index.ts — Server Start
const app = createApp()
const PORT = env.PORT ?? 3001
app.listen(PORT, () => console.log(`Server läuft auf Port ${PORT}`))
Projektstruktur-Prompt: "Erstelle Express TypeScript Projektstruktur: src/routes, src/middleware, src/services, src/models, src/utils — mit Barrel-Exporten und klarer Schichtentrennung."
Router und Controller-Pattern
RouterRessourcen-basiertes Routing mit Zod
# Prompt: "Express Router für User-CRUD mit Zod-Validierung und Async-Wrapper"
// src/middleware/validate.ts — Zod-Validierungs-Middleware
export function validate(schema: AnyZodObject) {
return (req: Request, res: Response, next: NextFunction) => {
const result = schema.safeParse({
body: req.body, query: req.query, params: req.params
})
if (!result.success) {
return res.status(400).json({ errors: result.error.flatten() })
}
req.validated = result.data
next()
}
}
// src/routes/users.ts
const router = express.Router()
const createUserSchema = z.object({
body: z.object({
name: z.string().min(2),
email: z.string().email(),
password: z.string().min(8)
})
})
router.get('/', authenticate, asyncHandler(async (req, res) => {
const { page = '1', limit = '20' } = req.query
const users = await userService.findAll({ page: +page, limit: +limit })
res.json(users)
}))
router.post('/', validate(createUserSchema), asyncHandler(async (req, res) => {
const user = await userService.create(req.validated.body)
res.status(201).json(user)
}))
router.get('/:id', authenticate, asyncHandler(async (req, res) => {
const user = await userService.findById(req.params.id)
if (!user) throw new NotFoundError('User nicht gefunden')
res.json(user)
}))
JWT-Authentication Middleware
AuthJWT-Middleware mit Refresh-Token
# Prompt: "JWT-Auth-Middleware mit Access + Refresh Token, sichere Cookie-Strategie"
// src/middleware/authenticate.ts
import jwt from 'jsonwebtoken'
export const authenticate: RequestHandler = (req, res, next) => {
const token = req.headers.authorization?.replace('Bearer ', '')
if (!token) return res.status(401).json({ error: 'Nicht authentifiziert' })
try {
const payload = jwt.verify(token, env.JWT_SECRET) as JwtPayload
req.user = { id: payload.sub, role: payload.role }
next()
} catch (err) {
if (err instanceof jwt.TokenExpiredError) {
return res.status(401).json({ error: 'Token abgelaufen', code: 'TOKEN_EXPIRED' })
}
res.status(401).json({ error: 'Ungültiger Token' })
}
}
// Token generieren
export function generateTokens(userId: string, role: string) {
const accessToken = jwt.sign(
{ sub: userId, role },
env.JWT_SECRET,
{ expiresIn: '15m' } // Kurz — sicher
)
const refreshToken = jwt.sign(
{ sub: userId },
env.JWT_REFRESH_SECRET,
{ expiresIn: '7d' }
)
return { accessToken, refreshToken }
}
// RBAC: Role-Based Access Control
export const authorize = (roles: string[]) => (req: Request, res: Response, next: NextFunction) => {
if (!roles.includes(req.user.role)) return res.status(403).json({ error: 'Keine Berechtigung' })
next()
}
Globales Error-Handling
ErrorsTypisierte Fehler und zentrales Error-Handling
# Prompt: "Erstelle typisierte Error-Klassen und zentralen Express Error-Handler"
// src/errors/AppError.ts
export class AppError extends Error {
constructor(
public message: string,
public statusCode: number = 500,
public code?: string
) {
super(message)
Object.setPrototypeOf(this, AppError.prototype)
}
}
export class NotFoundError extends AppError { constructor(msg: string) { super(msg, 404, 'NOT_FOUND') } }
export class UnauthorizedError extends AppError { constructor(msg: string) { super(msg, 401, 'UNAUTHORIZED') } }
export class ConflictError extends AppError { constructor(msg: string) { super(msg, 409, 'CONFLICT') } }
// src/middleware/errorHandler.ts
export const errorHandler: ErrorRequestHandler = (err, req, res, next) => {
console.error(`[${req.method}] ${req.path}`, err)
if (err instanceof AppError) {
return res.status(err.statusCode).json({
error: err.message,
code: err.code
})
}
// Prisma-Fehler
if (err.code === 'P2002') { // Unique Constraint
return res.status(409).json({ error: 'Datensatz existiert bereits' })
}
// Unbekannte Fehler: 500
res.status(500).json({ error: 'Interner Serverfehler' })
}
// asyncHandler — Fehler weiterleiten ohne try/catch in jedem Route
export const asyncHandler = (fn: RequestHandler): RequestHandler =>
(req, res, next) => Promise.resolve(fn(req, res, next)).catch(next)
Häufiger Fehler: Fehler in async Route-Handlers nicht zu next() weitergeben. Ohne
asyncHandler-Wrapper bleibt Express bei unbehandelten Promise-Rejections hängen. Claude Code fügt den Wrapper automatisch ein.OpenAPI-Dokumentation mit Swagger
# Prompt: "Swagger/OpenAPI-Docs für Express REST API mit swagger-jsdoc"
npm install swagger-jsdoc swagger-ui-express
// src/docs/swagger.ts
const options = {
definition: {
openapi: '3.0.0',
info: { title: 'API', version: '1.0.0' },
components: {
securitySchemes: {
bearerAuth: { type: 'http', scheme: 'bearer', bearerFormat: 'JWT' }
}
}
},
apis: ['./src/routes/*.ts']
}
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerJsdoc(options)))
// Route-Annotation
/**
* @openapi
* /api/v1/users:
* post:
* summary: User erstellen
* requestBody:
* required: true
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/CreateUser'
* responses:
* 201:
* description: User erstellt
*/
Backend-Modul im Kurs
Im Claude Code Mastery Kurs: vollständiges Express-Modul mit TypeScript-Setup, Middleware-Stack, JWT-Auth, Error-Handling, Prisma-Integration und OpenAPI-Dokumentation — für produktionsreife REST-APIs.
14 Tage kostenlos testen →