Salta ai contenuti

Supabase e Astro

Supabase è un’alternativa open source a Firebase. Fornisce un database Postgres, autenticazione, funzioni edge, sottoscrizioni in tempo reale e archiviazione.

  • Un progetto Supabase. Se non ne hai uno, puoi registrarti gratuitamente su supabase.com e creare un nuovo progetto.
  • Un progetto Astro con il rendering lato server (SSR) abilitato.
  • Credenziali Supabase per il tuo progetto. Puoi trovarle nella scheda Settings > API del tuo progetto Supabase.
    • SUPABASE_URL: L’URL del tuo progetto Supabase.
    • SUPABASE_ANON_KEY: La chiave anonima per il tuo progetto Supabase.

Per aggiungere le tue credenziali Supabase al tuo progetto Astro, aggiungi quanto segue al tuo file .env:

.env
SUPABASE_URL=YOUR_SUPABASE_URL
SUPABASE_ANON_KEY=YOUR_SUPABASE_ANON_KEY

Ora, queste variabili d’ambiente sono disponibili nel tuo progetto.

Se desideri avere IntelliSense per le tue variabili d’ambiente, modifica o crea il file env.d.ts nella tua directory src/ e aggiungi quanto segue:

src/env.d.ts
interface ImportMetaEnv {
readonly SUPABASE_URL: string
readonly SUPABASE_ANON_KEY: string
}
interface ImportMeta {
readonly env: ImportMetaEnv
}

Il tuo progetto dovrebbe ora includere questi file:

  • Directorysrc/
    • env.d.ts
  • .env
  • astro.config.mjs
  • package.json

Per connetterti a Supabase, dovrai installare @supabase/supabase-js nel tuo progetto.

Terminal window
npm install @supabase/supabase-js

Successivamente, crea una cartella chiamata lib nella tua directory src/. Qui aggiungerai il tuo client Supabase.

In supabase.ts, aggiungi quanto segue per inizializzare il tuo client Supabase:

src/lib/supabase.ts
import { createClient } from "@supabase/supabase-js";
export const supabase = createClient(
import.meta.env.SUPABASE_URL,
import.meta.env.SUPABASE_ANON_KEY,
);

Ora, il tuo progetto dovrebbe includere questi file:

  • Directorysrc/
    • Directorylib/
      • supabase.ts
    • env.d.ts
  • .env
  • astro.config.mjs
  • package.json

Aggiunta dell’autenticazione con Supabase

Sezione intitolata Aggiunta dell’autenticazione con Supabase

Supabase fornisce l’autenticazione out of the box. Supporta l’autenticazione email/password e l’autenticazione OAuth con molti provider, tra cui GitHub, Google e molti altri.

  • Un progetto Astro inizializzato con Supabase.
  • Un progetto Supabase con l’autenticazione email/password abilitata. Puoi abilitarla nella scheda Authentication > Providers del tuo progetto Supabase.

Creazione degli endpoint server di autenticazione

Sezione intitolata Creazione degli endpoint server di autenticazione

Per aggiungere l’autenticazione al tuo progetto, dovrai creare alcuni endpoint server. Questi endpoint verranno utilizzati per registrare, accedere e disconnettere gli utenti.

  • POST /api/auth/register: per registrare un nuovo utente.
  • POST /api/auth/signin: per accedere come utente.
  • GET /api/auth/signout: per disconnettere un utente.

Crea questi endpoint nella directory src/pages/api/auth del tuo progetto. Il tuo progetto dovrebbe ora includere questi nuovi file:

  • Directorysrc/
    • Directorylib/
      • supabase.ts
    • Directorypages/
      • Directoryapi/
        • Directoryauth/
          • signin.ts
          • signout.ts
          • register.ts
    • env.d.ts
  • .env
  • astro.config.mjs
  • package.json

register.ts crea un nuovo utente in Supabase. Accetta una richiesta POST con un’email e una password. Quindi utilizza l’SDK di Supabase per creare un nuovo utente.

src/pages/api/auth/register.ts
import type { APIRoute } from "astro";
import { supabase } from "../../../lib/supabase";
export const POST: APIRoute = async ({ request, redirect }) => {
const formData = await request.formData();
const email = formData.get("email")?.toString();
const password = formData.get("password")?.toString();
if (!email || !password) {
return new Response("Email e password sono obbligatorie", { status: 400 });
}
const { error } = await supabase.auth.signUp({
email,
password,
});
if (error) {
return new Response(error.message, { status: 500 });
}
return redirect("/signin");
};

signin.ts accede come utente. Accetta una richiesta POST con un’email e una password. Quindi utilizza l’SDK di Supabase per accedere come utente.

