expria-frontend/docs/adr/002-auth-api-decoupling.md

3.2 KiB

ADR 002 — Découplage auth-client et api-client

Statut : Accepté Date : 2026-04-17 Décideur : Hermann


Contexte

Le frontend doit :

  1. Obtenir un token JWT auprès de Supabase (authentification).
  2. Envoyer ce token au backend Hono sur chaque requête API (autorisation).

L'approche initiale proposée était un seul fichier api-client.ts contenant un intercepteur qui appelle directement le SDK Supabase pour récupérer le token avant chaque requête. Cette approche couple fortement le client HTTP à Supabase.

Options envisagées

Option A — API client avec intercepteur Supabase intégré

// api-client.ts
import { supabase } from './supabase'

async function apiFetch(path: string) {
  const { data } = await supabase.auth.getSession()
  return fetch(`${API_URL}${path}`, {
    headers: { Authorization: `Bearer ${data.session?.access_token}` }
  })
}
  • Avantages : code court, un seul fichier, familier aux développeurs juniors.
  • Inconvénients : couple définitivement l'HTTP layer à Supabase, rend impossible le test du client API sans mocker Supabase entier, bloque toute migration future d'auth (ex : passage à Auth0, Clerk, auth maison).

Option B — Deux fichiers distincts

// auth-client.ts  (connaît Supabase, rien d'autre)
export async function getAccessToken(): Promise<string | null> { ... }

// api-client.ts   (connaît HTTP, rien d'autre)
export async function apiFetch<T>(path: string, options: RequestOptions = {}): Promise<T> {
  const token = options.token ?? await getAccessToken()
  // fetch avec Bearer token
}
  • Avantages : séparation des responsabilités claire (auth vs HTTP), chaque fichier testable indépendamment, migration d'auth = modifier un seul fichier, respecte le principe d'inversion de dépendance.
  • Inconvénients : deux fichiers au lieu d'un (impact négligeable).

Décision

Option B — deux fichiers séparés :

  • src/shared/lib/auth-client.ts : gère exclusivement Supabase Auth (login, logout, register, récupération du token, refresh de session).
  • src/shared/lib/api-client.ts : gère exclusivement les appels HTTP vers api.expria.app (fetch, retry, timeout, logging, parsing d'erreurs).

L'api-client appelle getAccessToken() depuis l'auth-client, mais n'importe aucun type ni fonction Supabase directement.

Conséquences

Positives :

  • Un dev qui arrive comprend en 30 secondes : "ici l'auth, ici l'HTTP".
  • Le test de api-client.ts ne nécessite pas de mocker Supabase — on injecte un token factice.
  • Si Supabase est remplacé un jour (peu probable mais possible), seul auth-client.ts change. Aucun fichier feature n'est touché.
  • Cohérent avec la Règle 1 de DEVELOPMENT_PRINCIPLES.md : séparation stricte des responsabilités.

Négatives :

  • Un fichier supplémentaire. Coût négligeable.

À revisiter si :

  • Un besoin de couplage fort émerge (ex : un token custom généré par le backend plutôt que Supabase). Dans ce cas, la séparation reste valide — on change juste l'implémentation interne de getAccessToken().

Références

  • DEVELOPMENT_PRINCIPLES.md Règle 1 (séparation stricte)
  • ARCHITECTURE.md §10 Règle 1 (le frontend ne contient aucune logique métier)