ZeroToShip

Docs/Feature

// feature

Database (Supabase)

Il database è Postgres gestito da Supabase. Zero setup di server, niente ORM da imparare — usi il client Supabase e scrivi query in modo diretto. Row Level Security (RLS) attiva ti garantisce che ogni utente vede solo i suoi dati.

I due client

Come per l'auth, hai due helper: uno per il browser (Client Components), uno per il server (Server Components, route handler, Server Actions). Non mischiare mai i due — sono lì apposta per gestire i cookie di sessione nel modo giusto.

libs/supabase/client.ts
ts
"use client";
import { createClient } from "@/libs/supabase/client";

const supabase = createClient();
const { data } = await supabase.from("posts").select("*");

La tabella profiles

Il webhook Stripe assume che esista una tabella profiles con almeno queste colonne: id (UUID, foreign key a auth.users), email, customer_id, price_id, has_access (bool). La crei una volta sola nella SQL editor di Supabase:

supabase/schema.sql
sql
create table profiles (
  id uuid references auth.users on delete cascade primary key,
  email text,
  customer_id text,
  price_id text,
  has_access boolean default false,
  created_at timestamptz default now()
);

alter table profiles enable row level security;

create policy "Un utente vede solo il proprio profilo"
  on profiles for select
  using (auth.uid() = id);

Query lato server

In un Server Component leggi i dati dell'utente con la sessione già risolta dai cookie:

app/dashboard/page.tsx
tsx
const supabase = await createClient();
const { data: { user } } = await supabase.auth.getUser();

const { data: profile } = await supabase
  .from("profiles")
  .select("has_access, price_id")
  .eq("id", user.id)
  .single();

Grazie alle policy RLS, quella query ritorna solo la riga dell'utente loggato — anche se scrivi select * senza filtro, Postgres blocca l'accesso alle altre righe.