src/pages/api/auth/signin.ts
import type { APIRoute } from "astro";
import { supabase } from "../../../lib/supabase";
export const POST: APIRoute = async ({ request, cookies, redirect }) => {
const formData = await request.formData();
const email = formData.get("email")?.toString();
const password = formData.get("password")?.toString();
if (!email || !password) {
return new Response("Email e password sono obbligatorie", { status: 400 });
}
const { data, error } = await supabase.auth.signInWithPassword({
email,
password,
});
if (error) {
return new Response(error.message, { status: 500 });
}
const { access_token, refresh_token } = data.session;
cookies.set("sb-access-token", access_token, {
path: "/",
});
cookies.set("sb-refresh-token", refresh_token, {
path: "/",
});
return redirect("/dashboard");
};

signout.ts disconnette un utente. Accetta una richiesta GET e rimuove i token di accesso e aggiornamento dell’utente.

src/pages/api/auth/signout.ts
import type { APIRoute } from "astro";
export const GET: APIRoute = async ({ cookies, redirect }) => {
cookies.delete("sb-access-token", { path: "/" });
cookies.delete("sb-refresh-token", { path: "/" });
return redirect("/signin");
};

Creazione delle pagine di autenticazione

Sezione intitolata Creazione delle pagine di autenticazione

Ora che hai creato i tuoi endpoint server, crea le pagine che li utilizzeranno.

  • src/pages/register: contiene un modulo per registrare un nuovo utente.
  • src/pages/signin: contiene un modulo per accedere come utente.
  • src/pages/dashboard: contiene una pagina accessibile solo agli utenti autenticati.

Crea queste pagine nella directory src/pages. Il tuo progetto dovrebbe ora includere questi nuovi file:

  • Directorysrc/
    • Directorylib/
      • supabase.ts
    • Directorypages/
      • Directoryapi/
        • Directoryauth/
          • signin.ts
          • signout.ts
          • register.ts
      • register.astro
      • signin.astro
      • dashboard.astro
    • env.d.ts
  • .env
  • astro.config.mjs
  • package.json

register.astro contiene un modulo per registrare un nuovo utente. Accetta un’email e una password e invia una richiesta POST a /api/auth/register.

src/pages/register.astro
---
import Layout from "../layouts/Layout.astro";
---
<Layout title="Registrazione">
<h1>Registrazione</h1>
<p>Hai già un account? <a href="/signin">Accedi</a></p>
<form action="/api/auth/register" method="post">
<label for="email">Email</label>
<input type="email" name="email" id="email" />
<label for="password">Password</label>
<input type="password" name="password" id="password" />
<button type="submit">Registrati</button>
</form>
</Layout>

signin.astro contiene un modulo per accedere come utente. Accetta un’email e una password e invia una richiesta POST a /api/auth/signin. Controlla anche la presenza dei token di accesso e aggiornamento. Se sono presenti, reindirizza alla dashboard.

src/pages/signin.astro
---
import Layout from "../layouts/Layout.astro";
const { cookies, redirect } = Astro;
const accessToken = cookies.get("sb-access-token");
const refreshToken = cookies.get("sb-refresh-token");
if (accessToken && refreshToken) {
return redirect("/dashboard");
}
---
<Layout title="Accedi">
<h1>Accedi</h1>
<p>Nuovo qui? <a href="/register">Crea un account</a></p>
<form action="/api/auth/signin" method="post">
<label for="email">Email</label>
<input type="email" name="email" id="email" />
<label for="password">Password</label>
<input type="password" name="password" id="password" />
<button type="submit">Accedi</button>
</form>
</Layout>

dashboard.astro contiene una pagina accessibile solo agli utenti autenticati. Controlla la presenza dei token di accesso e aggiornamento. Se non sono presenti, reindirizza alla pagina di accesso.

src/pages/dashboard.astro
---
import Layout from "../layouts/Layout.astro";
import { supabase } from "../lib/supabase";
const { cookies, redirect } = Astro;
const accessToken = cookies.get("sb-access-token");
const refreshToken = cookies.get("sb-refresh-token");
if (!accessToken || !refreshToken) {
return redirect("/signin");
}
const { data, error } = await supabase.auth.setSession({
refresh_token: refreshToken.value,
access_token: accessToken.value,
});
if (error) {
cookies.delete("sb-access-token", {
path: "/",
});
cookies.delete("sb-refresh-token", {
path: "/",
});
return redirect("/signin");
}
const email = data.user?.email;
---
<Layout title="Dashboard">
<h1>Benvenuto {email}</h1>
<p>Siamo felici di vederti qui</p>
<form action="/api/auth/signout">
<button type="submit">Disconnettiti</button>
</form>
</Layout>

