SocialCasts API
API REST de podcasts sociaux โ Recherche, ecoute, amis & recommandations
Symfony 7.3
PHP 8.3
PostgreSQL 16
JWT Auth
FrankenPHP
18 Endpoints
API operationnelle
JWT configure (24h TTL)
Rate limiting actif
37 tests PHPUnit
# Authentication
L'API utilise JWT (JSON Web Token) via LexikJWT. Le token a une duree de 24 heures.
Workflow : POST /api/login โ recuperer le
token โ l'envoyer dans le header Authorization: Bearer <token> pour toutes les requetes authentifiees.
Endpoints publics (pas de token)
GET /health | Health check |
POST /api/register | Creer un compte |
POST /api/login | Se connecter |
Endpoints proteges (token requis)
Toutes les autres routes /api/* retournent 401 sans token valide.
# Rate Limiting
Protection contre le brute-force avec un sliding window :
| Endpoint | Limite | Cle |
|---|---|---|
POST /api/register | 5 req/min | IP client |
POST /api/login | 5 req/min | IP client |
GET /api/search | 30 req/min | User authentifie |
Depassement โ HTTP 429 avec
{"error": true, "code": "RATE_LIMITED"}
# Error Handling
Toutes les erreurs suivent le meme format :
{
"error": true,
"message": "Description lisible",
"code": "ERROR_CODE"
}
| HTTP | Code | Description |
|---|---|---|
| 400 | VALIDATION_ERROR | Donnees invalides |
| 400 | DUPLICATE_EMAIL | Email deja utilise |
| 400 | ALREADY_SUBSCRIBED | Deja abonne |
| 400 | ALREADY_FRIENDS | Deja amis |
| 400 | REQUEST_ALREADY_SENT | Demande deja envoyee |
| 400 | NOT_FRIENDS | Pas amis |
| 401 | INVALID_CREDENTIALS | Identifiants incorrects |
| 404 | PODCAST_NOT_FOUND | Podcast inexistant |
| 404 | USER_NOT_FOUND | Utilisateur inexistant |
| 404 | NOT_SUBSCRIBED | Pas abonne |
| 404 | REQUEST_NOT_FOUND | Pas de demande en attente |
| 404 | NOTIFICATION_NOT_FOUND | Notification inexistante |
| 429 | RATE_LIMITED | Trop de requetes |
| 503 | ITUNES_UNAVAILABLE | iTunes indisponible |
# Auth Endpoints
POST
/api/register
Public
5 req/min
Creer un compte
Request Body
{
"email": "user@example.com",
"password": "SecurePass123"
}
Response 201
{
"data": {
"id": 1,
"email": "user@example.com"
}
}
POST
/api/login
Public
5 req/min
Se connecter (JWT)
Request Body
{
"email": "user@example.com",
"password": "SecurePass123"
}
Response 200
{
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOi..."
}
# Search & Discovery
GET
/api/search?term=X
JWT
30 req/min
Rechercher des podcasts
Query Params
term | Requis โ mot-cle de recherche |
Response 200
{
"data": [
{
"itunes_id": 1237401284,
"title": "JavaScript Jabber",
"author": "Charles M Wood",
"artwork_url": "https://...600x600bb.jpg",
"feed_url": "https://...feed"
}
]
}
GET
/api/podcasts/{itunesId}/episodes
JWT
Liste des episodes d'un podcast
Response 200
{
"data": [
{
"title": "Episode title",
"description": "Episode summary...",
"audio_url": "https://...ep1.mp3",
"duration": 3600,
"published_at": "2026-02-25T19:20:00+00:00"
}
]
}
# Library (Subscriptions & History)
POST
/api/subscriptions/{itunesId}
JWT
S'abonner a un podcast
Response 201
{
"data": {
"id": 1,
"itunes_id": 1237401284,
"title": "JavaScript Jabber",
"author": "Charles M Wood"
}
}
DEL
/api/subscriptions/{itunesId}
JWT
Se desabonner
Response 204 No Content
GET
/api/subscriptions
JWT
Liste des abonnements
Response 200
{
"data": [
{ "itunes_id": 123, "title": "...", "author": "...", "artwork_url": "..." }
]
}
POST
/api/history
JWT
Sauvegarder la position de lecture
Request Body
{
"itunes_id": 1237401284,
"last_position": 342,
"is_completed": false
}
Response 201 (creation) / 200 (mise a jour)
{
"data": {
"itunes_id": 1237401284,
"title": "JavaScript Jabber",
"last_position": 342,
"is_completed": false,
"played_at": "2026-04-02T14:55:49+00:00"
}
}
GET
/api/history?limit=20&offset=0
JWT
Historique de lecture (pagine)
Query Params
limit | Optionnel โ max 100, defaut 20 |
offset | Optionnel โ defaut 0 |
Response 200
{
"data": [ ... ],
"total": 42
}
# Notifications & Recommendations
POST
/api/recommendations
JWT
Recommander un podcast a un ami
Le destinataire doit etre un ami accepte.
Request Body
{
"receiver_id": 2,
"itunes_id": 1237401284,
"message": "Tu devrais ecouter ca !"
}
Response 201
{
"data": {
"id": 1,
"sender": { "id": 1, "email": "alice@test.com" },
"receiver": { "id": 2, "email": "bob@test.com" },
"podcast": { "itunes_id": 123, "title": "..." },
"message": "Tu devrais ecouter ca !"
}
}
GET
/api/notifications?unread=true
JWT
Liste des notifications
Query Params
unread | Optionnel โ true (defaut) ou false pour toutes |
Response 200
{
"data": [
{
"id": 1,
"sender": { "email": "alice@test.com" },
"podcast": { "title": "...", "artwork_url": "..." },
"message": "Tu devrais ecouter ca !",
"created_at": "2026-04-02T12:34:01+00:00"
}
]
}
PUT
/api/notifications/{id}/read
JWT
Marquer comme lue
Response 200
{ "data": { "id": 1, "is_read": true } }
# Data Model
| Entity | Table | Key Fields |
|---|---|---|
| User | users | id, email (unique), password, roles |
| Podcast | podcasts | id, itunes_id (unique bigint), title, author, artwork_url, feed_url |
| Subscription | subscriptions | user_id, podcast_id โ unique(user, podcast) |
| PlayHistory | play_histories | user_id, podcast_id, last_position, is_completed, played_at |
| Friendship | friendships | requester_id, addressee_id, status (pending|accepted) |
| Recommendation | recommendations | sender_id, receiver_id, podcast_id, message, is_read |
# Testing & Bruno Collection
La collection Bruno contient 28 requetes organisees par domaine :
| Dossier | Requetes | Endpoints couverts |
|---|---|---|
Auth/ | 8 | Health, Register, Login |
Search/ | 5 | Search, Episodes |
Library/ | 9 | Subscriptions, History |
Social/ | 14 | Users, Friends, Recommendations, Notifications |
Fixtures de demo :
alice@test.com / bob@test.com (password: secret123)
โ Amis avec une recommandation pre-configuree.
# Social (Friends)
nullsi l'utilisateur n'existe pas ou si c'est vous-meme.Response 200
{ "data": { "id": 2, "email": "bob@test.com" } }Response 201
{ "data": { "status": "pending", "requester_id": 1, "addressee_id": 2 } }Response 200
{ "data": { "status": "accepted" } }Response 204 No Content
Response 200
{ "data": [ { "id": 2, "email": "bob@test.com" } ] }Response 200
{ "data": [ { "id": 1, "email": "alice@test.com" } ] }