Zum Hauptinhalt springen

API — CRUD-Patterns

Standard-Endpoints pro Resource

GET /api/customers # Liste
GET /api/customers/:id # Detail
POST /api/customers # Anlegen
PATCH /api/customers/:id # Teil-Update
PUT /api/customers/:id # Voll-Replace (selten)
DELETE /api/customers/:id # Soft-Delete

Listen + Pagination

GET /api/customers?page=1&limit=50&sort=createdAt:desc

Response:

{
"data": [...],
"meta": {
"page": 1,
"limit": 50,
"total": 234,
"pages": 5
}
}

Cursor-basiert (für sehr große Listen):

GET /api/customers?cursor=eyJ...&limit=100

Filter

Einfach (Query-Param)

?status=active
?createdAt[gte]=2026-01-01
?name[contains]=Müller

Komplex (POST-Body)

POST /api/customers/search
{
"filter": {
"AND": [
{ "status": "active" },
{ "OR": [
{ "name": { "contains": "Müller" } },
{ "ustId": "DE123" }
]
}
]
},
"sort": [{ "field": "createdAt", "order": "desc" }],
"include": ["locations", "primaryContact"],
"page": 1,
"limit": 50
}

Felder begrenzen (sparse fieldsets)

GET /api/customers?fields=id,name,email

Beziehungen einbinden (include)

GET /api/customers/:id?include=locations,banks,primaryContact

Gibt verschachtelte Objekte zurück.

Anlegen

POST /api/customers
{
"name": "Beispiel GmbH",
"ustId": "DE123456789",
"address": { "street": "...", "city": "Stuttgart", "zip": "70xxx" }
}

Response: 201 + neuer Datensatz.

Teil-Update

PATCH /api/customers/:id
{
"name": "Beispiel AG"
}

Nur die übergebenen Felder werden geändert.

Soft-Delete

DELETE /api/customers/:id

Setzt deletedAt-Flag. Datensatz im Papierkorb. Wiederherstellbar via:

POST /api/customers/:id/restore

Bulk-Operationen

Bulk-Create

POST /api/customers/bulk
[
{ "name": "Kunde 1", ... },
{ "name": "Kunde 2", ... }
]

Response: pro Item Status (success / error).

Bulk-Update

PATCH /api/customers/bulk
{
"filter": { "status": "inactive" },
"update": { "tags": ["archive"] }
}

Bulk-Delete

DELETE /api/customers/bulk
{ "ids": ["uuid1", "uuid2", ...] }

Idempotenz

Für POST/PATCH mit Idempotency-Key:

POST /api/customers
Idempotency-Key: my-unique-key-123

Mehrfache Aufrufe mit gleichem Key → gleiche Response, kein Doppel-Anlegen.

Validation

400-Errors mit Detail:

{
"error": {
"code": "VALIDATION",
"message": "Validation failed",
"details": {
"name": ["Pflichtfeld"],
"ustId": ["Format ungültig"]
}
}
}

Sub-Routen pro Resource

GET /api/customers/:id/locations
GET /api/customers/:id/banks
POST /api/customers/:id/sales-documents

Sub-Routen haben oft eigene CASL-Subjects (z.B. view:CustomerLocation).

Verwandte Doku