Per aggiungere l’autenticazione OAuth al tuo progetto, dovrai modificare il tuo client Supabase per abilitare il flusso di autenticazione con "pkce". Puoi leggere di più sui flussi di autenticazione nella documentazione di Supabase.

src/lib/supabase.ts
import { createClient } from "@supabase/supabase-js";
export const supabase = createClient(
import.meta.env.SUPABASE_URL,
import.meta.env.SUPABASE_ANON_KEY,
{
auth: {
flowType: "pkce",
},
},
);

Successivamente, nella dashboard di Supabase, abilita il provider OAuth che desideri utilizzare. Puoi trovare l’elenco dei provider supportati nella scheda Authentication > Providers del tuo progetto Supabase.

L’esempio seguente utilizza GitHub come provider OAuth. Per connettere il tuo progetto a GitHub, segui i passaggi nella documentazione di Supabase.

Quindi, crea un nuovo endpoint server per gestire il callback OAuth in src/pages/api/auth/callback.ts. Questo endpoint verrà utilizzato per scambiare il codice OAuth con un token di accesso e aggiornamento.

src/pages/api/auth/callback.ts
import type { APIRoute } from "astro";
import { supabase } from "../../../lib/supabase";
export const GET: APIRoute = async ({ url, cookies, redirect }) => {
const authCode = url.searchParams.get("code");
if (!authCode) {
return new Response("Nessun codice fornito", { status: 400 });
}
const { data, error } = await supabase.auth.exchangeCodeForSession(authCode);
if (error) {
return new Response(error.message, { status: 500 });
}
const { access_token, refresh_token } = data.session;
cookies.set("sb-access-token", access_token, {
path: "/",
});
cookies.set("sb-refresh-token", refresh_token, {
path: "/",
});
return redirect("/dashboard");
};

Successivamente, modifica la pagina di accesso per includere un nuovo pulsante per accedere con il provider OAuth. Questo pulsante dovrebbe inviare una richiesta POST a /api/auth/signin con il provider impostato sul nome del provider OAuth.

src/pages/signin.astro
---
import Layout from "../layouts/Layout.astro";
const { cookies, redirect } = Astro;
const accessToken = cookies.get("sb-access-token");
const refreshToken = cookies.get("sb-refresh-token");
if (accessToken && refreshToken) {
return redirect("/dashboard");
}
---
<Layout title="Accedi">
<h1>Accedi</h1>
<p>Nuovo qui? <a href="/register">Crea un account</a></p>
<form action="/api/auth/signin" method="post">
<label for="email">Email</label>
<input type="email" name="email" id="email" />
<label for="password">Password</label>
<input type="password" name="password" id="password" />
<button type="submit">Accedi</button>
<button value="github" name="provider" type="submit">Accedi con GitHub</button>
</form>
</Layout>

Infine, modifica l’endpoint server di accesso per gestire il provider OAuth. Se il provider è presente, reindirizzerà al provider OAuth. Altrimenti, accederà all’utente con l’email e la password.

src/pages/api/auth/signin.ts
import type { APIRoute } from "astro";
import { supabase } from "../../../lib/supabase";
import type { Provider } from "@supabase/supabase-js";
export const POST: APIRoute = async ({ request, cookies, redirect }) => {
const formData = await request.formData();
const email = formData.get("email")?.toString();
const password = formData.get("password")?.toString();
const provider = formData.get("provider")?.toString();
const validProviders = ["google", "github", "discord"];
if (provider && validProviders.includes(provider)) {
const { data, error } = await supabase.auth.signInWithOAuth({
provider: provider as Provider,
options: {
redirectTo: "http://localhost:4321/api/auth/callback"
},
});
if (error) {
return new Response(error.message, { status: 500 });
}
return redirect(data.url);
}
if (!email || !password) {
return new Response("Email e password sono obbligatorie", { status: 400 });
}
const { data, error } = await supabase.auth.signInWithPassword({
email,
password,
});
if (error) {
return new Response(error.message, { status: 500 });
}
const { access_token, refresh_token } = data.session;
cookies.set("sb-access-token", access_token, {
path: "/",
});
cookies.set("sb-refresh-token", refresh_token, {
path: "/",
});
return redirect("/dashboard");
};

Dopo aver creato l’endpoint di callback OAuth e modificato la pagina e l’endpoint server di accesso, il tuo progetto dovrebbe avere la seguente struttura di file:

  • Directorysrc/
    • Directorylib/
      • supabase.ts
    • Directorypages/
      • Directoryapi/
        • Directoryauth/
          • signin.ts
          • signout.ts
          • register.ts
          • callback.ts
      • register.astro
      • signin.astro
      • dashboard.astro
    • env.d.ts
  • .env
  • astro.config.mjs
  • package.json

Altre guide per servizi backend