Schema único, type-safety, migrations, Prisma Studio. Por qué Prisma + Postgres son el stack de DB con menor fricción para SaaS indie.
El stack "PostgreSQL + Prisma" se ha consolidado como el default para SaaS indie en 2026. No porque sea la única opción (Drizzle, Kysely, raw SQL existen), sino porque la combinación de type-safety + migrations + DX es difícil de igualar.
Esta guía explica por qué este combo gana, qué patrones funcionan y qué errores evitar.
schema.prisma y de ahí salen tipos TS, cliente,
migrations.migrate dev, lista.PostgreSQL aporta:
@db.JsonB) para campos flexiblesgenerator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model User {
id String @id @default(cuid())
email String @unique
name String?
createdAt DateTime @default(now())
posts Post[]
}
model Post {
id String @id @default(cuid())
title String
content String
authorId String
author User @relation(fields: [authorId], references: [id])
createdAt DateTime @default(now())
@@index([authorId, createdAt])
}Schema corto, todo lo necesario para arrancar.
Mal:
// Cada vez que importas, instancias un PrismaClient nuevo
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();En desarrollo, hot-reload abre nuevas conexiones cada vez. Acabas saturando la DB.
Bien:
// src/lib/db/client.ts
import { PrismaClient } from '@/generated/prisma/client';
declare global {
var prisma: PrismaClient | undefined;
}
export const db = globalThis.prisma ?? new PrismaClient();
if (process.env.NODE_ENV !== 'production') {
globalThis.prisma = db;
}Y en cualquier parte: import { db } from '@/lib/db/client'.
bunx prisma migrate dev --name add-billing-tableCrea archivo SQL, lo aplica a tu DB local, regenera el cliente. Lo commiteas con el código.
En producción:
bunx prisma migrate deployAplica las migraciones pendientes. No genera SQL nuevo, solo aplica lo que ya está en
prisma/migrations/.
Si haces la misma query en 3+ sitios, sácala a un archivo:
// src/lib/db/queries/users.ts
import { db } from '@/lib/db/client';
export async function getUserById(id: string) {
return db.user.findUnique({
where: { id },
include: { posts: { orderBy: { createdAt: 'desc' } } },
});
}Esto te da:
Desde Prisma 7 (2025), el cliente requiere un adapter explícito:
import { PrismaPg } from '@prisma/adapter-pg';
import { PrismaClient } from '@/generated/prisma/client';
const adapter = new PrismaPg({ connectionString: process.env.DATABASE_URL! });
const prisma = new PrismaClient({ adapter });Detalle pequeño pero crítico: si te lo saltas, los scripts standalone (bun script.ts) explotan con
error de inicialización.
1. Olvidar índices en columnas filtradas: si haces where: { userId }, mete índice. Sin él, en
producción con 100k filas, la query tarda segundos.
2. findMany sin paginación: si la tabla puede crecer, paginar (take, skip, o cursor).
Devolver 50k filas a un endpoint mata el server.
3. Hacer findUnique con campos no únicos: solo funciona con campos @unique. Para otros,
findFirst.
4. Cargar relaciones que no usas: include: { posts: true } siempre que no las necesites es
trabajo de DB que tiras a la basura.
Para el 95% de SaaS indie, Prisma es el default razonable.
Prisma + PostgreSQL en 2026 es el stack de DB con menor fricción para SaaS indie. Un schema, un cliente, type-safety, migraciones. Todo lo demás es ruido.
Si arrancas hoy, empieza aquí. Cuando crezcas y necesites algo más afilado, ya tendrás los datos para decidir.
Suscríbete para más tutoriales y tips sobre crear productos con IA
El 25 abr 2026 MinIO archivó su community edition. Migramos producción de Click2Eat y yamltools.dev a Garage self-host (S3-compatible, AGPLv3). Cuatro escollos no documentados, el patrón S3 API compartido + CDN dedicado por producto, y checklist de migración completo.