Ir al contenido
angel.library

Supabase en Astro

Supabase es una base de datos Postgres gestionada con una capa de servicios encima: auth, storage y una API REST/realtime autogenerada sobre las tablas, todo desde un único client JS.

Ventana de terminal
pnpm add @supabase/supabase-js

Configuración rápida — de cero a un endpoint funcionando

Sección titulada «Configuración rápida — de cero a un endpoint funcionando»

1. Crear el proyecto en supabase.com y copiar URL + keys desde Project Settings → API.

2. Variables de entorno:

.env
SUPABASE_URL=https://tuproyecto.supabase.co
SUPABASE_SERVICE_ROLE_KEY=tu-service-role-key

3. El client:

src/libs/supabase.ts
import { createClient } from "@supabase/supabase-js"
export const supabaseAdmin = createClient(
import.meta.env.SUPABASE_URL,
import.meta.env.SUPABASE_SERVICE_ROLE_KEY
)

import.meta.env es la forma de leer variables de entorno en Astro — las que no llevan el prefijo PUBLIC_ solo son legibles del lado del servidor, que es justo donde debe vivir la service role key.

4. Un endpoint real:

src/pages/api/posts.ts
import type { APIRoute } from "astro"
import { supabaseAdmin } from "../../lib/supabase"
export const GET: APIRoute = async () => {
const { data, error } = await supabaseAdmin.from("posts").select("*")
if (error)
return new Response(JSON.stringify({ error: "Error al leer" }), {
status: 500
})
return new Response(JSON.stringify(data), {
headers: { "Content-Type": "application/json" }
})
}

Requiere output: 'server' en astro.config.mjs.

const { data, error } = await supabaseAdmin.from('posts').select('*').eq('published', true);
const { data, error } = await supabaseAdmin.from('posts').insert({ title: 'Nuevo', author_id: userId }).select().single();
const { data, error } = await supabaseAdmin.from('posts').update({ published: true }).eq('id', postId);
const { error } = await supabaseAdmin.from('posts').delete().eq('id', postId);

RLS son políticas SQL que deciden, fila por fila, qué puede leer/escribir cada usuario — el mecanismo que hace seguro exponer una key en un cliente.

create policy "Los usuarios ven sus propios posts"
on posts for select
using (auth.uid() = author_id);

Con la service role key (como aquí), RLS no aplica — el filtro queda a cargo del código del endpoint:

export const GET: APIRoute = async ({ locals }) => {
const { data, error } = await supabaseAdmin
.from("posts")
.select("*")
.eq("author_id", locals.user?.id) // filtro explícito
if (error)
return new Response(JSON.stringify({ error: "Error al leer" }), {
status: 500
})
return new Response(JSON.stringify(data), {
headers: { "Content-Type": "application/json" }
})
}

locals.user viene del middleware de auth (better-auth o Auth.js) — ver Backend en Astro.

Alternativa a better-auth/Auth.js si el proyecto ya está integrado con el ecosistema Supabase para todo:

const { data, error } = await supabaseAdmin.auth.signUp({ email, password })

Verificar la sesión en el middleware:

src/middleware.ts
import { defineMiddleware } from "astro:middleware"
import { supabaseAdmin } from "./lib/supabase"
export const onRequest = defineMiddleware(async (context, next) => {
const token = context.cookies.get("sb-access-token")?.value
if (token) {
const {
data: { user }
} = await supabaseAdmin.auth.getUser(token)
context.locals.user = user
}
return next()
})
const { data, error } = await supabaseAdmin.storage.from('avatars').upload('user-1.png', buffer);
const { data } = supabaseAdmin.storage.from('avatars').getPublicUrl('user-1.png');
APIQué hace
createClient(url, serviceRoleKey)Instancia el client del servidor
.from('tabla').select/insert/update/delete()CRUD vía la API REST autogenerada
{ data, error }Cada llamada devuelve esto, no lanza
RLSControla qué fila puede tocar cada usuario — no aplica con service role
supabase.auth.*Alternativa a better-auth/Auth.js si el proyecto ya usa Supabase para todo
  • Para la mayoría de los proyectos Astro con output: 'server', better-auth o Auth.js son la elección recomendada para auth — Supabase Auth tiene sentido cuando el proyecto ya está fuertemente integrado con el resto del ecosistema Supabase.
  • Con la service role key, la autorización queda en manos del código del endpoint — RLS no protege nada aquí.
  • No es mutuamente excluyente con Prisma — se puede combinar Prisma para queries complejas y Supabase solo para auth/storage/realtime.