Home Projects Blog About Contact
Download CV
Back to Blog

Despliega tus proyectos gratis con Supabase: la guía definitiva paso a paso

Aprende a desplegar tu backend completo de forma gratuita usando Supabase — PostgreSQL, auth, storage y APIs REST, todo sin pagar un euro.

Despliega tus proyectos gratis con Supabase: la guía definitiva paso a paso

Despliega tus proyectos gratis con Supabase: la guía definitiva paso a paso

Descripción: Aprende a desplegar tu backend completo de forma gratuita usando Supabase. Esta guía cubre desde la creación de tu cuenta hasta la integración con tu frontend, incluyendo base de datos PostgreSQL, autenticación, almacenamiento de archivos y APIs REST — todo sin pagar un solo euro.


Tabla de contenidos

  1. ¿Qué es Supabase?
  2. ¿Qué incluye el plan gratuito?
  3. Arquitectura general
  4. Paso 1: Crear tu cuenta y proyecto
  5. Paso 2: Configurar la base de datos
  6. Paso 3: Configurar autenticación
  7. Paso 4: Usar el almacenamiento de archivos
  8. Paso 5: Conectar tu frontend
  9. Paso 6: Variables de entorno y seguridad
  10. Paso 7: Row Level Security (RLS)
  11. Paso 8: Desplegar el frontend gratuitamente
  12. Flujo completo de despliegue
  13. Límites del plan gratuito y cómo gestionarlos
  14. Consejos finales

¿Qué es Supabase?

Supabase es una alternativa open source a Firebase construida sobre PostgreSQL. Te ofrece todo lo que necesitas para el backend de tu aplicación sin tener que configurar servidores:

  • 🗄️ Base de datos PostgreSQL real y completa
  • 🔐 Autenticación con email, contraseña, OAuth (Google, GitHub, etc.)
  • 📁 Almacenamiento de archivos (imágenes, documentos, vídeos)
  • API REST y GraphQL generadas automáticamente desde tu esquema
  • 🔄 Realtime — escucha cambios en la base de datos en tiempo real
  • ⚙️ Edge Functions — serverless functions en la nube

Lo mejor: tiene un plan gratuito generoso que es más que suficiente para proyectos personales, portfolios, MVPs y side projects.


¿Qué incluye el plan gratuito?

RecursoLímite gratuito
Proyectos activos2 proyectos
Base de datos (PostgreSQL)500 MB
Almacenamiento de archivos1 GB
Ancho de banda5 GB/mes
Edge Functions500.000 invocaciones/mes
Usuarios autenticadosIlimitados
API requestsIlimitados
Realtime connections200 conexiones simultáneas

⚠️ Importante: Los proyectos gratuitos se pausan automáticamente tras 1 semana de inactividad. Si alguien visita tu app, se reactivan en ~30 segundos, pero debes tenerlo en cuenta.


Arquitectura general

Antes de empezar, veamos cómo encajan todas las piezas:

graph TD
    A[Usuario / Navegador] -->|HTTPS| B[Frontend\nNetlify / Vercel / GitHub Pages]
    B -->|Supabase JS Client| C[Supabase API Gateway]
    C --> D[PostgreSQL\nBase de datos]
    C --> E[Auth Service\nJWT Tokens]
    C --> F[Storage\nArchivos S3-compatible]
    C --> G[Realtime\nWebSockets]
    C --> H[Edge Functions\nDeno]

    style A fill:#4A90D9,color:#fff
    style B fill:#50E3C2,color:#333
    style C fill:#F5A623,color:#fff
    style D fill:#7B68EE,color:#fff
    style E fill:#FF6B6B,color:#fff
    style F fill:#4ECDC4,color:#fff
    style G fill:#45B7D1,color:#fff
    style H fill:#96CEB4,color:#fff

Tu frontend (React, Vue, Next.js, etc.) se comunica directamente con Supabase usando su SDK oficial. No necesitas un servidor intermedio para la mayoría de operaciones.


Paso 1: Crear tu cuenta y proyecto

