# API Design & Documentation **Meta-Description:** REST API Design Best Practices. OpenAPI Specification, Dokumentation mit Swagger und Developer Experience optimieren. **Keywords:** API Design, REST API, OpenAPI, Swagger, API Documentation, Developer Experience, API Best Practices --- ## Einführung **Gutes API Design** ist entscheidend für Developer Experience. Mit **OpenAPI/Swagger** können APIs dokumentiert, getestet und generiert werden. Dieser Guide zeigt Best Practices für konsistente, intuitive APIs. --- ## API Design Overview ``` ┌─────────────────────────────────────────────────────────────┐ │ REST API DESIGN PRINCIPLES │ ├─────────────────────────────────────────────────────────────┤ │ │ │ URL Structure: │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ Resources (Nouns, plural): │ │ │ │ ├── GET /users List users │ │ │ │ ├── POST /users Create user │ │ │ │ ├── GET /users/{id} Get user │ │ │ │ ├── PUT /users/{id} Update user │ │ │ │ ├── PATCH /users/{id} Partial update │ │ │ │ └── DELETE /users/{id} Delete user │ │ │ │ │ │ │ │ Nested Resources: │ │ │ │ ├── GET /users/{id}/projects │ │ │ │ └── POST /users/{id}/projects │ │ │ │ │ │ │ │ Query Parameters: │ │ │ │ ├── ?page=1&limit=20 Pagination │ │ │ │ ├── ?sort=createdAt:desc Sorting │ │ │ │ ├── ?filter[status]=active Filtering │ │ │ │ └── ?fields=id,name,email Field selection │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ │ HTTP Status Codes: │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ 2xx Success: │ │ │ │ ├── 200 OK Successful request │ │ │ │ ├── 201 Created Resource created │ │ │ │ └── 204 No Content Successful, no body │ │ │ │ │ │ │ │ 4xx Client Errors: │ │ │ │ ├── 400 Bad Request Invalid input │ │ │ │ ├── 401 Unauthorized Not authenticated │ │ │ │ ├── 403 Forbidden Not authorized │ │ │ │ ├── 404 Not Found Resource not found │ │ │ │ └── 422 Unprocessable Validation failed │ │ │ │ │ │ │ │ 5xx Server Errors: │ │ │ │ ├── 500 Internal Error Server error │ │ │ │ └── 503 Service Unavailable Temporarily down │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────┘ ``` --- ## OpenAPI Specification ```yaml # openapi/api.yaml openapi: 3.1.0 info: title: My SaaS API description: API for managing projects and tasks version: 1.0.0 contact: name: API Support email: api@example.com url: https://example.com/support license: name: MIT url: https://opensource.org/licenses/MIT servers: - url: https://api.example.com/v1 description: Production - url: https://api.staging.example.com/v1 description: Staging - url: http://localhost:3000/api/v1 description: Local development tags: - name: Projects description: Project management endpoints - name: Tasks description: Task management endpoints - name: Users description: User management endpoints paths: /projects: get: tags: [Projects] summary: List all projects description: Returns a paginated list of projects for the authenticated user operationId: listProjects security: - bearerAuth: [] parameters: - $ref: '#/components/parameters/PageParam' - $ref: '#/components/parameters/LimitParam' - name: status in: query description: Filter by status schema: type: string enum: [active, archived, draft] - name: sort in: query description: Sort field and direction schema: type: string example: createdAt:desc responses: '200': description: Successful response content: application/json: schema: $ref: '#/components/schemas/ProjectListResponse' '401': $ref: '#/components/responses/Unauthorized' '500': $ref: '#/components/responses/InternalError' post: tags: [Projects] summary: Create a project description: Creates a new project operationId: createProject security: - bearerAuth: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateProjectInput' responses: '201': description: Project created content: application/json: schema: $ref: '#/components/schemas/Project' '400': $ref: '#/components/responses/BadRequest' '422': $ref: '#/components/responses/ValidationError' /projects/{projectId}: get: tags: [Projects] summary: Get a project operationId: getProject security: - bearerAuth: [] parameters: - $ref: '#/components/parameters/ProjectIdParam' responses: '200': description: Successful response content: application/json: schema: $ref: '#/components/schemas/Project' '404': $ref: '#/components/responses/NotFound' patch: tags: [Projects] summary: Update a project operationId: updateProject security: - bearerAuth: [] parameters: - $ref: '#/components/parameters/ProjectIdParam' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/UpdateProjectInput' responses: '200': description: Project updated content: application/json: schema: $ref: '#/components/schemas/Project' delete: tags: [Projects] summary: Delete a project operationId: deleteProject security: - bearerAuth: [] parameters: - $ref: '#/components/parameters/ProjectIdParam' responses: '204': description: Project deleted components: securitySchemes: bearerAuth: type: http scheme: bearer bearerFormat: JWT parameters: ProjectIdParam: name: projectId in: path required: true description: Project ID schema: type: string format: uuid PageParam: name: page in: query description: Page number schema: type: integer minimum: 1 default: 1 LimitParam: name: limit in: query description: Items per page schema: type: integer minimum: 1 maximum: 100 default: 20 schemas: Project: type: object required: [id, name, status, createdAt] properties: id: type: string format: uuid example: '123e4567-e89b-12d3-a456-426614174000' name: type: string minLength: 1 maxLength: 100 example: 'My Project' description: type: string maxLength: 1000 status: type: string enum: [active, archived, draft] default: active createdAt: type: string format: date-time updatedAt: type: string format: date-time CreateProjectInput: type: object required: [name] properties: name: type: string minLength: 1 maxLength: 100 description: type: string maxLength: 1000 UpdateProjectInput: type: object properties: name: type: string minLength: 1 maxLength: 100 description: type: string maxLength: 1000 status: type: string enum: [active, archived, draft] ProjectListResponse: type: object properties: data: type: array items: $ref: '#/components/schemas/Project' pagination: $ref: '#/components/schemas/Pagination' Pagination: type: object properties: page: type: integer limit: type: integer total: type: integer totalPages: type: integer Error: type: object properties: code: type: string message: type: string details: type: object responses: BadRequest: description: Bad request content: application/json: schema: $ref: '#/components/schemas/Error' Unauthorized: description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/Error' NotFound: description: Resource not found content: application/json: schema: $ref: '#/components/schemas/Error' ValidationError: description: Validation error content: application/json: schema: allOf: - $ref: '#/components/schemas/Error' - type: object properties: errors: type: array items: type: object properties: field: type: string message: type: string InternalError: description: Internal server error content: application/json: schema: $ref: '#/components/schemas/Error' ``` --- ## Type Generation from OpenAPI ```typescript // scripts/generate-types.ts import { generateApi } from 'swagger-typescript-api'; import path from 'path'; generateApi({ name: 'api-client.ts', output: path.resolve(__dirname, '../lib/api'), input: path.resolve(__dirname, '../openapi/api.yaml'), httpClientType: 'fetch', generateClient: true, generateRouteTypes: true, generateResponses: true, extractRequestParams: true, extractRequestBody: true, unwrapResponseData: true, prettier: { printWidth: 100, singleQuote: true } }); ``` ```typescript // lib/api/client.ts - Generated API Client Usage import { Api } from './api-client'; const api = new Api({ baseUrl: process.env.NEXT_PUBLIC_API_URL, securityWorker: async () => { const token = await getAccessToken(); return token ? { headers: { Authorization: `Bearer ${token}` } } : {}; } }); // Type-safe API calls export async function getProjects(params?: { page?: number; limit?: number; status?: 'active' | 'archived' | 'draft'; }) { const response = await api.projects.listProjects(params); return response.data; } export async function createProject(input: { name: string; description?: string; }) { const response = await api.projects.createProject(input); return response.data; } ``` --- ## API Response Format ```typescript // lib/api/response.ts import { NextResponse } from 'next/server'; interface ApiResponse { data?: T; error?: { code: string; message: string; details?: unknown; }; pagination?: { page: number; limit: number; total: number; totalPages: number; }; } export function success( data: T, status: number = 200 ): NextResponse> { return NextResponse.json({ data }, { status }); } export function paginated( data: T[], pagination: { page: number; limit: number; total: number } ): NextResponse> { return NextResponse.json({ data, pagination: { ...pagination, totalPages: Math.ceil(pagination.total / pagination.limit) } }); } export function created(data: T): NextResponse> { return success(data, 201); } export function noContent(): NextResponse { return new NextResponse(null, { status: 204 }); } export function error( code: string, message: string, status: number = 400, details?: unknown ): NextResponse> { return NextResponse.json( { error: { code, message, details } }, { status } ); } export function badRequest(message: string, details?: unknown) { return error('BAD_REQUEST', message, 400, details); } export function unauthorized(message: string = 'Unauthorized') { return error('UNAUTHORIZED', message, 401); } export function forbidden(message: string = 'Forbidden') { return error('FORBIDDEN', message, 403); } export function notFound(resource: string = 'Resource') { return error('NOT_FOUND', `${resource} not found`, 404); } export function validationError(errors: Array<{ field: string; message: string }>) { return error('VALIDATION_ERROR', 'Validation failed', 422, { errors }); } export function internalError(message: string = 'Internal server error') { return error('INTERNAL_ERROR', message, 500); } ``` --- ## Interactive Documentation ```tsx // app/docs/api/page.tsx import SwaggerUI from 'swagger-ui-react'; import 'swagger-ui-react/swagger-ui.css'; export default function ApiDocsPage() { return (
); } // Alternative: Custom docs with syntax highlighting import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; import { vscDarkPlus } from 'react-syntax-highlighter/dist/esm/styles/prism'; function CodeExample({ code, language }: { code: string; language: string }) { return ( {code} ); } ``` --- ## API Design Checklist | Aspect | Best Practice | |--------|--------------| | **URLs** | Use nouns, plural, lowercase | | **HTTP Methods** | Match CRUD operations | | **Status Codes** | Use appropriate codes | | **Errors** | Consistent format with codes | | **Pagination** | Always paginate lists | | **Versioning** | Use URL versioning (/v1/) | | **Documentation** | OpenAPI specification | | **Authentication** | Bearer tokens, API keys | --- ## Fazit Gutes API Design erfordert: 1. **Konsistenz**: Einheitliche Struktur und Namenskonventionen 2. **OpenAPI**: Maschinenlesbare Spezifikation 3. **Dokumentation**: Interaktiv und aktuell 4. **Type Safety**: Generierte Typen aus Spec Developer Experience beginnt mit gutem API Design. --- ## Bildprompts 1. "API documentation page with Swagger UI, interactive endpoints" 2. "REST API resource diagram, CRUD operations visualization" 3. "OpenAPI specification editor, YAML code with validation" --- ## Quellen - [OpenAPI Specification](https://spec.openapis.org/oas/latest.html) - [REST API Design Guide](https://restfulapi.net/) - [Google API Design Guide](https://cloud.google.com/apis/design) - [Microsoft REST API Guidelines](https://github.com/microsoft/api-guidelines)