Acciones del servidor de Next.js con Prisma y PostgreSQL
Next.js Server Actions se comunica directamente con un cliente Prisma compartido y PostgreSQL, por lo que las operaciones CRUD de tareas funcionan sin rutas API ni llamadas fetch del lado del cliente.
Fuentes: Next.js Server Actions + Prisma + PostgreSQL: The Integration Pattern You Actually Need
Con frecuencia, las guías explican Next.js, Prisma y PostgreSQL como instalaciones separadas: se crea una estructura básica para la aplicación, se genera un cliente, se establece conexión con una base de datos y se ejecutan migraciones. Lo que falta es cómo estos tres componentes encajan juntos como un único patrón de integración, y por qué ese patrón elimina gran parte de los procedimientos habituales en un desarrollo full-stack.
La división del trabajo es clara. Server Actions transfieren tareas más allá de los límites del servidor. Prisma genera consultas seguras desde el punto de vista tipológico. PostgreSQL almacena las filas de datos. Cada capa se encarga de una tarea específica. Juntos, eliminan toda una capa intermedia: no hay rutas API personalizadas, ni ciclos fetch escritos a mano, ni procesos adicionales de serialización entre un componente React y la base de datos.
Bastaría con un pequeño gestor de tareas para mostrar este patrón de principio a fin, desde el esquema hasta una interfaz de usuario funcional, sin necesidad de implementar controladores de rutas para operaciones CRUD.
Por qué esta combinación
Las acciones del servidor, Prisma y PostgreSQL se refuerzan mutuamente en cada capa de una aplicación full-stack de Next.js.
Las acciones se ejecutan en el servidor, por lo que pueden importar directamente al cliente de Prisma sin exponer las credenciales de la base de datos al navegador. Prisma proporciona una interfaz de consultas tipada, de modo que los valores escritos en PostgreSQL y los valores leídos permanecen alineados en todo el grafo de TypeScript. PostgreSQL es fiable, cuenta con amplio soporte y funciona perfectamente con el flujo de trabajo de migraciones de Prisma.
El resultado es un camino corto desde el clic en un botón hasta la creación de una fila persistida, generalmente con menos código adicional que otros enfoques en el ecosistema de Next.js.
Configuración del proyecto
Comience con una nueva aplicación de Next.js e incorpore Prisma:
npx create-next-app@latest my-app --typescript
cd my-app
npm install prisma @prisma/client
npx prisma init
The prisma init command creates a prisma/ folder with a schema.prisma file and a .env file. Open .env and add your PostgreSQL connection string:
```DATABASE_URL="postgresql://USER:PASSWORD@HOST:5432/mydb"
3. Define Your Schema
Open prisma/schema.prisma and define your data model. We will keep it simple — a Task model:
```generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model Task {
id String @id @default(cuid())
title String
completed Boolean @default(false)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
Run the migration to create the table:
```npx prisma migrate dev --name init
This creates the table and generates the Prisma Client. You are ready to query.
El patrón de integración
Esta es la idea central. Una Acción de Servidor importa el cliente Prisma y se comunica directamente con la base de datos: sin rutas API, sin llamadas fetch del cliente, y sin ninguna capa adicional de serialización entre la interfaz de usuario y el almacenamiento. Primero, exporta una instancia compartida de Prisma que permanezca activa durante los recargues en tiempo real durante el desarrollo:
// lib/prisma.ts
import { PrismaClient } from '@prisma/client';
const globalForPrisma = globalThis as unknown as { prisma: PrismaClient };
export const prisma =
globalForPrisma.prisma ?? new PrismaClient();
if (process.env.NODE_ENV !== 'production')
globalForPrisma.prisma = prisma;
Luego define el módulo de Acciones de Servidor:
// app/tasks/actions.ts
'use server';
import { revalidatePath } from 'next/cache';
import { prisma } from '@/lib/prisma';
export async function getTasks() {
return prisma.task.findMany({ orderBy: { createdAt: 'desc' } });
}
5. Wiring It to Your UI
Now use the Server Actions in your page and components:
```// app/tasks/page.tsx
import { getTasks } from './actions';
import { TaskForm } from './TaskForm';
import { TaskList } from './TaskList';
export default async function TasksPage() {
const tasks = await getTasks();
return (
<main>
<h1>My Tasks</h1>
<TaskForm />
<TaskList tasks={tasks} />
</main>
);
}
export async function createTask(formData: FormData) {
const title = formData.get('title') as string;
if (!title?.trim()) return { error: 'Title is required' };
await prisma.task.create({ data: { title } });
revalidatePath('/tasks');
}
export async function toggleTask(id: string, completed: boolean) {
await prisma.task.update({ where: { id }, data: { completed: !completed } });
revalidatePath('/tasks');
}
export async function deleteTask(id: string) {
await prisma.task.delete({ where: { id } });
revalidatePath('/tasks');
}
Vincularlo a la interfaz de usuario
Llama a esas acciones desde la página. El componente del servidor puede invocar directamente getTasks(): sin useEffect, sin indicador de carga manual, y sin llamadas fetch del lado del cliente:
// app/tasks/page.tsx
import { getTasks } from './actions';
import { TaskForm } from './TaskForm';
import { TaskList } from './TaskList';
export default async function TasksPage() {
const tasks = await getTasks();
return (
<main>
<h1>My Tasks</h1>
<TaskForm />
<TaskList tasks={tasks} />
</main>
);
}
Conclusión
Server Actions junto con Prisma y PostgreSQL te brindan una solución de datos full-stack casi sin código genérico. Un componente llama a una función, esa función escribe o lee mediante Prisma, PostgreSQL almacena los datos, y revalidatePath actualiza la interfaz de usuario; generalmente se logra con unas pocas decenas de líneas de código en lugar de necesitar una capa API separada.