ADR-003: API Design Principles

Status: ✅ Accepted — 2026-07-03 by CPTO

CPTO Sign-off: 2026-07-03 — RESTful + OpenAPI ist richtig für Cartly. JWT + tenant_id Isolation aus ADR-002 muss in alle Endpunkte durchdekliniert werden. Rate Limiting nach Plan (Good/Better/Best) ist customer-friendly pricing.

Entscheidung: RESTful API Design mit versionierten Endpunkten und einheitlicher Authentifizierung


Kontext

Cartly benötigt eine API für:

Anforderungen:


Entscheidung

1. API Versioning

Pattern: URL-Path Versioning (/api/v1/)

/api/v1/products
/api/v1/orders
/api/v1/tenants

Regeln:

2. HTTP Methoden & Status Codes

Methode Verwendung Idempotent Response Code
GET Lesen 200, 404, 401
POST Erstellen 201, 400, 401
PUT Vollständiges Update 200, 400, 404, 401
PATCH Partielles Update 200, 400, 404, 401
DELETE Löschen 204, 404, 401
HEAD Existenz-Prüfung 200, 404, 401
OPTIONS CORS Preflight 204, 401

3. Request/Response Format

Request: JSON im Body

POST /api/v1/products
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
Content-Type: application/json

{
  "sku": "TSHIRT-BLK-M",
  "name": "Classic T-Shirt Schwarz",
  "price": 29.99,
  "sizes": ["S", "M", "L", "XL"],
  "colors": [
    {"name": "Schwarz", "hex": "#000000"},
    {"name": "Weiß", "hex": "#FFFFFF"}
  ]
}

Response: JSON mit konsistentem Wrapper

{
  "data": { ... },
  "meta": {
    "requestId": "req_abc123",
    "timestamp": "2026-07-03T12:00:00Z",
    "tenantId": "tenant_xyz"
  }
}

Error Response:

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "SKU ist bereits vergeben",
    "details": [
      {"field": "sku", "message": "Duplicate SKU"}
    ]
  },
  "meta": {
    "requestId": "req_abc123",
    "timestamp": "2026-07-03T12:00:00Z"
  }
}

4. Pagination

Pattern: Cursor-based Pagination (besser für große Datasets)

GET /api/v1/products?limit=20&cursor=eyJpZCI6MTIzfQ==

{
  "data": [...],
  "pagination": {
    "hasMore": true,
    "nextCursor": "eyJpZCI6MTQzfQ==",
    "total": 1234
  }
}

Alternativen:

5. Authentication & Authorization

JWT Structure:

interface JWTPayload {
  sub: string;           // User ID
  tenantId: string;       // Tenant ID
  role: 'admin' | 'manager' | 'sales';
  permissions: string[];  // ['products:read', 'products:write', ...]
  iat: number;
  exp: number;           // Access Token: 15min
}

Token Endpoints:

POST /api/v1/auth/login         # Login → {accessToken, refreshToken}
POST /api/v1/auth/refresh       # Refresh → {accessToken}
POST /api/v1/auth/logout        # Invalidate Refresh Token
POST /api/v1/auth/register      # Admin: Neuen User erstellen

Security Header:

Authorization: Bearer <access_token>
X-Tenant-ID: <tenant_id>        # Nur für Server→Server

6. Rate Limiting

Header:

X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 999
X-RateLimit-Reset: 1719993600
Retry-After: 60                  # Bei 429

Limits (per Tenant):

Plan Requests/Minute Requests/Stunde
Good 100 10,000
Better 500 50,000
Best 2000 200,000

7. Caching

Response Header:

Cache-Control: private, max-age=3600
ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
Last-Modified: Wed, 03 Jul 2026 12:00:00 GMT

Cache Strategy:

8. OpenAPI / Swagger

openapi: 3.1.0
info:
  title: Cartly API
  version: 1.0.0
  description: ERP+POS API für Fashion Retail
servers:
  - url: https://api.cartly.io/api/v1
paths:
  /products:
    get:
      summary: Liste aller Produkte
      parameters:
        - $ref: '#/components/parameters/TenantId'
        - $ref: '#/components/parameters/Limit'
        - $ref: '#/components/parameters/Cursor'

Endpunkt-Übersicht

Auth

POST   /api/v1/auth/login
POST   /api/v1/auth/refresh
POST   /api/v1/auth/logout
POST   /api/v1/auth/register    (Admin)
GET    /api/v1/auth/me

Products

GET    /api/v1/products
POST   /api/v1/products
GET    /api/v1/products/:id
PUT    /api/v1/products/:id
PATCH  /api/v1/products/:id
DELETE /api/v1/products/:id

Orders

GET    /api/v1/orders
POST   /api/v1/orders
GET    /api/v1/orders/:id
PATCH  /api/v1/orders/:id

Tenants (Admin only)

GET    /api/v1/tenants
POST   /api/v1/tenants
GET    /api/v1/tenants/:id
PATCH  /api/v1/tenants/:id

AI Proxy

POST   /api/v1/ai/completions    (OpenRouter Proxy)
GET    /api/v1/ai/models

Konsequenzen

Positiv

Negativ


Verwandte Entscheidungen


Erstellt: 2026-07-03 von Documentation Agent (a66674bf)