Saltar al contenido

Supabase y Astro

Supabase es una alternativa de código abierto a Firebase. Proporciona una base de datos Postgres, autenticación, edge functions, suscripciones en tiempo real y almacenamiento.

  • Un proyecto de Supabase. Si no tienes uno, puedes registrarte de forma gratuita en supabase.com y crear un nuevo proyecto.
  • Un proyecto de Astro con output: 'server' habilitado para renderizado bajo demanda.
  • Supabase credentials for your project. You can find these in the Settings > API tab of your Supabase project.
    • SUPABASE_URL: La URL de tu proyecto de Supabase.
    • SUPABASE_ANON_KEY: La clave anónima para tu proyecto de Supabase.

Para añadir tus credenciales de Supabase a tu proyecto de Astro, añade lo siguiente a tu archivo .env:

.env
SUPABASE_URL=YOUR_SUPABASE_URL
SUPABASE_ANON_KEY=YOUR_SUPABASE_ANON_KEY

Ahora, estas variables de entorno están disponibles en tu proyecto.

Si deseas tener IntelliSense para tus variables de entorno, edita o crea el archivo env.d.ts en tu directorio src/ y añade lo siguiente:

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

Tu proyecto ahora debería incluir estos archivos:

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

Para conectarte a Supabase, necesitarás instalar @supabase/supabase-js en tu proyecto.

Ventana de la terminal
npm install @supabase/supabase-js

A continuación, crea una carpeta llamada lib en tu directorio src/. Aquí es donde añadirás tu cliente de Supabase.

En supabase.ts, añade lo siguiente para inicializar tu cliente de 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,
);

Ahora, tu proyecto debería incluir estos archivos:

  • Directoriosrc/
    • Directoriolib/
      • supabase.ts
    • env.d.ts
  • .env
  • astro.config.mjs
  • package.json

Supabase proporciona autenticación de forma nativa. Admite autenticación de correo electrónico/contraseña y autenticación OAuth con muchos proveedores, incluyendo GitHub, Google y varios otros.

  • Un proyecto de Astro inicializado con Supabase.
  • Un proyecto de Supabase con autenticación de correo/contraseña habilitada. Puedes habilitarla en la pestaña Autenticación > Proveedores de tu proyecto de Supabase.

Para añadir autenticación a tu proyecto, necesitarás crear algunos endpoints de servidor. Estos endpoints se usarán para registrar, iniciar sesión y cerrar sesión de los usuarios.

  • POST /api/auth/register: para registrar un nuevo usuario.
  • POST /api/auth/signin: para iniciar la sesión de un usuario.
  • GET /api/auth/signout: para cerrar la sesión de un usuario.

Crea estos endpoints en el directorio src/pages/api/auth de tu proyecto. Si estás utilizando el modo de renderizado estático, debes especificar export const prerender = false en la parte superior de cada archivo para renderizar estos endpoints bajo demanda. Tu proyecto debería incluir ahora estos nuevos archivos:

  • Directoriosrc/
    • Directoriolib/
      • supabase.ts
    • Directoriopages/
      • Directorioapi/
        • Directorioauth/
          • signin.ts
          • signout.ts
          • register.ts
    • env.d.ts
  • .env
  • astro.config.mjs
  • package.json

register.ts crea un nuevo usuario en Supabase. Acepta una solicitud POST con un correo electrónico y una contraseña. Luego, utiliza el SDK de Supabase para crear un nuevo usuario.

src/pages/api/auth/register.ts
// With `output: 'static'` configured:
// export const prerender = false;
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 and password are required", { status: 400 });
}
const { error } = await supabase.auth.signUp({
email,
password,
});
if (error) {
return new Response(error.message, { status: 500 });
}
return redirect("/signin");
};

signin.ts inicia la sesión de un usuario. Acepta una solicitud POST con un correo electrónico y una contraseña. Luego, utiliza el SDK de Supabase para iniciar la sesión del usuario.

src/pages/api/auth/signin.ts
// With `output: 'static'` configured:
// export const prerender = false;
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 and password are required", { 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 cierra la sesión de un usuario. Acepta una solicitud GET y elimina los tokens de acceso y actualización del usuario.

src/pages/api/auth/signout.ts
// With `output: 'static'` configured:
// export const prerender = false;
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");
};

Ahora que has creado tus endpoints de servidor, crea las páginas que los usarán.

  • src/pages/register: contiene un formulario para registrar un nuevo usuario.
  • src/pages/signin: contiene un formulario para iniciar la sesión de un usuario.
  • src/pages/dashboard: contiene una página a la que solo pueden acceder los usuarios autenticados.

