Skip to content

L'API GraphQL

This content is not available in your language yet.

L’accueil et l’application joueur parlent à une seule API GraphQL (apps/api, GraphQL Yoga + Pothos). Elle ne contient aucune règle métier : chaque champ appelle une commande ou une requête de packages/domain, exécutée au nom de la personne connectée, sur le club de la requête.

  • Endpoint : POST /graphql sur l’API, en local http://localhost:<API_PORT>/graphql (le port est dans .ports).
  • Le club vient de l’hôte : l’en-tête x-bo-club-host (envoyé par l’accueil et l’application joueur), sinon l’en-tête Host. En local, le club de démonstration est bo.localhost. Un sous-domaine du club ou un domaine personnalisé vérifié fonctionnent de la même façon.
  • La personne vient du cookie de session Better Auth, émis pour cet hôte de club et refusé sur un autre. Sans session, vous êtes anonyme : seuls les champs ouverts à tous répondent, comme club, availability ou openingHours.
  • Protection CSRF : une requête « simple » sans x-bo-club-host est refusée. Envoyez toujours cet en-tête.
  • Le rôle (joueur, équipe, propriétaire) vient de l’adhésion au club. Un champ réservé à l’équipe ou au propriétaire est refusé avant même d’atteindre le domaine.

Une règle métier refusée revient comme une erreur GraphQL dont extensions.code est le nom de l’erreur du domaine, avec ses champs. L’accueil traduit ces codes en français (« Ce terrain est déjà pris sur ce créneau » pour SlotTaken).

  • Réservations : SlotTaken, OutsideOpeningHours, InvalidDuration, NotACreneau (entre deux créneaux : previous / next), DurationNotOnStep, OutsideBookingWindow, TooManyActiveBookings (le joueur nommé par userId / name a déjà maxActiveBookings matchs à venir dans ce sport), OverCapacity, InvalidTransition, HoldExpired, ShareUnpaid, NotInClub.
  • Tarifs et droits : EntitlementExhausted, EntitlementNotApplicable (avec rule : perDay, perWeek, perMonth, period, sport, duration, validity), ShareAlreadySettled.
  • Argent : AlreadyPaid, PaymentPending (payé chez le prestataire, confirmation en attente), InsufficientWallet, PaymentsNotConfigured, ProviderError, DayClosed (avec date : la journée a déjà son Z).
  • Horaires : ScheduleInUse (avec siteIds et assignmentIds : les sites et les périodes qui suivent encore cet horaire, passées comprises), ScheduleOverlap (avec conflictingAssignmentId : la période du même site ou terrain qui chevauche ; null quand deux enregistrements se sont croisés).
  • Événements : RegistrationClosed (avec reason : cancelled, closed (inscriptions closes), ended), AlreadyEntered (avec userIds : déjà dans une inscription en cours), EventFull (avec places : plus de place pour sortir de la liste d’attente), NotPartner.
  • Import : ImportFileUnreadable, ImportMappingIncomplete, ImportNotValidated, ImportAlreadyApplied, ImportDayClosed.
  • Club : ContrastTooLow, ImageRejected, HostRefused.
  • Génériques : NotFound (avec entity, id), Forbidden (avec reason), Invalid (avec field et reason).
  • Infrastructure : DbError, StorageError, EdgeError ne renvoient que le code, sans détail ; DbError a pour message database error. Un bug dans une commande revient en Internal (« internal error ») ; toute autre erreur inattendue revient masquée (INTERNAL_SERVER_ERROR). Le détail va dans les journaux du serveur.

bo api query envoie une requête à l’API de ce worktree, connecté par défaut comme propriétaire du club bo (--as none pour anonyme, --as <email> pour quelqu’un d’autre) :

Fenêtre de terminal
pnpm bo api query -q '{ planning(day: "2026-10-24T12:00:00Z") { bookings { kind startsAt } } }'
pnpm bo api query -q '{ availability(day: "2026-10-26T12:00:00Z", sport: "padel") { resources { resource { name } stepMin creneaux { startsAt endsAt state durationsMin } } } }'

Pour un script à vous, pnpm bo auth login <email> affiche un cookie de session et la commande curl correspondante.

Après une modification du schéma, pnpm bo api codegen réécrit apps/api/schema.graphql et régénère les types de l’accueil et de l’application joueur.

Le schéma complet est dans apps/api/schema.graphql. Les plus utiles :

  • Lecture : club (public), me, planning(day), availability(day, sport), booking(id), quote(input), openingHours, scheduleSettings, scheduleChangePreview(change) (les réservations qu’un changement d’horaire laisserait hors horaires, avant d’enregistrer), periods, tariffs, formules, clients(search), clientFile(userId) (la fiche client, section par section), wallet(userId), bookingMoney(bookingId), till(date), closings, strayPayments (l’argent reçu en trop, à rendre), pilotage(from, to, sport).
  • Côté joueur : playerClub, myBookings(scope), myBooking(id), myWallet, openMatches, searchMembers(q).
  • Réservations : createBooking, createBlock, createSeries, moveBooking, cancelBooking, changeBookingPlayers, checkIn, markNoShow.
  • Côté joueur, en écriture : bookAsPlayer, payMyShare, buyFormule, startTopUp.
  • Argent : collectShare, collectBooking, recordPayment, payWithWallet, createTopUp, refundPayment, closeDay.
  • Réglages : saveSchedule, deleteSchedule, setSiteDefaultSchedule, assignSchedule, setScheduleAssignmentDates, unassignSchedule, addClosure, createTariff, createPeriod, createFormule, sellFormule, createAddon, updateClubSettings, updateNotificationSettings.
  • Fiche client : addClientNote, removeClientNote.
  • Import : startImport, mapImport, applyImport.