5/10/20267 min de lectura

Arquitectura Full-Stack de Alto Rendimiento: Next.js 15 App Router, Supabase RLS y Docker

Guía arquitectónica para construir plataformas web modernas de alto impacto combinando Server Components, Server Actions tipadas, políticas de seguridad a nivel de fila (RLS) en PostgreSQL y contenedores Docker multi-stage.

#Next.js#TypeScript#Supabase#Docker#Arquitectura
Arquitectura Full-Stack de Alto Rendimiento: Next.js 15 App Router, Supabase RLS y Docker

El Paradigma Moderno del Desarrollo Web

El desarrollo web ha evolucionado desde las arquitecturas SPA con pesadas capas de empaquetado en el cliente hacia modelos híbridos impulsados por el servidor. Con la llegada de Next.js 15 y React Server Components (RSC), es posible renderizar la mayor parte de la interfaz en el servidor sin despachar megabytes de JavaScript innecesarios al navegador.

Al combinar este frontend con Supabase (PostgreSQL administrado con autenticación y almacenamiento) y un despliegue contenerizado mediante Docker, se obtiene un stack productivo, seguro y fácilmente escalable.


1. Separación de Responsabilidades: RSC y Server Actions

En la arquitectura del App Router de Next.js 15:

  • Server Components (Default): Se ejecutan exclusivamente en el servidor. Tienen acceso directo a la base de datos o APIs privadas y entregan HTML hidratado con cero impacto en el bundle del cliente.
  • Client Components (`"use client"`): Reservados para nodos de la vista con interactividad (modales, formularios con estado en vivo, oyentes de eventos del DOM).
  • Server Actions (`"use server"`): Funciones asíncronas tipadas que reemplazan los tradicionales endpoints REST POST/PUT/DELETE, integrándose nativamente con la revalidación de caché de Next.js (revalidatePath).
TYPESCRIPT
// src/features/blog/actions/blog-actions.ts
"use server";

import { revalidatePath } from "next/cache";
import { createServerClient } from "@/shared/lib/supabase/server";
import { blogPostSchema } from "../schemas/blog.schema";

export async function createBlogPost(prevState: any, formData: FormData) {
  // Validación estricta con Zod en el servidor
  const rawData = Object.fromEntries(formData.entries());
  const parsed = blogPostSchema.safeParse(rawData);

  if (!parsed.success) {
    return { error: parsed.error.issues[0].message };
  }

  const supabase = await createServerClient();
  const { error } = await supabase.from("blog_posts").insert(parsed.data);

  if (error) {
    return { error: error.message };
  }

  // Purga selectiva de la caché de Next.js
  revalidatePath("/blog");
  revalidatePath("/admin/blog");

  return { success: true };
}

2. Seguridad en la Capa de Datos: PostgreSQL Row Level Security (RLS)

Delegar la seguridad exclusivamente en la capa de aplicación (APIs en Node.js) suele derivar en vulnerabilidades si un endpoint omite una comprobación de permisos. Con Row Level Security (RLS) de PostgreSQL, la autorización se define a nivel de motor de base de datos:

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

-- Política 1: Lectura pública solo para artículos marcados como publicados
CREATE POLICY "Public can view published posts"
ON public.blog_posts
FOR SELECT
USING (published = true);

-- Política 2: Solo administradores autenticados pueden insertar o modificar
CREATE POLICY "Admins can manage all posts"
ON public.blog_posts
FOR ALL
TO authenticated
USING (auth.jwt() ->> 'email' = 'tu-email-admin@dominio.com')
WITH CHECK (auth.jwt() ->> 'email' = 'tu-email-admin@dominio.com');

Con estas dos políticas, cualquier cliente anónimo que intente consultar artículos en borrador recibirá un resultado vacío directo del motor de base de datos, garantizando aislamiento total.


3. Despliegue Contenerizado con Docker Multi-Stage

Para producción, un contenedor no debe incluir dependencias de desarrollo ni herramientas del compilador. El patrón Multi-Stage divide la construcción en etapas aisladas:

DOCKERFILE
# Etapa 1: Instalación de dependencias
FROM node:20-alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci --prefer-offline

# Etapa 2: Compilación de Next.js
FROM node:20-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
ENV NEXT_TELEMETRY_DISABLED=1
RUN npm run build

# Etapa 3: Runner de Producción ultra-ligero
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1

RUN addgroup --system --gid 1001 nodejs && \
    adduser --system --uid 1001 nextjs

# Solo copiamos el artefacto standalone y recursos estáticos
COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static

USER nextjs
EXPOSE 3000
CMD ["node", "server.js"]

Ventajas de esta configuración:

  1. Reducción del tamaño de imagen: Pasa de ~1.2 GB a menos de 140 MB.
  2. Superficie de ataque mínima: Al ejecutarse con un usuario no root (nextjs), se reducen riesgos de escalamiento de privilegios.
  3. Inmutabilidad y portabilidad: El mismo contenedor probado localmente con docker-compose es el que corre en servidores cloud o VPS.

Conclusiones

Construir software moderno requiere equilibrio entre agilidad y solidez arquitectónica. La convergencia de Next.js 15 para la experiencia de usuario y rendimiento web, Supabase RLS para la integridad de datos y Docker para operaciones reproducibles conforma una base de grado industrial lista para proyectos de escala empresarial.