# Frontend Integration Guide — Veza Backend API **Version**: 1.2.0 **Date**: 2025-01-27 **Base URL**: `http://localhost:8080/api/v1` (configurable via `VITE_API_URL`) --- ## Table des Matières 1. [Configuration](#configuration) 2. [Authentification](#authentification) 3. [Format des Réponses](#format-des-réponses) 4. [Gestion des Erreurs](#gestion-des-erreurs) 5. [Codes d'Erreur](#codes-derreur) 6. [Exemples de Requêtes](#exemples-de-requêtes) 7. [Health Checks](#health-checks) --- ## Configuration ### Variables d'Environnement Frontend ```bash # .env.local ou .env.production VITE_API_URL=http://localhost:8080/api/v1 VITE_WS_URL=ws://localhost:8081/ws VITE_STREAM_URL=ws://localhost:8082/stream ``` ### Headers Requis Toutes les requêtes authentifiées doivent inclure : ```javascript { "Authorization": "Bearer ", "Content-Type": "application/json" } ``` --- ## Authentification ### Format du Token JWT Le token JWT est fourni dans le header `Authorization` : ``` Authorization: Bearer ``` ### Endpoints d'Authentification #### POST `/auth/login` **Request** : ```json { "email": "user@example.com", "password": "password123", "remember_me": false } ``` **Response Success (200)** : ```json { "success": true, "data": { "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "expires_in": 3600, "token_type": "Bearer", "user": { "id": "550e8400-e29b-41d4-a716-446655440000", "email": "user@example.com", "username": "username", "role": "user" } } } ``` **Response Error (401)** : ```json { "success": false, "data": null, "error": { "code": 1000, "message": "Invalid credentials", "request_id": "req-123", "timestamp": "2025-01-27T10:00:00Z" } } ``` #### POST `/auth/register` **Request** : ```json { "email": "user@example.com", "password": "password123", "username": "username" } ``` #### POST `/auth/refresh` **Request** : ```json { "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." } ``` #### GET `/auth/me` **Headers** : ``` Authorization: Bearer ``` **Response Success (200)** : ```json { "success": true, "data": { "id": "550e8400-e29b-41d4-a716-446655440000", "email": "user@example.com", "username": "username", "role": "user", "created_at": "2025-01-27T10:00:00Z" } } ``` --- ## Format des Réponses ### Réponse Succès Toutes les réponses de succès suivent ce format : ```json { "success": true, "data": { ... }, "message": "Optional success message" } ``` **Exemples** : - **GET** `/tracks/:id` : ```json { "success": true, "data": { "id": "550e8400-e29b-41d4-a716-446655440000", "title": "Track Title", "artist": "Artist Name", "duration": 180.5, "status": "ready" } } ``` - **POST** `/tracks` (201 Created) : ```json { "success": true, "data": { "id": "550e8400-e29b-41d4-a716-446655440000", "title": "New Track", "status": "processing" }, "message": "Track uploaded successfully" } ``` ### Réponse Erreur Toutes les réponses d'erreur suivent ce format standardisé : ```json { "success": false, "data": null, "error": { "code": 2000, "message": "Validation failed", "details": [ { "field": "email", "message": "Email is required" } ], "request_id": "req-123", "timestamp": "2025-01-27T10:00:00Z", "context": { "user_id": "550e8400-e29b-41d4-a716-446655440000" } } } ``` **Champs** : - `code` : Code d'erreur numérique (voir [Codes d'Erreur](#codes-derreur)) - `message` : Message d'erreur lisible - `details` : Détails de validation (optionnel, pour erreurs 400) - `request_id` : ID de requête pour corrélation des logs (optionnel) - `timestamp` : Timestamp ISO 8601 - `context` : Contexte additionnel (optionnel) --- ## Gestion des Erreurs ### TypeScript Interface ```typescript interface APIError { code: number; message: string; details?: Array<{ field: string; message: string; }>; request_id?: string; timestamp: string; context?: Record; } interface APIResponse { success: boolean; data: T | null; error?: APIError; } ``` ### Helper Function ```typescript async function apiRequest( endpoint: string, options?: RequestInit ): Promise { const token = localStorage.getItem('access_token'); const response = await fetch(`${import.meta.env.VITE_API_URL}${endpoint}`, { ...options, headers: { 'Content-Type': 'application/json', ...(token && { Authorization: `Bearer ${token}` }), ...options?.headers, }, }); const data: APIResponse = await response.json(); if (!data.success || data.error) { throw new APIError(data.error!); } return data.data!; } class APIError extends Error { constructor(public error: APIError) { super(error.message); this.name = 'APIError'; } } ``` ### Gestion des Codes HTTP | Code HTTP | Signification | Action Frontend | |-----------|---------------|-----------------| | 200 | Succès | Traiter `data` | | 201 | Créé | Traiter `data` | | 400 | Bad Request | Afficher `error.message` + `error.details` | | 401 | Unauthorized | Rediriger vers login, rafraîchir token | | 403 | Forbidden | Afficher erreur, vérifier permissions | | 404 | Not Found | Afficher "Ressource non trouvée" | | 409 | Conflict | Afficher `error.message` | | 422 | Unprocessable Entity | Afficher `error.message` + `error.details` | | 429 | Too Many Requests | Afficher "Trop de requêtes", attendre | | 500 | Internal Server Error | Logger `request_id`, afficher message générique | | 502 | Bad Gateway | Afficher "Service temporairement indisponible" | --- ## Codes d'Erreur ### Authentication & Authorization (1000-1999) | Code | Message | HTTP Status | |------|---------|-------------| | 1000 | Invalid credentials | 401 | | 1001 | Token expired | 401 | | 1002 | Token invalid | 401 | | 1003 | Forbidden | 403 | | 1004 | Unauthorized | 401 | ### Validation (2000-2999) | Code | Message | HTTP Status | |------|---------|-------------| | 2000 | Validation failed | 400 | | 2001 | Required field | 400 | | 2002 | Invalid format | 400 | | 2003 | Out of range | 400 | ### Resource (3000-3999) | Code | Message | HTTP Status | |------|---------|-------------| | 3000 | Not found | 404 | | 3001 | Already exists | 409 | | 3002 | Conflict | 409 | ### Business Logic (4000-4999) | Code | Message | HTTP Status | |------|---------|-------------| | 4000 | Operation not allowed | 422 | | 4005 | Quota exceeded | 403 | ### Rate Limiting (5000-5099) | Code | Message | HTTP Status | |------|---------|-------------| | 5000 | Rate limit exceeded | 429 | ### Internal (9000-9999) | Code | Message | HTTP Status | |------|---------|-------------| | 9000 | Internal error | 500 | | 9001 | Database error | 500 | --- ## Exemples de Requêtes ### Health Check ```bash curl -X GET http://localhost:8080/api/v1/health ``` **Response** : ```json { "status": "ok" } ``` ### Login ```bash curl -X POST http://localhost:8080/api/v1/auth/login \ -H "Content-Type: application/json" \ -d '{ "email": "user@example.com", "password": "password123", "remember_me": false }' ``` **Response Success** : ```json { "success": true, "data": { "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "expires_in": 3600, "token_type": "Bearer", "user": { "id": "550e8400-e29b-41d4-a716-446655440000", "email": "user@example.com", "username": "username", "role": "user" } } } ``` **Response Error (Invalid Credentials)** : ```json { "success": false, "data": null, "error": { "code": 1000, "message": "Invalid credentials", "request_id": "req-123", "timestamp": "2025-01-27T10:00:00Z" } } ``` ### Get User Profile ```bash curl -X GET http://localhost:8080/api/v1/auth/me \ -H "Authorization: Bearer " ``` **Response Success** : ```json { "success": true, "data": { "id": "550e8400-e29b-41d4-a716-446655440000", "email": "user@example.com", "username": "username", "role": "user", "created_at": "2025-01-27T10:00:00Z" } } ``` ### Get Track ```bash curl -X GET http://localhost:8080/api/v1/tracks/550e8400-e29b-41d4-a716-446655440000 \ -H "Authorization: Bearer " ``` **Response Success** : ```json { "success": true, "data": { "id": "550e8400-e29b-41d4-a716-446655440000", "title": "Track Title", "artist": "Artist Name", "duration": 180.5, "status": "ready", "user_id": "550e8400-e29b-41d4-a716-446655440000", "created_at": "2025-01-27T10:00:00Z" } } ``` **Response Error (Not Found)** : ```json { "success": false, "data": null, "error": { "code": 3000, "message": "track not found", "request_id": "req-123", "timestamp": "2025-01-27T10:00:00Z" } } ``` ### Create Playlist ```bash curl -X POST http://localhost:8080/api/v1/playlists \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "title": "My Playlist", "description": "A great playlist", "is_public": true }' ``` **Response Success (201)** : ```json { "success": true, "data": { "id": "550e8400-e29b-41d4-a716-446655440000", "title": "My Playlist", "description": "A great playlist", "is_public": true, "user_id": "550e8400-e29b-41d4-a716-446655440000", "created_at": "2025-01-27T10:00:00Z" } } ``` **Response Error (Validation)** : ```json { "success": false, "data": null, "error": { "code": 2000, "message": "Validation failed", "details": [ { "field": "title", "message": "Title is required" } ], "request_id": "req-123", "timestamp": "2025-01-27T10:00:00Z" } } ``` --- ## Health Checks ### GET `/health` Health check simple (toujours 200 si serveur actif). ```bash curl -X GET http://localhost:8080/api/v1/health ``` **Response** : ```json { "status": "ok" } ``` ### GET `/readyz` Readiness probe (vérifie DB, Redis, RabbitMQ). ```bash curl -X GET http://localhost:8080/api/v1/readyz ``` **Response Success (200)** : ```json { "status": "ready", "database": "ok", "redis": "ok", "rabbitmq": "ok" } ``` **Response Degraded (200)** : ```json { "status": "degraded", "database": "ok", "redis": "unavailable", "rabbitmq": "unavailable" } ``` **Note** : `/readyz` retourne toujours 200 (même en mode dégradé) pour permettre au service de démarrer même si Redis/RabbitMQ sont indisponibles. ### GET `/status` Status détaillé (DB, Redis, Chat Server, Stream Server). ```bash curl -X GET http://localhost:8080/api/v1/status ``` **Response** : ```json { "status": "ok", "database": { "status": "ok", "latency_ms": 5 }, "redis": { "status": "ok", "latency_ms": 2 }, "chat_server": { "status": "ok", "url": "http://localhost:8081" }, "stream_server": { "status": "ok", "url": "http://localhost:8082" } } ``` --- ## Notes Importantes ### CORS En production, `CORS_ALLOWED_ORIGINS` doit être configuré. Le Frontend doit être dans la liste des origines autorisées. ### Rate Limiting - **Login** : 5 tentatives par minute - **Global** : 100 requêtes par minute (configurable) Les headers de rate limiting sont retournés : ``` X-RateLimit-Limit: 100 X-RateLimit-Remaining: 99 X-RateLimit-Reset: 1234567890 ``` ### Pagination Les listes (tracks, playlists, etc.) supportent la pagination : ``` GET /tracks?page=1&limit=20 ``` **Response** : ```json { "success": true, "data": [...], "pagination": { "page": 1, "limit": 20, "total": 100, "total_pages": 5, "has_next": true, "has_previous": false } } ``` ### UUID Format Tous les IDs sont des UUID v4 : ``` 550e8400-e29b-41d4-a716-446655440000 ``` --- ## Support - **Documentation API** : `/swagger/index.html` (Swagger UI) - **Issues** : [GitHub Issues](https://github.com/veza/veza-backend-api/issues) --- **Last Updated**: 2025-01-27