Busca en todas las páginas de la documentación
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥
npm install prisma @prisma/client
npx prisma init// prisma/schema.prisma
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model User {
id Int @id @default(autoincrement())
email String @unique
name String?
posts Post[]
createdAt DateTime @default(now())
}
model Post {
id Int @id @default(autoincrement())
title String
content String?
published Boolean @default(false)
author User @relation(fields: [authorId], references: [id])
authorId Int
createdAt DateTime @default(now())
}npx prisma migrate dev --name init
npx prisma generate// 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;Cuándo usarlo: Necesitas consultas de base de datos con seguridad de tipos en una aplicación Next.js con tipos generados automáticamente, manejo de relaciones y migraciones de esquema.
// app/posts/page.tsx (Server Component)
import { prisma } from "@/lib/prisma";
export default async function PostsPage() {
const posts = await prisma.post.findMany({
where: { published: true },
include: { author: { select: { name: true, email: true } } },
orderBy: { createdAt: "desc" },
take: 20,
});
return (
<div className="max-w-3xl mx-auto p-6">
<h1 className="text-2xl font-bold mb-6">Publicaciones publicadas</h1>
{posts.map((post) => (
<article key={post.id} className="border-b py-4">
<h2 className="text-xl font-semibold">{post.title}</h2>
<p className="text-gray-600 text-sm">
por {post.author.name ?? "Anónimo"}
</p>
{post.content && <p className="mt-2">{post.content}</p>}
</article>
))}
</div>
);
}// app/posts/actions.ts
"use server";
import { prisma } from "@/lib/prisma";
import { revalidatePath } from "next/cache";
export async function createPost(formData: FormData) {
const title = formData.get("title") as string;
const content = formData.get("content") as string;
const authorId = Number(formData.get("authorId"));
await prisma.post.create({
data: { title, content, authorId, published: false },
});
revalidatePath("/posts");
}
export async function publishPost(postId: number) {
await prisma.post.update({
where: { id: postId },
data: { published: true },
});
revalidatePath("/posts");
}
export async function deletePost(postId: number) {
await prisma.post.delete({ where: { id: postId } });
revalidatePath("/posts");
}// app/posts/new/page.tsx
"use client";
import { createPost } from "../actions";
export default function NewPostPage() {
return (
<form action={createPost} className="max-w-md mx-auto p-6 space-y-4">
<input type="hidden" name="authorId" value="1" />
<div>
<label className="block text-sm font-medium">Título</label>
<input
name="title"
required
className="w-full border rounded px-3 py-2"
/>
</div>
<div>
<label className="block text-sm font-medium">Contenido</label>
<textarea
name="content"
rows={5}
className="w-full border rounded px-3 py-2"
/>
</div>
<button
type="submit"
className="bg-blue-600 text-white px-4 py-2 rounded"
>
Crear publicación
</button>
</form>
);
}Lo que esto demuestra:
revalidatePathschema.prisma, proporcionando campos de modelo autocompletados, recorrido de relaciones y filtros de consultanode_modules/.prisma/client y se regenera en prisma generate o prisma migrate devinclude y select controlan qué datos relacionados se obtienenprisma/migrations/, rastreados por una tabla _prisma_migrations en tu base de datosglobalForPrisma) previene que hot reload de Next.js cree nuevas instancias de PrismaClient, lo que agoraría las conexiones de base de datosFiltrado y paginación:
const results = await prisma.post.findMany({
where: {
AND: [
{ published: true },
{ title: { contains: searchQuery, mode: "insensitive" } },
],
},
skip: (page - 1) * pageSize,
take: pageSize,
orderBy: { createdAt: "desc" },
});
const total = await prisma.post.count({
where: { published: true },
});Transacciones:
const [post, user] = await prisma.$transaction([
prisma.post.create({ data: { title: "Hello", authorId: 1 } }),
prisma.user.update({
where: { id: 1 },
data: { name: "Updated Name" },
}),
]);
// Interactive transaction
await prisma.$transaction(async (tx) => {
const user = await tx.user.findUnique({ where: { id: 1 } });
if (!user) throw new Error("User not found");
await tx.post.create({ data: { title: "Hello", authorId: user.id } });
});Upsert (crear o actualizar):
const user = await prisma.user.upsert({
where: { email: "alice@example.com" },
update: { name: "Alice Updated" },
create: { email: "alice@example.com", name: "Alice" },
});SQL sin procesar para consultas complejas:
const results = await prisma.$queryRaw<
{ id: number; title: string }[]
>`SELECT id, title FROM "Post" WHERE "published" = true LIMIT ${limit}`;User, Post, etc.include y select - no se necesitan definiciones de tipos manualesPrisma.PostCreateInput para tipos de datos de creación, Prisma.PostWhereInput para filtrosPrisma.PostGetPayload<{ include: { author: true } }> te da el tipo de retorno exacto para una consulta con incluyesimport { Prisma } from "@prisma/client";
type PostWithAuthor = Prisma.PostGetPayload<{
include: { author: true };
}>;
function renderPost(post: PostWithAuthor) {
return `${post.title} by ${post.author.name}`;
}Agotamiento de conexiones en desarrollo - hot reload de Next.js crea nuevas instancias de PrismaClient. Solución: Usa el patrón singleton mostrado en la sección Receta. Almacena el cliente en globalThis.
Cliente generado obsoleto - Después de cambiar schema.prisma, las consultas pueden no reflejar campos nuevos. Solución: Ejecuta npx prisma generate después de cambios de esquema. prisma migrate dev lo hace automáticamente.
Consultas N+1 - Acceder a relaciones en un bucle sin include dispara una consulta por iteración. Solución: Usa include o select para cargar relaciones con entusiasmo en la consulta inicial.
Serialización de BigInt - Los campos BigInt no se pueden serializar a JSON para componentes cliente. Solución: Convierte BigInt a Number o String antes de pasar a componentes cliente: Number(post.viewCount).
Los cambios de enum requieren migración - Agregar valores a una enumeración de Prisma requiere una migración, no solo prisma generate. Solución: Ejecuta npx prisma migrate dev --name add-enum-value.
Confusión de zona horaria de DateTime - Prisma almacena DateTime como UTC. Solución: Siempre maneja la conversión de zona horaria en el lado del cliente, no en consultas.
| Biblioteca | Mejor para | Compensación |
|---|---|---|
| Prisma | ORM con seguridad de tipos con migraciones | Runtime más pesado, cliente generado |
| Drizzle ORM | Ligero, sintaxis similar a SQL | Menos abstracción, migraciones manuales |
| Kysely | Generador de consultas con seguridad de tipos | Sin gestión de esquema o migraciones |
| TypeORM | ORM basado en decoradores | Menos seguridad de tipos, más pesado |
| Knex.js | Generador de consultas SQL sin procesar | Sin generación de tipos, tipos manuales |
PrismaClient, abriendo conexiones de base de datos nuevasglobalThis asegura que solo una instancia persista a través de hot reloadsinclude obtiene todos los campos escalares del modelo padre más las relaciones especificadasselect obtiene solo los campos que explícitamente enumeraste, incluidas relacionesinclude como select al mismo nivel superiorselect cuando quieras minimizar los datos devueltosimport { prisma } from "@/lib/prisma";
export default async function Page() {
const users = await prisma.user.findMany();
return <ul>{users.map(u => <li key={u.id}>{u.name}</li>)}</ul>;
}No se necesita capa API -- Server Components se ejecutan en el servidor y pueden consultar la base de datos directamente.
prisma.$transaction([query1, query2]) ejecuta consultas en ordenprisma.$transaction(async (tx) => { ... }) te permite usar resultados intermediosnode_modules/.prisma/clientschema.prisma solo no regenera el clientenpx prisma generate después de cada cambio de esquemanpx prisma migrate dev ejecuta generate automáticamenteconst page = 2;
const pageSize = 10;
const [posts, total] = await Promise.all([
prisma.post.findMany({
skip: (page - 1) * pageSize,
take: pageSize,
orderBy: { createdAt: "desc" },
}),
prisma.post.count(),
]);BigInt no se pueden serializar a JSONNumber o String antes de pasar: Number(post.viewCount)Number.MAX_SAFE_INTEGER, usa String() en su lugarimport { Prisma } from "@prisma/client";
type PostWithAuthor = Prisma.PostGetPayload<{
include: { author: true };
}>;Prisma.PostGetPayload infiere la forma exacta basándose en include/selectimport { Prisma } from "@prisma/client";
const data: Prisma.PostCreateInput = {
title: "Hello",
author: { connect: { id: 1 } },
};*CreateInput, *UpdateInput, *WhereInput, y *OrderByInput para cada modelomigrate dev crea un archivo de migración SQL, lo aplica y regenera el clientedb push aplica cambios de esquema directamente sin crear archivos de migraciónmigrate dev para flujos de trabajo de producción donde necesitas historial de migracióndb push para prototipos rápidos o cuando no necesitas un rastro de migraciónconst results = await prisma.$queryRaw<
{ id: number; title: string }[]
>`SELECT id, title FROM "Post" WHERE published = true`;post.author en un bucle sin include dispara una consulta por publicacióninclude: { author: true } en el findMany inicial para cargar relaciones con entusiasmoselect para obtener solo los campos específicos de autor que necesitasRevisado por Chris St. John·Última actualización: 16 jul 2026
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