1.1 Regístrate en Supabase

  1. Ve a supabase.com
  2. Haz clic en “Start your project”
  3. Regístrate con tu cuenta de GitHub (recomendado) o con email

1.2 Crea tu primer proyecto

Una vez dentro del dashboard:

  1. Haz clic en “New project”
  2. Selecciona tu organización (o crea una nueva)
  3. Rellena los datos:
Nombre del proyecto:  mi-app-genial
Contraseña de BD:     [genera una contraseña fuerte y guárdala]
Región:               West EU (Ireland)  ← más cercana a España
Plan:                 Free
  1. Haz clic en “Create new project”

⏳ La creación tarda entre 1 y 2 minutos. Supabase está aprovisionando tu instancia de PostgreSQL.

1.3 Guarda tus credenciales

Nada más crear el proyecto, ve a Settings → API y anota:

  • Project URL → algo como https://xyzabc.supabase.co
  • anon public key → clave pública (segura para el frontend)
  • service_role keyNUNCA la expongas en el frontend

Paso 2: Configurar la base de datos

2.1 El editor SQL

Supabase incluye un editor SQL completo en el dashboard. Ve a Table Editor o SQL Editor para gestionar tu base de datos.

2.2 Crear tablas con el editor visual

Ve a Table Editor → New Table y crea tu primera tabla. Por ejemplo, una tabla tareas:

ColumnaTipoOpciones
iduuidPrimary key, default: gen_random_uuid()
created_attimestamptzdefault: now()
titulotextNot null
completadabooleandefault: false
user_iduuidFK → auth.users.id

2.3 O créala con SQL directamente

Ve a SQL Editor y ejecuta:

-- Crear tabla de tareas
CREATE TABLE public.tareas (
  id          uuid DEFAULT gen_random_uuid() PRIMARY KEY,
  created_at  timestamptz DEFAULT now() NOT NULL,
  titulo      text NOT NULL,
  descripcion text,
  completada  boolean DEFAULT false,
  user_id     uuid REFERENCES auth.users(id) ON DELETE CASCADE
);

-- Habilitar Row Level Security (imprescindible)
ALTER TABLE public.tareas ENABLE ROW LEVEL SECURITY;

2.4 La API REST se genera automáticamente

En cuanto creas una tabla, Supabase genera automáticamente una API REST sobre ella. Puedes consultarla en API Docs dentro del dashboard.

sequenceDiagram
    participant F as Frontend
    participant S as Supabase API
    participant DB as PostgreSQL

    F->>S: GET /rest/v1/tareas
    S->>DB: SELECT * FROM tareas WHERE (RLS policies)
    DB-->>S: Filas resultantes
    S-->>F: JSON con las tareas

    F->>S: POST /rest/v1/tareas
    S->>DB: INSERT INTO tareas (...)
    DB-->>S: Fila insertada
    S-->>F: 201 Created

Paso 3: Configurar autenticación

3.1 Habilitar proveedores de auth

Ve a Authentication → Providers. Por defecto tienes Email/Password activado.

Para añadir Google OAuth:

  1. Crea un proyecto en Google Cloud Console
  2. Ve a APIs & Services → Credentials → Create OAuth 2.0 Client
  3. En Authorized redirect URIs añade:
    https://TU_PROJECT_ID.supabase.co/auth/v1/callback
  4. Copia el Client ID y Client Secret en Supabase → Authentication → Providers → Google

3.2 Usar autenticación desde el frontend

Primero instala el SDK:

npm install @supabase/supabase-js

Crea un archivo src/lib/supabase.js:

import { createClient } from '@supabase/supabase-js'

const supabaseUrl = import.meta.env.VITE_SUPABASE_URL
const supabaseAnonKey = import.meta.env.VITE_SUPABASE_ANON_KEY

export const supabase = createClient(supabaseUrl, supabaseAnonKey)

Registro con email y contraseña

const { data, error } = await supabase.auth.signUp({
  email: 'jose@ejemplo.com',
  password: 'mi-contraseña-segura'
})

if (error) console.error('Error:', error.message)
else console.log('Usuario creado:', data.user)

