eedb049bab
Next.js 16 app with full CRUD lead management: - REST API (/api/leads) with GET (pagination), POST, PUT, DELETE (soft delete) - Zod validation (server + client), Supabase (PostgreSQL) - Responsive UI: leads table (desktop) + cards (mobile), modal form, toast notifications - 43 tests (Vitest + React Testing Library): validation, components, pagination - Docker setup (multi-stage build, health check) - Supabase migration + 15 seed leads - Documentation (CLAUDE.md, README, API docs) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
208 lines
3.0 KiB
Markdown
208 lines
3.0 KiB
Markdown
# API Documentation
|
|
|
|
## Base URL
|
|
|
|
```
|
|
http://localhost:3000/api
|
|
```
|
|
|
|
## Endpoints
|
|
|
|
### List Leads
|
|
|
|
```
|
|
GET /api/leads?page=1&limit=10
|
|
```
|
|
|
|
**Query Parameters:**
|
|
|
|
| Parameter | Type | Default | Description |
|
|
|-----------|------|---------|-------------|
|
|
| page | number | 1 | Page number (min: 1) |
|
|
| limit | number | 10 | Items per page (min: 1, max: 100) |
|
|
|
|
**Response (200):**
|
|
|
|
```json
|
|
{
|
|
"data": [
|
|
{
|
|
"id": "uuid",
|
|
"first_name": "Max",
|
|
"email": "max@beispiel.de",
|
|
"phone": "+49 123 456789",
|
|
"fitness_goal": "abnehmen",
|
|
"created_at": "2026-01-01T00:00:00Z",
|
|
"updated_at": "2026-01-01T00:00:00Z",
|
|
"deleted_at": null
|
|
}
|
|
],
|
|
"meta": {
|
|
"current_page": 1,
|
|
"per_page": 10,
|
|
"total": 15,
|
|
"total_pages": 2,
|
|
"has_more": true
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### Create Lead
|
|
|
|
```
|
|
POST /api/leads
|
|
Content-Type: application/json
|
|
```
|
|
|
|
**Request Body:**
|
|
|
|
```json
|
|
{
|
|
"first_name": "Max",
|
|
"email": "max@beispiel.de",
|
|
"phone": "+49 123 456789",
|
|
"fitness_goal": "abnehmen"
|
|
}
|
|
```
|
|
|
|
| Field | Type | Required | Validation |
|
|
|-------|------|----------|------------|
|
|
| first_name | string | yes | 2-100 chars, trimmed |
|
|
| email | string | yes | Valid email, max 255, lowercased |
|
|
| phone | string | no | Max 20 chars, digits/spaces/+/- |
|
|
| fitness_goal | enum | yes | abnehmen, muskelaufbau, ausdauer, flexibilitaet |
|
|
|
|
**Response (201):**
|
|
|
|
```json
|
|
{
|
|
"data": { ... }
|
|
}
|
|
```
|
|
|
|
**Error (422) - Validation:**
|
|
|
|
```json
|
|
{
|
|
"error": {
|
|
"code": "VALIDATION_ERROR",
|
|
"message": "Validierungsfehler",
|
|
"details": {
|
|
"email": ["Bitte eine gueltige E-Mail-Adresse eingeben"]
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
**Error (409) - Duplicate email:**
|
|
|
|
```json
|
|
{
|
|
"error": {
|
|
"code": "CONFLICT",
|
|
"message": "Ein Lead mit dieser E-Mail-Adresse existiert bereits"
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### Get Lead
|
|
|
|
```
|
|
GET /api/leads/:id
|
|
```
|
|
|
|
**Response (200):**
|
|
|
|
```json
|
|
{
|
|
"data": { ... }
|
|
}
|
|
```
|
|
|
|
**Error (404):**
|
|
|
|
```json
|
|
{
|
|
"error": {
|
|
"code": "NOT_FOUND",
|
|
"message": "Lead nicht gefunden"
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### Update Lead
|
|
|
|
```
|
|
PUT /api/leads/:id
|
|
Content-Type: application/json
|
|
```
|
|
|
|
**Request Body (partial update):**
|
|
|
|
```json
|
|
{
|
|
"first_name": "Maximilian",
|
|
"fitness_goal": "muskelaufbau"
|
|
}
|
|
```
|
|
|
|
All fields are optional but at least one must be provided.
|
|
|
|
**Response (200):**
|
|
|
|
```json
|
|
{
|
|
"data": { ... }
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### Delete Lead (Soft Delete)
|
|
|
|
```
|
|
DELETE /api/leads/:id
|
|
```
|
|
|
|
Sets `deleted_at` timestamp instead of removing the record.
|
|
|
|
**Response (200):**
|
|
|
|
```json
|
|
{
|
|
"data": {
|
|
"message": "Lead erfolgreich geloescht"
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Error Response Format
|
|
|
|
All errors follow this structure:
|
|
|
|
```json
|
|
{
|
|
"error": {
|
|
"code": "ERROR_CODE",
|
|
"message": "Human-readable message",
|
|
"details": {}
|
|
}
|
|
}
|
|
```
|
|
|
|
| HTTP Status | Code | Description |
|
|
|------------|------|-------------|
|
|
| 400 | INVALID_ID / INVALID_JSON | Bad request |
|
|
| 404 | NOT_FOUND | Resource not found |
|
|
| 409 | CONFLICT | Duplicate entry |
|
|
| 422 | VALIDATION_ERROR | Input validation failed |
|
|
| 500 | DATABASE_ERROR / INTERNAL_ERROR | Server error |
|