Supabase : ce que ça ajoute au-dessus de PostgreSQL
Guide complet de Supabase pour les développeurs qui connaissent PostgreSQL : architecture interne, PostgREST, Auth, Realtime, Storage, RLS, et quand l'utiliser plutôt qu'un connecteur direct.
Vous connaissez PostgreSQL : vous savez créer des tables, écrire des requêtes, poser des index. Peut-être avez-vous déjà connecté une application en envoyant des requêtes SQL depuis votre code backend. Alors pourquoi Supabase ?
La réponse courte : Supabase résout le problème de donner accès à vos données à des clients que vous ne contrôlez pas — navigateurs, applications mobiles — sans que vous ayez à écrire toute une couche d’API.
Cet article détaille ce que Supabase apporte concrètement, comment chaque composant fonctionne, et les cas où il vaut mieux s’en passer.
1. Le problème que Supabase résout
Imaginez une application web classique. Votre frontend React fait des requêtes à votre backend Node.js/Go/Python. Votre backend valide, authentifie, puis exécute des requêtes SQL. C’est trois couches :
Browser → Backend API → PostgreSQL
Pour chaque table, chaque opération de lecture ou d’écriture, vous écrivez :
- Un endpoint REST
- La validation d’input
- La vérification d’autorisation
- La requête SQL
C’est du boilerplate répétitif. L’idée centrale de Supabase est de déplacer les règles d’autorisation directement dans PostgreSQL, et de laisser la base les appliquer elle-même à chaque requête. Au lieu d’écrire “dans mon backend, si l’utilisateur A demande des posts, je filtre sur user_id = A”, vous l’écrivez une fois en SQL dans la base, et c’est PostgreSQL qui filtre — pour toute requête, quel que soit le client.
Browser → Kong → PostgREST → PostgreSQL (règles d'accès dans la base)
Le backend API disparaît pour les lectures et écritures simples. Il reste pertinent pour la logique métier complexe, mais vous n’avez plus à écrire des dizaines d’endpoints pour exposer vos tables.
Les sections 4 (PostgREST) et 6 (RLS) détaillent comment ce mécanisme fonctionne en pratique. La section 11 montre le cycle de vie complet d’une requête de bout en bout.
2. La pile technique
Supabase n’est pas une base de données — c’est une plateforme qui enveloppe un PostgreSQL dédié dans une suite de services. Chaque composant est open source et indépendamment déployable. En auto-hébergement, le Docker Compose lance environ 12 conteneurs.
┌─────────────────────────────────────────────────────┐
│ Kong (API Gateway) │
│ /rest/v1 /auth/v1 /realtime/v1 /storage/v1 │
└──────┬──────────┬──────────┬──────────┬─────────────┘
│ │ │ │
PostgREST GoTrue Realtime Storage
(Haskell) (Go) (Elixir) (Node.js)
│ │ │ │
└──────────┴──────────┴──────────┘
│
PostgreSQL 15/16
(avec extensions : pgvector,
pg_graphql, pg_net, pgsodium...)
Voici le rôle de chaque composant :
- Kong : porte d’entrée unique. Toutes les requêtes passent par lui ; il les route vers le bon service selon le préfixe d’URL.
- PostgREST : génère automatiquement une API REST à partir de votre schéma SQL.
- GoTrue (Supabase Auth) : gère l’inscription, la connexion, et émet des tokens d’identité.
- Realtime : pousse les changements de la base vers les clients connectés via WebSocket.
- Storage : stockage de fichiers avec contrôle d’accès géré par PostgreSQL.
- Supavisor : gère le pool de connexions entre les services et PostgreSQL (section 10).
- Studio : le dashboard web pour administrer le tout (Next.js).
- Edge Functions : fonctions serverless TypeScript qui s’exécutent sur le même réseau que la base.
3. Connecteur direct vs Supabase
Un connecteur PostgreSQL (ou driver), c’est une bibliothèque dans votre langage de programmation qui permet à votre code d’envoyer des requêtes SQL directement à la base. En Python c’est psycopg2, en Go c’est pgx, en Node.js c’est pg. Votre code s’exécute sur un serveur, ouvre une connexion authentifiée avec un identifiant et un mot de passe, et exécute du SQL.
Ce modèle fonctionne bien quand votre code s’exécute dans un environnement de confiance — un serveur backend que vous contrôlez, un pipeline de traitement de données. Personne d’autre ne peut voir vos identifiants de connexion.
Le problème surgit dès que vous voulez exposer des données à un browser ou une app mobile. Ces clients s’exécutent sur des machines que vous ne contrôlez pas. N’importe qui peut ouvrir les outils développeur du navigateur et lire tout ce qui est embarqué dans le code JavaScript — identifiants de connexion compris. Vous ne pouvez pas mettre votre mot de passe PostgreSQL dans une page web.
Il vous faut donc une couche intermédiaire qui :
- Identifie l’utilisateur (authentification)
- Vérifie ce qu’il a le droit de faire (autorisation)
- Traduit ses demandes en requêtes SQL sécurisées
C’est précisément ce que Supabase fournit, pré-assemblé et configuré :
| Problème à résoudre | Ce que Supabase apporte |
|---|---|
| Exposer des données à des clients non fiables (browser, mobile) | PostgREST + RLS — aucun code d’API à écrire |
| Système d’authentification | GoTrue avec 20+ providers OAuth, SAML, OTP, magic links |
| Trop de connexions depuis des fonctions serverless | Supavisor, un pooler de connexions |
| Flux de changements en temps réel vers les clients | Realtime via WebSocket |
| Stockage de fichiers avec contrôle d’accès SQL | Storage avec RLS sur storage.objects |
| Compute serverless adjacent à la base | Edge Functions sur le même segment réseau |
| Typage TypeScript automatique du schéma | supabase gen types typescript |
| Parité dev local / prod | supabase start (Docker Compose, mêmes versions) |
En résumé : pour du serveur-à-serveur dans un périmètre de confiance, un connecteur direct reste souvent la meilleure option. Pour des clients externes, Supabase remplace une API CRUD entière.
4. PostgREST : l’API REST auto-générée
Langage : Haskell | Repo : PostgREST/postgrest
PostgREST est un serveur web qui se connecte à PostgreSQL et lit la structure de votre base — tables, colonnes, types, clés étrangères, fonctions — pour construire automatiquement une API REST complète. Sans génération de code, sans fichier de configuration. Ajoutez une colonne à une table : elle est immédiatement disponible dans l’API.
Comment ça fonctionne
Chaque requête HTTP est traduite en SQL paramétré. Un GET /rest/v1/users?age=gt.30 devient :
SELECT * FROM users WHERE age > 30
-- avec les règles d'accès évaluées pour l'utilisateur courant
PostgREST connaît vos clés étrangères et peut résoudre des JOINs automatiquement. Si posts a une colonne author_id référençant profiles, vous pouvez écrire :
GET /rest/v1/posts?select=id,title,author:profiles(name)
Sans écrire de SQL de JOIN.
Le modèle de rôles : comment PostgREST transmet l’identité à PostgreSQL
C’est la pièce clé du système. PostgreSQL permet de créer des rôles (des identités avec des permissions) : Supabase en crée deux par défaut, anon (visiteur non connecté) et authenticated (utilisateur avec un token valide).
Pour chaque requête reçue, PostgREST positionne deux variables dans la session PostgreSQL avant d’exécuter le SQL :
BEGIN;
SET LOCAL ROLE authenticated;
-- ^ dit à PostgreSQL : "cette requête vient du rôle authenticated"
SET LOCAL request.jwt.claims = '{"sub":"uuid-user","role":"authenticated",...}';
-- ^ stocke l'identité complète de l'utilisateur dans la session
SELECT * FROM posts;
-- ^ PostgreSQL applique maintenant les règles RLS du rôle authenticated
COMMIT;
SET LOCAL positionne une variable pour la durée de cette transaction uniquement. C’est ainsi que les fonctions SQL comme auth.uid() — que vous écrirez dans vos politiques RLS — peuvent connaître l’identité de l’utilisateur courant : elles lisent simplement request.jwt.claims, positionné par PostgREST dans la session avant chaque requête.
Filtrage, tri, pagination
Tout s’exprime en paramètres d’URL :
GET /rest/v1/posts?author_id=eq.42&order=created_at.desc&limit=20&offset=0
GET /rest/v1/posts?title=ilike.*supabase*
Limites de l’API REST
PostgREST ne permet pas le SQL arbitraire. Les requêtes complexes — agrégations, calculs avancés — doivent être encapsulées dans une vue SQL ou une fonction stockée, puis appelées via RPC (appel de fonction côté serveur) :
POST /rest/v1/rpc/get_monthly_stats
{"month": "2026-03"}
5. Auth : JWT et providers d’identité
Langage : Go | Repo : supabase/auth
Supabase Auth est une API d’authentification qui émet des JWT et stocke les enregistrements utilisateurs dans le schéma auth de votre PostgreSQL (auth.users, auth.sessions…). Ce schéma n’est pas exposé via PostgREST.
Un JWT (JSON Web Token) est un token signé numériquement. Il contient les informations sur l’utilisateur (son UUID, son rôle, son email) et une signature calculée avec une clé secrète. N’importe quel service qui connaît la clé peut vérifier que le token est authentique sans interroger la base — c’est ce qui permet à PostgREST de valider un token et d’en extraire l’identité en quelques microsecondes, sans aller-retour supplémentaire.
Modèle de tokens
- Access token : JWT de courte durée (1 heure par défaut). C’est lui qui est envoyé dans les headers de chaque requête API.
- Refresh token : token longue durée stocké en base. Quand l’access token expire, le client l’échange contre un nouveau. Le refresh token est invalidé et remplacé à chaque usage, ce qui limite la fenêtre d’exploitation en cas de vol.
Méthodes d’authentification supportées
Email : mot de passe, magic link (lien envoyé par email, pas de mot de passe), OTP (code à usage unique envoyé par email ou SMS)
SMS : Twilio, Vonage, MessageBird, Textlocal
OAuth (20+ providers) : Google, GitHub, Apple, Facebook, Discord, Slack, Spotify, LinkedIn, GitLab, Azure, Twitch, Twitter/X, Keycloak…
Enterprise : SAML 2.0 SSO (plans Team/Enterprise)
Confiance de JWT tiers : Supabase peut valider des tokens émis par des services externes comme Clerk ou Auth0. Cela permet d’utiliser la couche base de données de Supabase avec un système d’auth existant.
Connexion anonyme : crée un enregistrement utilisateur réel, convertible ensuite en compte complet en liant un provider.
Le lien avec PostgreSQL
Une fois qu’un utilisateur est connecté, son JWT accompagne chaque requête. PostgREST le lit, vérifie sa signature, et stocke son contenu dans la session PostgreSQL via SET LOCAL request.jwt.claims (voir section 4). Supabase fournit des fonctions SQL qui lisent ces données :
auth.uid() -- UUID de l'utilisateur courant
auth.jwt() -- Contenu complet du JWT (type jsonb)
auth.role() -- 'anon' ou 'authenticated'
Ces fonctions s’utilisent dans les politiques RLS pour écrire des règles comme “un utilisateur ne voit que ses propres lignes” — sans aucun code applicatif.
6. Row Level Security : la sécurité dans la base
RLS (Row Level Security) est une fonctionnalité native de PostgreSQL — pas spécifique à Supabase. Elle permet de définir des règles qui contrôlent quelles lignes d’une table un utilisateur peut voir ou modifier, directement en SQL.
Principe
Sans RLS, si un rôle a accès à une table, il voit toutes les lignes. Avec RLS activée, PostgreSQL filtre automatiquement selon des politiques :
ALTER TABLE posts ENABLE ROW LEVEL SECURITY;
-- Sans politique, personne ne peut rien lire (deny-by-default)
-- Les utilisateurs connectés ne voient que leurs propres posts
CREATE POLICY "read_own_posts"
ON posts FOR SELECT
TO authenticated
USING (auth.uid() = user_id);
-- Les visiteurs voient uniquement les posts publiés
CREATE POLICY "read_published_posts"
ON posts FOR SELECT
TO anon
USING (published = true);
La condition dans USING(...) est évaluée pour chaque ligne. Si elle est vraie, la ligne est retournée. Sinon, elle est silencieusement exclue — le client ne sait pas que la ligne existe. Aucun code applicatif n’applique ces règles : PostgreSQL le fait lui-même, pour toute requête, quel que soit son origine.
Performance : l’index manquant
RLS évalue la politique sur chaque ligne lue par la requête. Si la colonne utilisée dans la politique n’a pas d’index, PostgreSQL doit lire toute la table pour vérifier chaque ligne — exactement comme un WHERE sans index.
-- Sans cet index, chaque requête fait un scan complet de la table
CREATE INDEX ON posts (user_id);
Sur une grande table, l’absence de cet index peut multiplier le temps de requête par 100 ou plus. C’est le premier piège à éviter avec RLS.
Évaluation de auth.uid() : une fois par requête ou par ligne ?
Par défaut, PostgreSQL réévalue auth.uid() pour chaque ligne comparée. Sur 100 000 lignes, la fonction est appelée 100 000 fois. Entourer l’appel d’un SELECT suffit pour signaler à PostgreSQL qu’il s’agit d’une valeur scalaire constante pour toute la requête — il la calcule alors une seule fois avant de lancer le scan :
-- auth.uid() appelé pour chaque ligne (lent sur grande table)
USING (auth.uid() = user_id)
-- auth.uid() calculé une seule fois pour toute la requête (rapide)
USING (user_id = (SELECT auth.uid()))
Source : Supabase RLS Performance Best Practices
Politiques sur Storage
Les métadonnées des fichiers stockés dans Supabase sont dans une table PostgreSQL (storage.objects). RLS s’applique à cette table comme à n’importe quelle autre :
CREATE POLICY "upload_own_avatar"
ON storage.objects FOR INSERT
WITH CHECK (
bucket_id = 'avatars' AND
(storage.foldername(name))[1] = auth.uid()::text
);
7. Realtime : subscriptions en temps réel
Langage : Elixir / Phoenix | Repo : supabase/realtime
Realtime est un cluster Elixir distribué. Les clients se connectent via WebSocket et souscrivent à des channels. Trois capacités distinctes :
Postgres Changes
PostgreSQL maintient en permanence un journal interne de toutes les modifications — le Write-Ahead Log (WAL) — qui lui sert notamment à se récupérer après une panne et à répliquer les données vers d’autres serveurs. Realtime s’abonne à ce journal pour détecter les changements en temps réel et les pousser aux clients connectés.
Pour que Realtime puisse s’abonner au WAL, PostgreSQL lui réserve une position de lecture appelée slot de réplication : c’est un marqueur qui indique jusqu’où Realtime a lu. PostgreSQL ne supprime pas les données du WAL tant que tous les slots actifs ne les ont pas lues. Si Realtime prend du retard, le WAL grossit et peut remplir le disque — c’est un point de surveillance important en auto-hébergement.
supabase
.channel("posts-changes")
.on(
"postgres_changes",
{ event: "*", schema: "public", table: "posts" },
(payload) => console.log(payload),
)
.subscribe();
L’autorisation est appliquée par RLS : pour chaque ligne modifiée, Realtime vérifie que la politique SELECT de l’abonné l’autoriserait à la voir. Les abonnés non autorisés ne reçoivent pas la notification.
Broadcast
Pub/sub éphémère — n’importe quel client peut envoyer un message sur un channel, tous les abonnés le reçoivent immédiatement. Non persisté, latence très faible. Utilisé pour les curseurs collaboratifs, les notifications légères entre utilisateurs.
Presence
Un registre partagé entre tous les clients d’un channel. Chaque client y pousse son état ({user_id, cursor_position} par exemple) ; tous les abonnés reçoivent les mises à jour et les événements d’arrivée/départ. Conçu pour les fonctionnalités “qui est en ligne”.
8. Storage : fichiers avec contrôle d’accès SQL
Langage : TypeScript (Node.js) | Repo : supabase/storage
Supabase Storage est un service de stockage de fichiers. Son originalité : les métadonnées de chaque fichier (nom du bucket, chemin, type MIME, taille, propriétaire) sont stockées dans une table PostgreSQL ordinaire (storage.objects). Cela permet d’écrire des politiques RLS sur les fichiers exactement comme sur vos propres tables.
Par exemple, pour n’autoriser les téléchargements que si l’utilisateur est membre du projet auquel le fichier est rattaché :
CREATE POLICY "members_can_read_project_files"
ON storage.objects FOR SELECT
TO authenticated
USING (
bucket_id = 'project-files' AND
EXISTS (
SELECT 1 FROM project_members
WHERE project_id = (storage.foldername(name))[1]::uuid
AND user_id = auth.uid()
)
);
Sans Supabase, cette logique vivrait dans votre API, à écrire et maintenir manuellement.
Protocoles d’upload
Upload standard : HTTP multipart, jusqu’à 50 MB (plan gratuit).
Upload resumable (protocole TUS) : reprend automatiquement après une interruption réseau. Jusqu’à 500 GB sur les plans payants. Utile pour les fichiers vidéo ou les assets lourds.
Protocole S3 : Supabase Storage expose un endpoint compatible avec l’API AWS S3. N’importe quel outil S3 fonctionne sans modification (aws s3 cp, boto3, rclone). Les trois protocoles sont interopérables.
CDN et transformations
Servi via un CDN global (Cloudflare sur la plateforme hébergée). Transformations d’images à la volée (redimensionnement, recadrage, conversion de format) disponibles sur les plans payants.
9. Edge Functions : compute adjacent à la base
Runtime : Deno personnalisé | Repo : supabase/edge-runtime
Les Edge Functions sont des fonctions serverless TypeScript qui s’exécutent sur l’infrastructure Supabase, sur le même réseau que votre base. Chaque appel démarre un environnement isolé, exécute la fonction, et s’arrête — il n’y a pas d’état persistant entre deux appels.
Limites strictes
| Ressource | Limite |
|---|---|
| Temps CPU par requête | 2 secondes |
| Temps total (idle timeout) | 150 secondes |
| Taille du bundle | 20 MB |
| Mémoire | ~150 MB par isolate |
Les Edge Functions couvrent les cas que PostgREST ne peut pas gérer : logique conditionnelle complexe, appels à des APIs tierces, envoi d’emails, webhooks entrants.
Connexion à la base
import { createClient } from "https://esm.sh/@supabase/supabase-js";
const supabase = createClient(
Deno.env.get("SUPABASE_URL")!,
Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!,
);
Deno.serve(async (req) => {
const { data } = await supabase.from("posts").select("*");
return new Response(JSON.stringify(data));
});
La clé service_role bypasse RLS — toutes les lignes sont accessibles, sans filtre par utilisateur. Elle est réservée aux opérations administratives côté serveur, jamais exposée dans du code frontend.
Planification via pg_cron + pg_net
pg_cron est une extension PostgreSQL qui planifie des tâches directement depuis SQL, comme un cron. pg_net permet à PostgreSQL de faire des requêtes HTTP de façon asynchrone. Combinés, ils permettent de déclencher une Edge Function à intervalles réguliers sans infrastructure externe :
SELECT cron.schedule(
'daily-digest',
'0 8 * * *', -- tous les jours à 8h
$$
SELECT net.http_post(
url := 'https://project.supabase.co/functions/v1/daily-digest',
headers := '{"Authorization": "Bearer ' || current_setting('app.service_role_key') || '"}'
)
$$
);
10. Supavisor : pooler de connexions
Langage : Elixir | Repo : supabase/supavisor
PostgreSQL a une limite sur le nombre de connexions simultanées (quelques centaines par défaut). Ouvrir une connexion implique de démarrer un processus OS côté serveur — c’est coûteux à créer et maintenir.
Ce n’est pas un problème pour un serveur backend classique : il ouvre quelques connexions au démarrage et les réutilise. Mais pour les architectures serverless, c’est critique : une fonction Lambda ou Vercel démarre une nouvelle instance à chaque requête, chacune essayant d’ouvrir sa propre connexion. Avec quelques dizaines de requêtes simultanées, la base est vite saturée.
Un pooler de connexions résout ce problème en maintenant un petit nombre de connexions ouvertes vers PostgreSQL et en les partageant entre de nombreux clients — comme une caisse commune dans un supermarché où beaucoup de clients font la queue, plutôt qu’une caisse dédiée par client.
Supavisor (qui a remplacé PgBouncer en 2023-2024) expose deux modes :
- Mode session (port 5432) : chaque client obtient une connexion dédiée pour toute la durée de sa session. Nécessaire si vous utilisez des fonctionnalités qui s’appuient sur l’état de la connexion. Pour les backends serveur long-lived.
- Mode transaction (port 6543) : une connexion est allouée uniquement le temps d’une transaction, puis rendue au pool. Un même pool de 20 connexions peut ainsi servir des milliers d’appels simultanés, tant que chacun est court. Pour les fonctions serverless.
Règle pratique : depuis un environnement serverless (Edge Functions, Lambda, Vercel), utilisez le pooler (port 6543). Depuis un serveur long-lived, connectez-vous directement (port 5432).
11. Architecture interne : cycle de vie d’une requête
Pour ancrer tout ce qui précède, voici ce qui se passe concrètement quand un browser appelle supabase.from('posts').select('*') :
- Le SDK JS envoie
GET https://project.supabase.co/rest/v1/postsavec deux headers : le JWT de l’utilisateur et la cléanondu projet. - Kong reçoit la requête. Il vérifie que la clé
anoncorrespond au projet, puis route vers PostgREST. - PostgREST vérifie la signature du JWT avec la clé secrète du projet. Si le JWT est valide, il en extrait le rôle (
authenticated) et les claims (UUID, email…). - PostgREST prend une connexion dans le pool Supavisor et exécute une transaction :
BEGIN; SET LOCAL ROLE authenticated; -- PostgreSQL sait maintenant que c'est un utilisateur connecté SET LOCAL request.jwt.claims = '{"sub":"user-uuid","role":"authenticated",...}'; -- Les fonctions auth.uid(), auth.jwt() peuvent maintenant lire ces données SELECT * FROM posts; -- PostgreSQL évalue les politiques RLS avant de retourner les lignes COMMIT; - PostgreSQL évalue les politiques RLS sur
posts. La politiqueUSING (user_id = (SELECT auth.uid()))lit l’UUID depuis les claims de la session. Les lignes dontuser_idne correspond pas sont exclues. - Les résultats sont retournés en JSON : PostgREST → Kong → browser.
Les deux clés Supabase : la clé anon est publique — elle identifie le projet et active le rôle anon dans PostgreSQL. Elle peut figurer dans votre code frontend. La clé service_role bypasse toutes les politiques RLS. Elle ne doit jamais quitter votre serveur.
12. Pièges et limitations
PostgREST : cache du schéma
PostgREST lit la structure de votre base au démarrage et la met en cache. Si vous modifiez une table (ajout de colonne, nouvelle table), l’API retourne des erreurs jusqu’au rafraîchissement du cache — automatique sur la plateforme hébergée, potentiellement manuel en auto-hébergement. Prévoyez ce délai dans les déploiements qui modifient le schéma.
RLS : fonctions SECURITY DEFINER
En PostgreSQL, une fonction peut être déclarée SECURITY DEFINER : elle s’exécute alors avec les permissions de son créateur (souvent un super-utilisateur), et non celles de l’appelant. Si vous utilisez une telle fonction dans une politique RLS, elle peut lire des tables sans que RLS s’applique à ces lectures intermédiaires — c’est une fuite de données silencieuse possible.
RLS : sous-requêtes dans les politiques
Une sous-requête dans une politique RLS est réévaluée pour chaque ligne de la table. Sur une table de 100 000 lignes, la sous-requête s’exécute 100 000 fois :
-- Problème : la sous-requête tourne pour chaque ligne de la table
USING (user_id IN (
SELECT member_id FROM team_members WHERE team_id = auth.jwt()->>'team_id'
))
La même astuce que pour auth.uid() s’applique — entourer d’un SELECT pour forcer une évaluation unique :
-- La sous-requête est évaluée une seule fois
USING (user_id IN (
SELECT member_id FROM team_members
WHERE team_id = (SELECT auth.jwt()->>'team_id')
))
Realtime : surveiller le lag du slot de réplication
Realtime lit le journal WAL en continu. Si le serveur Realtime prend du retard — surcharge, réseau, redémarrage — PostgreSQL accumule les données WAL et ne peut pas les supprimer tant que Realtime ne les a pas lues. Le disque peut se remplir. En auto-hébergement, vérifiez régulièrement la vue pg_replication_slots : la colonne confirmed_flush_lsn doit avancer régulièrement.
Edge Functions : CPU 2 secondes
La limite de 2 secondes de CPU est stricte. Tout traitement lourd (cryptographie sur de grands payloads, inférence ML, traitement d’images complexe) heurtera ce plafond.
Free tier : pause après 7 jours d’inactivité
Les projets sur le plan gratuit sont mis en pause après 7 jours sans activité. Le réveil prend ~30 secondes (toute la pile redémarre). Inadapté à une production même légère.
13. Quand ne pas utiliser Supabase
Chemins d’écriture haute fréquence, faible latence : chaque requête passe par Kong → PostgREST → PostgreSQL, ce qui ajoute 5–30 ms selon la région. Pour un système de trading ou un classement de jeu mis à jour à la milliseconde, un connecteur direct depuis votre backend est plus adapté.
Charges analytiques lourdes : PostgreSQL sur Supabase est optimisé pour les transactions — de nombreuses petites requêtes rapides. Pour des requêtes analytiques sur des dizaines de millions de lignes, il faudra soit pg_duckdb (disponible via Supabase Warehouse), soit un outil analytique dédié.
Équipes avec une expertise infrastructure PostgreSQL : si vous gérez déjà PostgreSQL sur AWS RDS ou bare metal, Supabase coûte de l’argent sans vous apporter de valeur infrastructure.
Multi-région actif-actif : Supabase est mono-région par projet (réplicas en lecture disponibles sur Pro+, mais toutes les écritures vont sur une seule région). Pour un vrai actif-actif global, regardez CockroachDB ou Spanner.
Tarification Auth à l’échelle : à 1 million de MAU (Monthly Active Users — utilisateurs actifs dans le mois), le coût de l’Auth seul sur le plan Pro est ~3 250 $/mois. À cette échelle, un système d’authentification sur PostgreSQL direct est significativement moins cher.
14. Alternatives
| Besoin | Solution |
|---|---|
| Plateforme BaaS complète (auth + DB + realtime + storage + functions) avec PostgreSQL | Supabase |
| PostgreSQL uniquement, branching git-style instantané, vrai scale-to-zero | Neon |
| API GraphQL au-dessus d’un PostgreSQL existant, permissions champ par champ | Hasura |
| MySQL avec sharding horizontal (Vitess) et DDL non-bloquants à très grande échelle | PlanetScale |
| NoSQL, compatibilité Firebase, offline-first mobile | Firebase |
| PostgreSQL pour équipes investies dans l’ORM Prisma | Prisma Postgres |
| Contrôle maximal, équipe avec expertise PostgreSQL | PostgreSQL self-hosted (RDS, Hetzner, bare metal) |
Supabase vs Neon : Neon offre un branching copy-on-write au niveau du stockage — créer une copie d’une base de production est instantané, sans rejouer les migrations. Supabase Branching crée une nouvelle base vide et rejoue les migrations : c’est plus proche d’un CI de migrations que d’un clone de données. Si le branching instantané d’une base de production est votre besoin primaire, Neon l’implémente mieux.
Supabase vs Hasura : Hasura est un processus séparé avec un système de permissions GraphQL plus granulaire au niveau des champs. pg_graphql de Supabase s’exécute à l’intérieur de PostgreSQL — moins de customisation, mais zéro aller-retour vers un service externe. Si votre équipe frontend est investie dans Apollo Client ou Relay, Hasura est plus naturel.
Conclusion
L’idée centrale de Supabase tient en une phrase : déplacer les règles d’autorisation dans PostgreSQL, là où les données vivent, plutôt que dans une couche applicatif à écrire et maintenir. PostgREST expose vos tables en API REST, RLS contrôle ce que chaque utilisateur voit, et les autres composants — Auth, Realtime, Storage, Edge Functions — complètent la pile pour livrer une application sans backend sur mesure.
Pour un backend serveur-à-serveur dans un périmètre de confiance, un connecteur direct reste la meilleure option. Pour une application full-stack où le browser touche directement la base, Supabase élimine une quantité substantielle de boilerplate — à condition de maîtriser RLS, ses performances, et ses limites.
Sources :
- Architecture Supabase — DeepWiki
- PostgREST Documentation
- Supabase Auth Architecture
- Row Level Security — Supabase Docs
- RLS Performance Best Practices
- Realtime Architecture
- Storage v3 : Resumable Uploads
- S3 Compatible Storage
- Edge Functions Architecture
- Supavisor — GitHub
- Supabase Pricing
- Neon vs Supabase — Bytebase
- Supabase vs Firebase — Bytebase