Supabase Storage est une couche de fichiers S3-compatible, intégrée à l'auth et au système RLS de PostgreSQL. Tu n'as pas de bucket AWS à configurer ni de règles IAM à gérer : tout se pilote depuis le dashboard ou le SDK JavaScript que tu connais déjà.
Ce qu'est un bucket
Un bucket est un conteneur de fichiers. Tu en crées un par "domaine" logique de ton app : un bucket avatars pour les photos de profil, un bucket documents pour les pièces jointes, etc. Chaque bucket a ses propres règles d'accès.
Tu peux créer un bucket depuis le dashboard (Storage > New bucket) ou via le SDK :
const { data, error } = await supabase.storage.createBucket('avatars')Bucket public ou privé
C'est la première décision à prendre lors de la création d'un bucket.
- Bucket public : chaque fichier est accessible via une URL directe, sans authentification. Adapté aux assets publics (logos, images de fond, fichiers téléchargeables sans restriction).
- Bucket privé : aucun fichier n'est accessible sans une URL signée temporaire. C'est le mode par défaut. Adapté aux documents personnels, avatars, données sensibles.
Uploader un fichier
La méthode upload prend un chemin de fichier et le contenu (File, Blob, ArrayBuffer, ReadableStream) :
const { data, error } = await supabase.storage
.from('avatars')
.upload('public/avatar1.png', file)Le premier argument est le chemin dans le bucket (pas le nom de l'input). Tu peux organiser tes fichiers en dossiers en incluant des / dans ce chemin. Un pattern courant : utiliser l'identifiant de l'utilisateur comme premier segment ({user_id}/avatar.jpg), ce qui simplifie les politiques RLS.
Récupérer l'URL d'un fichier
Bucket public : getPublicUrl
Pour un bucket marqué public, tu génères l'URL directement, sans aller sur le réseau :
const { data } = supabase.storage
.from('avatars')
.getPublicUrl('public/avatar1.png')
console.log(data.publicUrl)
// https://xyzabc.supabase.co/storage/v1/object/public/avatars/public/avatar1.pngPas d'await : cette méthode est synchrone, elle construit l'URL à partir des paramètres sans appel réseau.
Bucket privé : createSignedUrl
Pour un bucket privé, tu génères une URL temporaire signée. Le paramètre expiresIn est en secondes :
const { data, error } = await supabase.storage
.from('documents')
.createSignedUrl('contrats/2026/mon-contrat.pdf', 3600)
console.log(data.signedUrl)
// URL valide pendant 1 heureRLS sur Storage : les policies storage.objects
Le RLS (Row Level Security) de Supabase s'applique aussi au storage. Les fichiers sont des lignes dans la table storage.objects, et tu peux y attacher des policies exactement comme sur tes tables métier.
Sans policy : ton bucket privé est inaccessible, même pour le propriétaire du fichier. C'est le piège le plus fréquent.
Voici une policy typique : un utilisateur authentifié peut uploader et lire uniquement dans son propre dossier ({user_id}/...) :
-- Permettre à l'utilisateur d'uploader dans son dossier
create policy "Upload dans son propre dossier"
on storage.objects
for insert
to authenticated
with check (
bucket_id = 'avatars'
and (storage.foldername(name))[1] = (select auth.uid())::text
);
-- Permettre à l'utilisateur de lire ses propres fichiers
create policy "Lecture de ses propres fichiers"
on storage.objects
for select
to authenticated
using (
bucket_id = 'avatars'
and (storage.foldername(name))[1] = (select auth.uid())::text
);storage.foldername(name) décompose le chemin en segments. [1] est le premier segment, soit {user_id} si tu as suivi le pattern de nommage conseillé plus haut. auth.uid() retourne l'UUID de l'utilisateur connecté, cohérent avec les policies RLS des tables métier.
