Menu
Back to Blog
2 min read
Backend

API Design & Documentation

REST API Design Best Practices. OpenAPI Specification, Dokumentation mit Swagger und Developer Experience optimieren.

API DesignREST APIOpenAPISwaggerAPI DocumentationDeveloper Experience
API Design & Documentation

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

# 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

// 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
  }
});
// 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

// lib/api/response.ts
import { NextResponse } from 'next/server';

interface ApiResponse<T> {
  data?: T;
  error?: {
    code: string;
    message: string;
    details?: unknown;
  };
  pagination?: {
    page: number;
    limit: number;
    total: number;
    totalPages: number;
  };
}

export function success<T>(
  data: T,
  status: number = 200
): NextResponse<ApiResponse<T>> {
  return NextResponse.json({ data }, { status });
}

export function paginated<T>(
  data: T[],
  pagination: { page: number; limit: number; total: number }
): NextResponse<ApiResponse<T[]>> {
  return NextResponse.json({
    data,
    pagination: {
      ...pagination,
      totalPages: Math.ceil(pagination.total / pagination.limit)
    }
  });
}

export function created<T>(data: T): NextResponse<ApiResponse<T>> {
  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<ApiResponse<never>> {
  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

// app/docs/api/page.tsx
import SwaggerUI from 'swagger-ui-react';
import 'swagger-ui-react/swagger-ui.css';

export default function ApiDocsPage() {
  return (
    <div className="min-h-screen">
      <SwaggerUI
        url="/openapi/api.yaml"
        docExpansion="list"
        defaultModelsExpandDepth={-1}
        displayRequestDuration={true}
        filter={true}
        showExtensions={true}
        tryItOutEnabled={true}
      />
    </div>
  );
}

// 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 (
    <SyntaxHighlighter
      language={language}
      style={vscDarkPlus}
      customStyle={{ borderRadius: '8px' }}
    >
      {code}
    </SyntaxHighlighter>
  );
}

API Design Checklist

AspectBest 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