Login

const { data, error } = await supabase.auth.signInWithPassword({
  email: 'jose@ejemplo.com',
  password: 'mi-contraseña-segura'
})

Login con Google

const { error } = await supabase.auth.signInWithOAuth({
  provider: 'google',
  options: {
    redirectTo: 'https://tu-app.netlify.app'
  }
})

Cerrar sesión

await supabase.auth.signOut()

Escuchar cambios de sesión

supabase.auth.onAuthStateChange((event, session) => {
  if (event === 'SIGNED_IN') {
    console.log('Usuario logueado:', session.user)
  }
  if (event === 'SIGNED_OUT') {
    console.log('Sesión cerrada')
  }
})

3.3 Flujo de autenticación

flowchart TD
    A[Usuario abre la app] --> B{¿Tiene sesión activa?}
    B -- Sí --> C[Dashboard / Contenido privado]
    B -- No --> D[Página de Login]
    D --> E{Método de login}
    E --> F[Email + Contraseña]
    E --> G[Google OAuth]
    E --> H[GitHub OAuth]
    F --> I[Supabase verifica credenciales]
    G --> J[Redirige a Google]
    H --> K[Redirige a GitHub]
    J --> I
    K --> I
    I --> L{¿Credenciales válidas?}
    L -- Sí --> M[Genera JWT Token]
    L -- No --> N[Error de autenticación]
    M --> C
    N --> D

Paso 4: Usar el almacenamiento de archivos

4.1 Crear un bucket

Ve a Storage → New bucket:

  • Nombre: avatares
  • Public: actívalo si las imágenes deben ser públicas (ej. fotos de perfil)
  • File size limit: por ejemplo, 5 MB
  • Allowed MIME types: image/jpeg, image/png, image/webp

4.2 Subir archivos desde el frontend

// Subir una imagen de perfil
const subirAvatar = async (file, userId) => {
  const fileExt = file.name.split('.').pop()
  const filePath = `${userId}/avatar.${fileExt}`

  const { data, error } = await supabase.storage
    .from('avatares')
    .upload(filePath, file, {
      cacheControl: '3600',
      upsert: true  // Sobreescribe si ya existe
    })

  if (error) throw error

  // Obtener URL pública
  const { data: urlData } = supabase.storage
    .from('avatares')
    .getPublicUrl(filePath)

  return urlData.publicUrl
}

4.3 Descargar y eliminar archivos

// Descargar
const { data, error } = await supabase.storage
  .from('avatares')
  .download(`${userId}/avatar.jpg`)

// Eliminar
const { error } = await supabase.storage
  .from('avatares')
  .remove([`${userId}/avatar.jpg`])

Paso 5: Conectar tu frontend

5.1 Operaciones CRUD sobre la base de datos

Con el cliente de Supabase puedes hacer consultas muy parecidas a SQL, pero en JavaScript:

Leer datos (SELECT)

// Obtener todas las tareas del usuario actual
const obtenerTareas = async () => {
  const { data, error } = await supabase
    .from('tareas')
    .select('*')
    .order('created_at', { ascending: false })

  if (error) throw error
  return data
}

// Con filtros
const tareasPendientes = await supabase
  .from('tareas')
  .select('id, titulo, created_at')
  .eq('completada', false)
  .limit(10)

Insertar datos (INSERT)

const crearTarea = async (titulo, descripcion) => {
  const { data, error } = await supabase
    .from('tareas')
    .insert({
      titulo,
      descripcion,
      user_id: (await supabase.auth.getUser()).data.user.id
    })
    .select()  // Devuelve el registro creado

  if (error) throw error
  return data[0]
}

Actualizar datos (UPDATE)

const completarTarea = async (tareaId) => {
  const { error } = await supabase
    .from('tareas')
    .update({ completada: true })
    .eq('id', tareaId)

  if (error) throw error
}

Eliminar datos (DELETE)

const eliminarTarea = async (tareaId) => {
  const { error } = await supabase
    .from('tareas')
    .delete()
    .eq('id', tareaId)

  if (error) throw error
}

