Supabase è diventato il backend di default per la maggior parte dei progetti JavaScript seri nel 2026. Offre PostgreSQL gestito, autenticazione completa, storage, realtime e Edge Functions — tutto con un SDK TypeScript eccellente e un piano gratuito generoso. Se stai costruendo un’applicazione con Next.js, React o qualsiasi framework JavaScript moderno, Supabase ti permette di avere un backend production-ready in meno di un’ora.
In questa guida copro setup, autenticazione, Row Level Security, realtime subscriptions e Edge Functions — con esempi di codice concreti e le best practice per il 2026.
Setup del progetto
Crea un progetto su supabase.com (il piano free include 500MB database, 1GB storage, 2GB bandwidth). Poi installa il client:
npm install @supabase/supabase-js @supabase/ssrLe variabili d’ambiente necessarie (nel tuo .env.local):
NEXT_PUBLIC_SUPABASE_URL=https://xxxx.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJ...
SUPABASE_SERVICE_ROLE_KEY=eyJ... # solo lato server, mai esporre al clientcreateBrowserClient vs createServerClient
Questa è la distinzione più importante per un’app Next.js con App Router. Sbagliare qui causa problemi di autenticazione e sicurezza.
createBrowserClient va usato esclusivamente in Client Components (quelli con "use client"). Gestisce i cookie automaticamente nel browser e mantiene la sessione dell’utente. Non usarlo mai in Server Components o Route Handlers.
// lib/supabase/client.ts
import { createBrowserClient } from "@supabase/ssr";
export function createClient() {
return createBrowserClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
);
}createServerClient va usato in Server Components, Route Handlers e Server Actions. Legge/scrive i cookie tramite le funzioni cookies() di Next.js:
// lib/supabase/server.ts
import { createServerClient } from "@supabase/ssr";
import { cookies } from "next/headers";
export async function createClient() {
const cookieStore = await cookies();
return createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
cookies: {
getAll() { return cookieStore.getAll(); },
setAll(cookiesToSet) {
cookiesToSet.forEach(({ name, value, options }) =>
cookieStore.set(name, value, options)
);
},
},
}
);
}Row Level Security (RLS)
RLS è la funzionalità più importante di Supabase dal punto di vista della sicurezza. Permette di definire regole a livello di database che determinano quali righe ogni utente può leggere, inserire, aggiornare o eliminare — indipendentemente da come viene chiamata l’API.
Esempio: una tabella projects dove ogni utente vede solo i suoi progetti:
-- Abilita RLS sulla tabella
ALTER TABLE projects ENABLE ROW LEVEL SECURITY;
-- Policy SELECT: gli utenti vedono solo i propri progetti
CREATE POLICY "users_read_own_projects"
ON projects FOR SELECT
USING (auth.uid() = user_id);
-- Policy INSERT: gli utenti possono creare progetti per se stessi
CREATE POLICY "users_insert_own_projects"
ON projects FOR INSERT
WITH CHECK (auth.uid() = user_id);
-- Policy UPDATE/DELETE
CREATE POLICY "users_modify_own_projects"
ON projects FOR ALL
USING (auth.uid() = user_id);Regola fondamentale: quando abiliti RLS su una tabella, di default nessuno può accedere a nulla. Devi creare esplicitamente le policy per ogni operazione che vuoi permettere. Se il tuo codice restituisce array vuoti inaspettati, probabilmente manca una policy.
Autenticazione con Supabase Auth
Supabase Auth supporta email/password, magic link, OAuth (Google, GitHub, Apple, ecc.) e phone OTP. Setup base con email/password:
// Sign up
const { data, error } = await supabase.auth.signUp({
email: "utente@example.com",
password: "password-sicura-12345",
});
// Sign in
const { data, error } = await supabase.auth.signInWithPassword({
email: "utente@example.com",
password: "password-sicura-12345",
});
// OAuth con Google
const { data, error } = await supabase.auth.signInWithOAuth({
provider: "google",
options: { redirectTo: `${window.location.origin}/auth/callback` },
});
// Ottieni utente corrente (server-side)
const { data: { user } } = await supabase.auth.getUser();
// Sign out
await supabase.auth.signOut();Per proteggere le route con middleware in Next.js, crea middleware.ts nella root:
import { createServerClient } from "@supabase/ssr";
import { NextResponse, type NextRequest } from "next/server";
export async function middleware(request: NextRequest) {
let supabaseResponse = NextResponse.next({ request });
const supabase = createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
cookies: {
getAll: () => request.cookies.getAll(),
setAll: (cookiesToSet) => {
cookiesToSet.forEach(({ name, value, options }) =>
supabaseResponse.cookies.set(name, value, options)
);
},
},
}
);
const { data: { user } } = await supabase.auth.getUser();
if (!user && !request.nextUrl.pathname.startsWith("/login")) {
return NextResponse.redirect(new URL("/login", request.url));
}
return supabaseResponse;
}
export const config = {
matcher: ["/dashboard/:path*", "/app/:path*"],
};Realtime subscriptions
Supabase Realtime permette di ricevere aggiornamenti in tempo reale quando i dati nel database cambiano. Usa Postgres Changes sotto il cofano — non hai bisogno di WebSocket personalizzati.
import { useEffect, useState } from "react";
import { createClient } from "@/lib/supabase/client";
export function useRealtimeMessages(roomId: string) {
const [messages, setMessages] = useState<Message[]>([]);
const supabase = createClient();
useEffect(() => {
// Carica messaggi iniziali
supabase
.from("messages")
.select("*")
.eq("room_id", roomId)
.order("created_at")
.then(({ data }) => setMessages(data ?? []));
// Subscription ai nuovi messaggi
const channel = supabase
.channel(`room-${roomId}`)
.on(
"postgres_changes",
{
event: "INSERT",
schema: "public",
table: "messages",
filter: `room_id=eq.${roomId}`,
},
(payload) => {
setMessages((prev) => [...prev, payload.new as Message]);
}
)
.subscribe();
return () => { supabase.removeChannel(channel); };
}, [roomId]);
return messages;
}Edge Functions
Le Edge Functions di Supabase sono funzioni serverless in TypeScript/Deno che girano ai margini della rete (vicino all’utente) con latenza minima. Ideali per webhook, trasformazioni dati, chiamate ad API esterne che non vuoi esporre al client.
// supabase/functions/send-notification/index.ts
import { serve } from "https://deno.land/std@0.208.0/http/server.ts";
serve(async (req) => {
const { userId, message } = await req.json();
// Chiama un servizio esterno (es. Resend per email)
const res = await fetch("https://api.resend.com/emails", {
method: "POST",
headers: {
Authorization: `Bearer ${Deno.env.get("RESEND_API_KEY")}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
from: "noreply@tuodominio.it",
to: [`${userId}@example.com`],
subject: "Notifica",
text: message,
}),
});
return new Response(JSON.stringify({ success: res.ok }), {
headers: { "Content-Type": "application/json" },
});
});Deploy con supabase functions deploy send-notification. Invocazione dal client: supabase.functions.invoke("send-notification", { body: { userId, message } }).
Risorse correlate
Approfondisci sul blog
- Stack zero cost SaaS: Vercel + Supabase + Clerk
- RAG con Claude API e pgvector
- Drizzle ORM: query type-safe in TypeScript
Documentazione e strumenti
FAQ
Supabase è adatto per applicazioni in produzione con migliaia di utenti?
Sì. Il piano Pro (€25/mese) supporta database fino a 8GB, 50GB storage e 250GB bandwidth — più che sufficiente per la maggior parte delle applicazioni SaaS fino a qualche decina di migliaia di utenti attivi. Supabase usa Postgres gestito su AWS e garantisce SLA del 99,9%.
Devo sempre usare RLS o posso usare la service role key lato server?
Puoi usare entrambi gli approcci. La service role key (che bypassa RLS) va usata solo in contesti server completamente fidati — mai esposta al client. RLS è preferibile perché sposta la logica di autorizzazione nel database, riducendo il rischio di errori nell’applicazione. Usa la service role solo per operazioni amministrative o job batch.
Le Edge Functions di Supabase supportano npm?
Le Edge Functions usano il runtime Deno, non Node.js. Puoi importare pacchetti da deno.land, esm.sh o jsr.io. Molti pacchetti npm sono disponibili tramite esm.sh con la sintassi import X from "npm:package-name". La compatibilità con l’ecosistema npm è quasi completa nel 2026.
Come migro da Firebase a Supabase?
Supabase offre una guida ufficiale di migrazione da Firebase. I punti principali: Firestore (document store) → tabelle Postgres con schema relazionale; Firebase Auth → Supabase Auth (stessa API per Google/GitHub OAuth); Firebase Storage → Supabase Storage (stessa struttura bucket). Lo sforzo maggiore è la riprogettazione del modello dati da document a relazionale.
Realtime funziona anche con tabelle che hanno RLS abilitato?
Sì, ma devi configurarlo correttamente. Le subscription realtime rispettano le RLS policy — l’utente riceve solo gli aggiornamenti per le righe che ha il permesso di leggere. Devi però abilitare esplicitamente la replication per la tabella nella dashboard Supabase (Database → Replication → toggle per la tabella).

