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 /healthHealth check
POST /api/registerCreer un compte
POST /api/loginSe 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 :

EndpointLimiteCle
POST /api/register5 req/minIP client
POST /api/login5 req/minIP client
GET /api/search30 req/minUser 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"
}
HTTPCodeDescription
400VALIDATION_ERRORDonnees invalides
400DUPLICATE_EMAILEmail deja utilise
400ALREADY_SUBSCRIBEDDeja abonne
400ALREADY_FRIENDSDeja amis
400REQUEST_ALREADY_SENTDemande deja envoyee
400NOT_FRIENDSPas amis
401INVALID_CREDENTIALSIdentifiants incorrects
404PODCAST_NOT_FOUNDPodcast inexistant
404USER_NOT_FOUNDUtilisateur inexistant
404NOT_SUBSCRIBEDPas abonne
404REQUEST_NOT_FOUNDPas de demande en attente
404NOTIFICATION_NOT_FOUNDNotification inexistante
429RATE_LIMITEDTrop de requetes
503ITUNES_UNAVAILABLEiTunes 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

termRequis โ€” 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

limitOptionnel โ€” max 100, defaut 20
offsetOptionnel โ€” defaut 0

Response 200

{
  "data": [ ... ],
  "total": 42
}

# Social (Friends)

GET /api/users/search?email=X JWT Rechercher un utilisateur
Retourne null si l'utilisateur n'existe pas ou si c'est vous-meme.

Response 200

{
  "data": { "id": 2, "email": "bob@test.com" }
}
POST /api/friends/{userId}/request JWT Envoyer une demande d'ami

Response 201

{ "data": { "status": "pending", "requester_id": 1, "addressee_id": 2 } }
PUT /api/friends/{userId}/accept JWT Accepter une demande

Response 200

{ "data": { "status": "accepted" } }
DEL /api/friends/{userId} JWT Supprimer un ami

Response 204 No Content

GET /api/friends JWT Liste des amis

Response 200

{ "data": [ { "id": 2, "email": "bob@test.com" } ] }
GET /api/friends/requests JWT Demandes en attente

Response 200

{ "data": [ { "id": 1, "email": "alice@test.com" } ] }

# 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

unreadOptionnel โ€” 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

EntityTableKey Fields
Userusersid, email (unique), password, roles
Podcastpodcastsid, itunes_id (unique bigint), title, author, artwork_url, feed_url
Subscriptionsubscriptionsuser_id, podcast_id โ€” unique(user, podcast)
PlayHistoryplay_historiesuser_id, podcast_id, last_position, is_completed, played_at
Friendshipfriendshipsrequester_id, addressee_id, status (pending|accepted)
Recommendationrecommendationssender_id, receiver_id, podcast_id, message, is_read

# Testing & Bruno Collection

La collection Bruno contient 28 requetes organisees par domaine :

DossierRequetesEndpoints couverts
Auth/8Health, Register, Login
Search/5Search, Episodes
Library/9Subscriptions, History
Social/14Users, Friends, Recommendations, Notifications
Fixtures de demo : alice@test.com / bob@test.com (password: secret123) โ€” Amis avec une recommandation pre-configuree.