5.2 Realtime — escuchar cambios en tiempo real

// Suscribirse a cambios en la tabla tareas
const canal = supabase
  .channel('cambios-tareas')
  .on(
    'postgres_changes',
    {
      event: '*',  // INSERT, UPDATE, DELETE
      schema: 'public',
      table: 'tareas'
    },
    (payload) => {
      console.log('Cambio detectado:', payload)
      // Aquí actualizas tu estado local
    }
  )
  .subscribe()

// Para dejar de escuchar
supabase.removeChannel(canal)

5.3 Ejemplo completo con React

import { useState, useEffect } from 'react'
import { supabase } from './lib/supabase'

export default function ListaTareas() {
  const [tareas, setTareas] = useState([])
  const [titulo, setTitulo] = useState('')

  useEffect(() => {
    cargarTareas()

    // Escuchar cambios en tiempo real
    const canal = supabase
      .channel('tareas')
      .on('postgres_changes', { event: '*', schema: 'public', table: 'tareas' },
        () => cargarTareas()
      )
      .subscribe()

    return () => supabase.removeChannel(canal)
  }, [])

  const cargarTareas = async () => {
    const { data } = await supabase
      .from('tareas')
      .select('*')
      .order('created_at', { ascending: false })
    setTareas(data || [])
  }

  const agregarTarea = async (e) => {
    e.preventDefault()
    if (!titulo.trim()) return

    await supabase.from('tareas').insert({ titulo })
    setTitulo('')
  }

  const toggleTarea = async (tarea) => {
    await supabase
      .from('tareas')
      .update({ completada: !tarea.completada })
      .eq('id', tarea.id)
  }

  return (
    <div>
      <form onSubmit={agregarTarea}>
        <input
          value={titulo}
          onChange={(e) => setTitulo(e.target.value)}
          placeholder="Nueva tarea..."
        />
        <button type="submit">Agregar</button>
      </form>

      <ul>
        {tareas.map((tarea) => (
          <li key={tarea.id}>
            <input
              type="checkbox"
              checked={tarea.completada}
              onChange={() => toggleTarea(tarea)}
            />
            {tarea.titulo}
          </li>
        ))}
      </ul>
    </div>
  )
}

Paso 6: Variables de entorno y seguridad

6.1 Configura tus variables de entorno

Crea un archivo .env.local en la raíz de tu proyecto:

VITE_SUPABASE_URL=https://xyzabc.supabase.co
VITE_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

⚠️ Añade .env.local a tu .gitignore. Nunca subas este archivo a GitHub.

6.2 ¿Cuál clave uso en el frontend?

graph LR
    A[Claves de Supabase] --> B[anon / public key]
    A --> C[service_role key]

    B --> D[✅ Frontend\nNavegador del usuario\nExponible con RLS activo]
    C --> E[🚫 NUNCA en el frontend\nSolo en servidor\nBypasea RLS]

    style D fill:#d4edda,color:#155724
    style E fill:#f8d7da,color:#721c24

La anon key es segura en el frontend porque:

  • Solo puede ejecutar operaciones que las políticas RLS permiten
  • No tiene acceso a datos de otros usuarios si las políticas están bien configuradas
  • Es pública por diseño (equivale a “no autenticado”)

Paso 7: Row Level Security (RLS)

RLS es la característica más importante de Supabase. Permite definir reglas a nivel de fila para controlar quién puede leer, crear, actualizar o eliminar cada registro.

7.1 ¿Por qué es imprescindible?

Sin RLS, cualquier usuario con tu anon key podría leer todos los datos de tu base de datos. Con RLS, cada usuario solo ve sus propios datos.

7.2 Habilitar RLS y crear políticas

-- Habilitar RLS en la tabla
ALTER TABLE public.tareas ENABLE ROW LEVEL SECURITY;

-- Política: los usuarios solo ven sus propias tareas
CREATE POLICY "Ver mis tareas"
ON public.tareas
FOR SELECT
USING (auth.uid() = user_id);