Crea estas páginas en el directorio src/pages. Tu proyecto debería incluir ahora estos nuevos archivos:

  • Directoriosrc/
    • Directoriolib/
      • supabase.ts
    • Directoriopages/
      • Directorioapi/
        • Directorioauth/
          • 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 formulario para registrar un nuevo usuario. Acepta un correo electrónico y una contraseña, y envía una solicitud POST a /api/auth/register.

src/pages/register.astro
---
import Layout from "../layouts/Layout.astro";
---
<Layout title="Register">
<h1>Register</h1>
<p>Already have an account? <a href="/signin">Sign in</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">Register</button>
</form>
</Layout>

signin.astro contiene un formulario para iniciar la sesión de un usuario. Acepta un correo electrónico y una contraseña, y envía una solicitud POST a /api/auth/signin. También comprueba la presencia de los tokens de acceso y de actualización. Si están presentes, redirige al panel de control (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="Sign in">
<h1>Sign in</h1>
<p>New here? <a href="/register">Create an 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">Login</button>
</form>
</Layout>

dashboard.astro contiene una página a la que solo pueden acceder los usuarios autenticados. Comprueba la presencia de los tokens de acceso y de actualización. Si no están presentes o no son válidos, redirige a la página de inicio de sesión.

src/pages/dashboard.astro
---
import Layout from "../layouts/Layout.astro";
import { supabase } from "../lib/supabase";
const accessToken = Astro.cookies.get("sb-access-token");
const refreshToken = Astro.cookies.get("sb-refresh-token");
if (!accessToken || !refreshToken) {
return Astro.redirect("/signin");
}
let session;
try {
session = await supabase.auth.setSession({
refresh_token: refreshToken.value,
access_token: accessToken.value,
});
if (session.error) {
Astro.cookies.delete("sb-access-token", {
path: "/",
});
Astro.cookies.delete("sb-refresh-token", {
path: "/",
});
return Astro.redirect("/signin");
}
} catch (error) {
Astro.cookies.delete("sb-access-token", {
path: "/",
});
Astro.cookies.delete("sb-refresh-token", {
path: "/",
});
return Astro.redirect("/signin");
}
const email = session.data.user?.email;
---
<Layout title="dashboard">
<h1>Welcome {email}</h1>
<p>We are happy to see you here</p>
<form action="/api/auth/signout">
<button type="submit">Sign out</button>
</form>
</Layout>

Para añadir autenticación OAuth a tu proyecto, necesitarás editar tu cliente de Supabase para habilitar el flujo de autenticación con "pkce". Puedes leer más sobre los flujos de autenticación en la documentación de 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",
},
},
);

A continuación, en el panel de control de Supabase, habilita el proveedor de OAuth que te gustaría usar. Puedes encontrar la lista de proveedores compatibles en la pestaña Autenticación > Proveedores de tu proyecto de Supabase.

El siguiente ejemplo utiliza GitHub como proveedor de OAuth. Para conectar tu proyecto a GitHub, sigue los pasos de la documentación de Supabase.

Luego, crea un nuevo endpoint de servidor para manejar la devolución de llamada (callback) de OAuth en src/pages/api/auth/callback.ts. Este endpoint se usará para intercambiar el código OAuth por un token de acceso y otro de actualización.

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("No code provided", { 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");
};

A continuación, edita la página de inicio de sesión para incluir un nuevo botón para iniciar sesión con el proveedor de OAuth. Este botón debe enviar una solicitud POST a /api/auth/signin con el provider establecido con el nombre del proveedor de 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="Sign in">
<h1>Sign in</h1>
<p>New here? <a href="/register">Create an 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">Login</button>
<button value="github" name="provider" type="submit">Sign in with GitHub</button>
</form>
</Layout>

Finalmente, edita el endpoint del servidor de inicio de sesión para manejar el proveedor de OAuth. Si el provider está presente, redirigirá al proveedor de OAuth. De lo contrario, iniciará la sesión del usuario con el correo electrónico y la contraseña.

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 and password are required", { 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");
};

Después de crear el endpoint de devolución de llamada (callback) de OAuth y editar la página de inicio de sesión y el endpoint del servidor, tu proyecto debería tener la siguiente estructura de archivos:

  • Directoriosrc/
    • Directoriolib/
      • supabase.ts
    • Directoriopages/
      • Directorioapi/
        • Directorioauth/
          • signin.ts
          • signout.ts
          • register.ts
          • callback.ts
      • register.astro
      • signin.astro
      • dashboard.astro
    • env.d.ts
  • .env
  • astro.config.mjs
  • package.json

Más guías de servicios de backend

Contribuir Comunidad Patrocinar