Files
Portfolio/blog-posts/48-api-design-patterns.md
damjan_savicandClaude Opus 4.5 e1bbe5455d Initial commit: Portfolio Website
Vollständige Next.js 15 Portfolio-Website mit:
- Blog-System mit 100+ Artikeln
- Supabase-Integration
- Responsive Design mit Tailwind CSS
- TypeScript-Konfiguration
- Testing-Setup mit Vitest und Playwright

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-01 15:07:20 +01:00

476 lines
12 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# API Design Patterns für 2026
**Meta-Description:** Moderne API Design Patterns. REST Best Practices, GraphQL vs tRPC, API Versioning und Error Handling Standards.
**Keywords:** API Design, REST API, GraphQL, tRPC, API Versioning, Error Handling, OpenAPI, API Security
---
## Einführung
Gutes API Design entscheidet über Developer Experience und Wartbarkeit. 2026 stehen mehrere Paradigmen zur Verfügung: **REST, GraphQL, tRPC** jedes mit eigenen Stärken für verschiedene Use Cases.
---
## API Paradigmen im Vergleich
```
┌─────────────────────────────────────────────────────────────┐
│ API PARADIGMEN 2026 │
├─────────────────────────────────────────────────────────────┤
│ │
│ REST │
│ ├── Resource-orientiert │
│ ├── HTTP Verben (GET, POST, PUT, DELETE) │
│ ├── Stateless │
│ ├── Caching-freundlich │
│ └── Best for: Public APIs, Microservices │
│ │
│ GraphQL │
│ ├── Schema-first │
│ ├── Single Endpoint │
│ ├── Client-driven Queries │
│ ├── Subscriptions (Real-time) │
│ └── Best for: Complex Data, Mobile Apps │
│ │
│ tRPC │
│ ├── End-to-End Type Safety │
│ ├── No Code Generation │
│ ├── Zod Validation │
│ ├── React Query Integration │
│ └── Best for: TypeScript Monorepos │
│ │
└─────────────────────────────────────────────────────────────┘
```
---
## REST Best Practices
```typescript
// 1. Resource Naming (Plural Nouns)
// ✅ Good
GET /users
GET /users/:id
POST /users
PUT /users/:id
DELETE /users/:id
// ❌ Bad
GET /getUser
POST /createUser
GET /user-list
// 2. Nested Resources
GET /users/:userId/posts
GET /users/:userId/posts/:postId
POST /users/:userId/posts
// 3. Query Parameters für Filtering/Sorting
GET /posts?status=published&author=123&sort=-createdAt&limit=10&offset=20
// 4. HTTP Status Codes
// 200 OK - Success
// 201 Created - Resource created
// 204 No Content - Success, no body (DELETE)
// 400 Bad Request - Validation error
// 401 Unauthorized - Authentication required
// 403 Forbidden - Permission denied
// 404 Not Found - Resource not found
// 409 Conflict - Duplicate/Conflict
// 422 Unprocessable Entity - Semantic error
// 429 Too Many Requests - Rate limited
// 500 Internal Server Error - Server error
```
```typescript
// Next.js App Router REST API
// app/api/users/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { z } from 'zod';
const createUserSchema = z.object({
email: z.string().email(),
name: z.string().min(2)
});
// GET /api/users
export async function GET(request: NextRequest) {
const searchParams = request.nextUrl.searchParams;
const limit = parseInt(searchParams.get('limit') || '10');
const offset = parseInt(searchParams.get('offset') || '0');
const users = await db.user.findMany({
take: limit,
skip: offset
});
const total = await db.user.count();
return NextResponse.json({
data: users,
pagination: {
total,
limit,
offset,
hasMore: offset + limit < total
}
});
}
// POST /api/users
export async function POST(request: NextRequest) {
try {
const body = await request.json();
const data = createUserSchema.parse(body);
const user = await db.user.create({ data });
return NextResponse.json(
{ data: user },
{ status: 201 }
);
} catch (error) {
if (error instanceof z.ZodError) {
return NextResponse.json(
{
error: 'Validation Error',
details: error.errors
},
{ status: 400 }
);
}
throw error;
}
}
```
---
## Error Response Standard
```typescript
// RFC 7807 Problem Details
interface ProblemDetails {
type: string; // URI Reference für Error Type
title: string; // Kurze Beschreibung
status: number; // HTTP Status Code
detail?: string; // Ausführliche Beschreibung
instance?: string; // URI der fehlerhaften Ressource
[key: string]: unknown; // Erweiterungen
}
// Beispiel Implementation
function createErrorResponse(
status: number,
title: string,
detail?: string,
extras?: Record<string, unknown>
): NextResponse {
const body: ProblemDetails = {
type: `https://api.example.com/errors/${status}`,
title,
status,
detail,
instance: `/api/request-id/${crypto.randomUUID()}`,
timestamp: new Date().toISOString(),
...extras
};
return NextResponse.json(body, {
status,
headers: {
'Content-Type': 'application/problem+json'
}
});
}
// Verwendung
if (!user) {
return createErrorResponse(
404,
'User Not Found',
`User with ID ${id} does not exist`
);
}
// Validation Error mit Details
return createErrorResponse(
400,
'Validation Error',
'The request body contains invalid data',
{
errors: [
{ field: 'email', message: 'Invalid email format' },
{ field: 'name', message: 'Name is required' }
]
}
);
```
---
## API Versioning Strategies
```typescript
// 1. URL Versioning (Empfohlen für Breaking Changes)
// /api/v1/users
// /api/v2/users
// app/api/v1/users/route.ts
export async function GET() {
// V1 Response Format
return NextResponse.json({ users: [...] });
}
// app/api/v2/users/route.ts
export async function GET() {
// V2 Response Format (z.B. neue Felder)
return NextResponse.json({
data: [...],
meta: { version: 'v2' }
});
}
// 2. Header Versioning
// Accept: application/vnd.api+json;version=2
export async function GET(request: NextRequest) {
const accept = request.headers.get('Accept') || '';
const version = accept.match(/version=(\d+)/)?.[1] || '1';
if (version === '2') {
return handleV2(request);
}
return handleV1(request);
}
// 3. Query Parameter (für Clients ohne Header-Kontrolle)
// /api/users?version=2
export async function GET(request: NextRequest) {
const version = request.nextUrl.searchParams.get('version') || '1';
// ...
}
```
---
## Rate Limiting
```typescript
import { Ratelimit } from '@upstash/ratelimit';
import { Redis } from '@upstash/redis';
const ratelimit = new Ratelimit({
redis: Redis.fromEnv(),
limiter: Ratelimit.slidingWindow(10, '10s'), // 10 Requests pro 10s
analytics: true
});
// Middleware
export async function middleware(request: NextRequest) {
const ip = request.ip ?? '127.0.0.1';
const { success, limit, reset, remaining } = await ratelimit.limit(ip);
if (!success) {
return NextResponse.json(
{
error: 'Too Many Requests',
retryAfter: Math.ceil((reset - Date.now()) / 1000)
},
{
status: 429,
headers: {
'X-RateLimit-Limit': limit.toString(),
'X-RateLimit-Remaining': remaining.toString(),
'X-RateLimit-Reset': reset.toString(),
'Retry-After': Math.ceil((reset - Date.now()) / 1000).toString()
}
}
);
}
const response = NextResponse.next();
response.headers.set('X-RateLimit-Limit', limit.toString());
response.headers.set('X-RateLimit-Remaining', remaining.toString());
return response;
}
```
---
## Pagination Patterns
```typescript
// 1. Offset Pagination (Einfach, aber langsam bei großen Datasets)
interface OffsetPagination {
data: User[];
pagination: {
total: number;
limit: number;
offset: number;
hasMore: boolean;
};
}
// 2. Cursor Pagination (Empfohlen für große Datasets)
interface CursorPagination<T> {
data: T[];
pagination: {
cursor: string | null;
hasMore: boolean;
};
}
async function getUsersWithCursor(cursor?: string, limit = 20) {
const users = await db.user.findMany({
take: limit + 1, // +1 um hasMore zu prüfen
cursor: cursor ? { id: cursor } : undefined,
orderBy: { createdAt: 'desc' }
});
const hasMore = users.length > limit;
const data = hasMore ? users.slice(0, -1) : users;
const nextCursor = hasMore ? data[data.length - 1].id : null;
return {
data,
pagination: {
cursor: nextCursor,
hasMore
}
};
}
// 3. Keyset Pagination (Für sortierte Daten)
// /api/posts?after=2024-01-15T10:00:00Z&limit=20
```
---
## OpenAPI Specification
```typescript
// Mit Zod + zod-to-openapi
import { OpenAPIHono, createRoute, z } from '@hono/zod-openapi';
const app = new OpenAPIHono();
const UserSchema = z.object({
id: z.string().uuid(),
email: z.string().email(),
name: z.string()
}).openapi('User');
const route = createRoute({
method: 'get',
path: '/users/{id}',
request: {
params: z.object({
id: z.string().uuid()
})
},
responses: {
200: {
content: {
'application/json': {
schema: UserSchema
}
},
description: 'User found'
},
404: {
content: {
'application/json': {
schema: z.object({
error: z.string()
})
}
},
description: 'User not found'
}
}
});
app.openapi(route, (c) => {
const { id } = c.req.valid('param');
// ...
});
// OpenAPI Spec generieren
app.doc('/doc', {
openapi: '3.1.0',
info: {
title: 'My API',
version: '1.0.0'
}
});
```
---
## API Security Checklist
```typescript
// 1. Authentication
// - JWT mit kurzer Expiration
// - Refresh Token Rotation
// - Secure Cookie Storage
// 2. Authorization
// - RBAC oder ABAC
// - Resource-level Permissions
// - Rate Limiting per User
// 3. Input Validation
// - Zod für alle Inputs
// - Sanitize User Input
// - File Upload Restrictions
// 4. Headers
const securityHeaders = {
'Content-Security-Policy': "default-src 'self'",
'X-Content-Type-Options': 'nosniff',
'X-Frame-Options': 'DENY',
'Strict-Transport-Security': 'max-age=31536000; includeSubDomains',
'X-XSS-Protection': '1; mode=block'
};
// 5. CORS
const corsConfig = {
origin: ['https://app.example.com'],
methods: ['GET', 'POST', 'PUT', 'DELETE'],
credentials: true,
maxAge: 86400
};
```
---
## Fazit
Gutes API Design 2026 bedeutet:
1. **Konsistenz**: Einheitliche Naming, Responses, Errors
2. **Type Safety**: Zod/OpenAPI für Contracts
3. **Performance**: Pagination, Caching, Rate Limiting
4. **Security**: Validation, Auth, Headers
Die Wahl zwischen REST/GraphQL/tRPC hängt vom Use Case ab.
---
## Bildprompts
1. "API endpoints connecting client and server, clean architecture diagram"
2. "REST vs GraphQL vs tRPC comparison, three paths to data"
3. "Security shield protecting API gateway, authentication visualization"
---
## Quellen
- [REST API Design Best Practices](https://restfulapi.net/)
- [RFC 7807 - Problem Details](https://datatracker.ietf.org/doc/html/rfc7807)
- [OpenAPI Specification](https://spec.openapis.org/oas/latest.html)
- [tRPC Documentation](https://trpc.io/)