-- Política: los usuarios solo pueden crear tareas con su propio user_id
CREATE POLICY "Crear mis tareas"
ON public.tareas
FOR INSERT
WITH CHECK (auth.uid() = user_id);

-- Política: los usuarios solo pueden actualizar sus propias tareas
CREATE POLICY "Actualizar mis tareas"
ON public.tareas
FOR UPDATE
USING (auth.uid() = user_id);

-- Política: los usuarios solo pueden eliminar sus propias tareas
CREATE POLICY "Eliminar mis tareas"
ON public.tareas
FOR DELETE
USING (auth.uid() = user_id);

7.3 Política para datos públicos (ej. posts de un blog)

-- Cualquiera puede leer posts publicados
CREATE POLICY "Posts públicos visibles para todos"
ON public.posts
FOR SELECT
USING (publicado = true);

-- Solo el autor puede editar su post
CREATE POLICY "Solo el autor puede editar"
ON public.posts
FOR UPDATE
USING (auth.uid() = autor_id);

7.4 Cómo funcionan las políticas

flowchart TD
    A[Cliente hace una query] --> B{¿Está autenticado?}
    B -- No --> C[auth.uid devuelve NULL]
    B -- Sí --> D[auth.uid devuelve UUID del usuario]
    C --> E[RLS evalúa política con NULL]
    D --> E
    E --> F{¿La política se cumple\npara CADA fila?}
    F -- Sí --> G[✅ Fila incluida en resultado]
    F -- No --> H[❌ Fila excluida del resultado]
    G --> I[Respuesta al cliente]
    H --> I

Paso 8: Desplegar el frontend gratuitamente

Supabase gestiona tu backend. Para el frontend tienes varias opciones gratuitas excelentes:

Opción A: Vercel (recomendado para Next.js)

  1. Ve a vercel.com e inicia sesión con GitHub
  2. Haz clic en “Add New Project”
  3. Importa tu repositorio de GitHub
  4. Añade las variables de entorno:
    VITE_SUPABASE_URL = https://xyzabc.supabase.co
    VITE_SUPABASE_ANON_KEY = eyJ...
  5. Haz clic en Deploy

Opción B: Netlify (excelente para React/Vue/Svelte)

  1. Ve a netlify.com e inicia sesión con GitHub
  2. Haz clic en “Add new site → Import an existing project”
  3. Conecta tu repositorio
  4. Configura el build:
    Build command:   npm run build
    Publish directory: dist
  5. Ve a Site settings → Environment variables y añade tus variables de Supabase
  6. Haz clic en Deploy site

Opción C: GitHub Pages (estático puro)

Para apps con Vite, añade esto a tu vite.config.js:

export default defineConfig({
  base: '/nombre-repositorio/',
  // ...
})

Y crea un workflow en .github/workflows/deploy.yml:

name: Deploy to GitHub Pages

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
        with:
          node-version: 18
      - run: npm install
      - run: npm run build
        env:
          VITE_SUPABASE_URL: ${{ secrets.VITE_SUPABASE_URL }}
          VITE_SUPABASE_ANON_KEY: ${{ secrets.VITE_SUPABASE_ANON_KEY }}
      - uses: peaceiris/actions-gh-pages@v3
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: ./dist

💡 Para GitHub Pages, añade los secrets en Settings → Secrets → Actions de tu repositorio.


Flujo completo de despliegue

Así queda el flujo de principio a fin:

graph TD
    A[💻 Desarrollo local\nnpm run dev] -->|git push origin main| B[GitHub\nRepositorio]

    B -->|CI/CD automático| C{Plataforma de\ndespliegue}
    C --> D[Vercel]
    C --> E[Netlify]
    C --> F[GitHub Pages]

    D & E & F --> G[Frontend desplegado\nhttps://mi-app.vercel.app]

    G -->|SDK Supabase| H[Supabase Cloud\nPlan Gratuito]

    H --> I[(PostgreSQL\n500 MB gratis)]
    H --> J[🔐 Auth\nUsuarios ilimitados]
    H --> K[📁 Storage\n1 GB gratis)]
    H --> L[⚡ Realtime\nWebSockets]

    style A fill:#2d3748,color:#fff
    style B fill:#24292e,color:#fff
    style G fill:#00b894,color:#fff
    style H fill:#3ecf8e,color:#fff
    style I fill:#7B68EE,color:#fff
    style J fill:#FF6B6B,color:#fff
    style K fill:#4ECDC4,color:#fff
    style L fill:#45B7D1,color:#fff

