Aller au contenu

English version · Version Markdown, lisible par un agent · v0.3.0

Migrer depuis Supabase

Cette page déplace un projet Supabase vers un projet Bastide : le schéma et les données, les utilisateurs de l'authentification, les seaux et les objets du stockage. Votre application garde supabase-js : elle change l'adresse et la clé du projet.

Ce qu'il faut avant de commencer

  • Un projet Bastide créé avec son backend (project_create_with_backend), vierge : l'importeur refuse un projet cible déjà occupé, avant d'écrire.
  • L'adresse de connexion directe de la base source (rôle postgres, connexion directe ou mode session), sa clé service_role, et sa clé anon si vous voulez garder les clés historiques.
  • Un exploitant : pendant la bêta sur invitation, l'import est un geste d'exploitant, pas une opération du registre. Il ouvre la base du locataire en administrateur pour installer les extensions.
  • Lisez d'abord la section « Incompatibilités connues » plus bas : un client qui s'appuie sur SAML, sur le point S3 ou sur postgres_changes ne passe pas tel quel.

1. Lancer l'import

Les secrets de la source ne passent pas par la ligne de commande, que ps montre à tout utilisateur de l'hôte : ils se lisent dans l'environnement.

SUPABASE_DB_URL='postgres://postgres:<mot-de-passe>@db.<ref>.supabase.co:5432/postgres' SUPABASE_SERVICE_ROLE_KEY='<service_role>' SUPABASE_ANON_KEY='<anon>' pnpm importer:supabase <bak_…> --stockage https://<ref>.supabase.co --rapport rapport.json

--stockage est exigé dès que la source a un seau ; sans lui l'import s'interrompt et le dit. L'ordre des étapes n'est pas libre : inventaire et cible jugée vierge, utilisateurs, schéma et données (politiques et droits recomposés, en une transaction), stockage (une somme par objet), comparaison du catalogue et recomptage des lignes, rapport.

Le code de sortie dit l'issue : 0 importé, 3 importé avec des écarts à lire dans le rapport, 1 interrompu ou échoué, 2 pas fait (argument ou variable manquants). En cas d'échec, l'importeur ne défait rien : il écrit dans un projet neuf, que vous retirez si besoin ; chaque étape qui écrit le fait dans une transaction, et le rapport dit laquelle s'est arrêtée.

L'importeur n'écrit rien à la source : ni jeton révoqué, ni passage en lecture seule.

2. Double exécution : faire tourner les deux côtés avant de basculer

Tant que vous ne basculez pas, la source reste votre application vivante. La procédure :

1. Importer comme ci-dessus. L'import est une copie à un instant donné. 2. Garder les clés d'origine. Fournies à l'import, SUPABASE_ANON_KEY et SUPABASE_SERVICE_ROLE_KEY sont rangées par leur empreinte et restent acceptées douze mois, chaque réponse portant Deprecation: true et Sunset (voir LIM-09). Une copie de votre application peut donc viser Bastide sans changer ses clés : seule l'adresse du projet change. 3. Faire tourner une copie de l'application sur Bastide, à côté de la production qui reste sur Supabase : la même adresse de projet remplacée, les mêmes clés, le même parcours. Rejouez vos parcours ordinaires : inscription, confirmation, connexion, lecture, écriture, téléversement. 4. Comparer. Lisez le rapport (--rapport) : catalogue comparé, lignes recomptées, somme de chaque objet. Comparez ce que vos parcours rendent des deux côtés. 5. **Passer aux clés sb_publishable_… et sb_secret_…** (backend.key.create) avant l'échéance des douze mois, puis révoquez les clés historiques. 6. Basculer. Aucune réplication continue n'existe : les écritures faites à la source après l'import ne suivent pas. La fenêtre de lecture seule de la source (TR-194, EX-MIG-140) est prévue au lot L2 et n'existe pas encore. Pour basculer sans perte, suspendez vous-même les écritures de l'application, lancez l'import dans un NOUVEAU projet Bastide vierge, vérifiez le rapport, puis changez l'adresse de production.