Límites del plan gratuito y cómo gestionarlos

El problema de la pausa automática

Los proyectos gratuitos se pausan tras 7 días sin actividad. Esto puede dar mala experiencia si alguien visita tu app tras varios días inactivos (verá una carga lenta de ~30 segundos).

Soluciones:

  1. Cron job de keep-alive — usa un servicio como cron-job.org para hacer un ping a tu API cada 3 días:

    URL: https://xyzabc.supabase.co/rest/v1/
    Headers: apikey: TU_ANON_KEY
    Frecuencia: cada 3 días
  2. Mostrar un mensaje de carga — en tu frontend, si la primera query tarda mucho, muestra un mensaje amigable:

    // Detectar si el proyecto está "frío"
    const timeout = new Promise((_, reject) =>
      setTimeout(() => reject(new Error('Iniciando servidor...')), 5000)
    )
  3. Upgrade al plan Pro ($25/mes) si el proyecto lo requiere.

Gestionar los 500 MB de base de datos

-- Ver el tamaño actual de tu base de datos
SELECT pg_size_pretty(pg_database_size(current_database()));

-- Ver el tamaño de cada tabla
SELECT
  tablename,
  pg_size_pretty(pg_total_relation_size(tablename::regclass)) AS tamaño
FROM pg_tables
WHERE schemaname = 'public'
ORDER BY pg_total_relation_size(tablename::regclass) DESC;

Consejos para ahorrar espacio:

  • Usa tipos de datos eficientes (int en lugar de bigint cuando sea posible)
  • Limpia registros antiguos periódicamente con jobs programados
  • Almacena archivos en Storage (no en la BD como bytea)
  • Usa índices solo donde realmente los necesitas

Consejos finales

✅ Buenas prácticas

  • Siempre activa RLS antes de lanzar tu app en producción
  • Nunca expongas la service_role key en el frontend ni en GitHub
  • Usa migraciones SQL para gestionar cambios en el esquema (Supabase CLI tiene soporte para esto)
  • Haz backups manuales periódicos desde el dashboard en proyectos importantes
  • Usa el Dashboard de Logs para depurar problemas de auth o queries lentas

🚀 Para ir más allá (sin coste)

Si necesitas lógica de servidor (webhooks, cron jobs, integraciones), Supabase ofrece Edge Functions — funciones serverless escritas en TypeScript/Deno:

// supabase/functions/enviar-bienvenida/index.ts
import { serve } from 'https://deno.land/std@0.168.0/http/server.ts'

serve(async (req) => {
  const { email } = await req.json()

  // Lógica personalizada (enviar email, llamar a APIs externas, etc.)
  console.log(`Nuevo usuario: ${email}`)

  return new Response(JSON.stringify({ ok: true }), {
    headers: { 'Content-Type': 'application/json' }
  })
})

Para desplegarlas:

# Instalar Supabase CLI
npm install -g supabase

# Login
supabase login

# Desplegar función
supabase functions deploy enviar-bienvenida

Resumen

Con Supabase en el plan gratuito puedes desplegar proyectos reales con:

  • ✅ PostgreSQL completo con 500 MB
  • ✅ Autenticación con email y OAuth
  • ✅ Almacenamiento de hasta 1 GB
  • ✅ API REST generada automáticamente
  • ✅ Realtime con WebSockets
  • ✅ Edge Functions serverless
  • ✅ Frontend desplegado en Vercel/Netlify también gratis

El único costo real es tiempo de aprendizaje. Supabase es una de las herramientas más potentes disponibles hoy en día para construir y desplegar aplicaciones web modernas sin gastar un euro.