Un retour arrière avant l'étape 6 est sans effet sur la source : ne rien faire côté Supabase, et retirer le projet Bastide.

3. Brancher l'application

Remplacez l'adresse et la clé du projet. Les chemins de Supabase sont tenus sans réécriture : https://<projet>.<domaine>/rest/v1/…, /auth/v1/…, /realtime/v1/…. Les en-têtes de supabase-js sont ceux de Supabase : la clé dans apikey, un jeton de session dans Authorization.

Incompatibilités connues

Chaque limite ci-dessous est reproduite par un test (pnpm limites) ; la page publique est https://bastide.app/limites-supabase.

  • LIM-01 : une réponse REST porte au plus 1000 lignes, tronquée sans erreur ; paginez (Range, ou limit et offset).
  • LIM-02 : une ressource ne s'embarque que le long d'une clé étrangère déclarée, sinon 400 PGRST200 ; déclarez la clé, ou exposez une vue.
  • LIM-03 : en mode transaction du mutualiseur (celui des connexions directes, par défaut), NOTIFY n'atteint pas un client qui fait LISTEN ; écoutez depuis une connexion en mode session.
  • LIM-04 : en mode transaction, pg_advisory_lock n'exclut pas une autre connexion du même projet ; employez pg_advisory_xact_lock, ou le mode session.
  • LIM-05 : en mode transaction, l'état de session n'est pas isolé (SET, table temporaire, curseur WITH HOLD) ; employez SET LOCAL, ON COMMIT DROP, ou le mode session.
  • LIM-06 : ne sont pas servis la connexion d'entreprise SAML (signInWithSSO), la liaison d'une identité (linkIdentity) et /auth/v1/settings (404, chemin_auth_inconnu). Une connexion d'entreprise passe par Keycloak en OpenID Connect. Les douze fournisseurs de signInWithOAuth et la double authentification sont servis.
  • LIM-07 : le point compatible S3 (/storage/v1/s3) n'est pas servi (404, chemin_inconnu) ; il viendra avec EX-DATA-370 (lot L2). Le stockage de storage-js est servi.
  • LIM-08 : le temps réel sert la diffusion et la présence, pas l'écoute des changements : un abonnement postgres_changes est refusé. Employez select realtime.diffuser_les_changements('public.ma_table').
  • LIM-09 : les clés historiques anon et service_role ne valent que pour un projet importé, deux clés d'origine seulement, douze mois. Toute autre reçoit 401 cle_refusee.
  • LIM-10 : updateUser ne change que le mot de passe ; une adresse ou des métadonnées sont refusées (400, changement_d_adresse_ailleurs, metadonnees_non_rangees). Employez POST /auth/v1/user/email, et une table du projet pour les métadonnées.
  • LIM-11 : une table n'est accordée aux rôles de l'API que si elle porte la sécurité au niveau des lignes ET au moins une politique ; les rôles portent le nom de la base en préfixe, donc un grant … to authenticated écrit tel quel est refusé (code 42704).

Autres écarts mesurés : l'utilisateur rendu ne porte pas last_sign_in_at (le champ est absent, pas nul) ; ne sont ni mesurés ni servis les codes de secours, le web3 et le changement d'adresse par le client. Le taux de la suite d'intégration officielle de supabase-js (95 % exigés par EX-DATA-090) n'est pas encore joué : elle suppose des clés asymétriques avec un point JWKS, prévues au lot L2.

Les briques sous le plan de données sont six, épinglées par empreinte (matrice des briques) : PostgreSQL 17, pgvector, Supavisor, PostgREST, imgproxy (édition libre) et SeaweedFS. Le client éprouvé est @supabase/supabase-js 2.117.2. L'import ne reprend que les extensions admises par le palier du projet cible.

Voir aussi : Migrer depuis Vercel, Sortir de Bastide.