Next.js + Prisma + PostgreSQL
Uma configuração full-stack Next.js 15 com Prisma ORM, banco de dados PostgreSQL e Server Actions para mutações.
Busque em todas as páginas da documentação
Uma configuração full-stack Next.js 15 com Prisma ORM, banco de dados PostgreSQL e Server Actions para mutações.
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥
npx create-next-app@latest my-app --typescript --tailwind --app
cd my-appnpm install prisma @prisma/client
npm install -D prismanpx prisma init --datasource-provider postgresqlprisma/schema.prisma.DATABASE_URL em .env.npx prisma migrate dev --name initlib/prisma.ts para evitar esgotar conexões durante o hot-reload.Um aplicativo Todo completo com Prisma + PostgreSQL + Server Actions.
prisma/schema.prismagenerator client \{
provider = "prisma-client-js"
\}
datasource db \{
provider = "postgresql"
url = env("DATABASE_URL")
\}
model Todo \{
id String @id @default(cuid())
title String
completed Boolean @default(false)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
\}lib/prisma.ts (singleton)import \{ PrismaClient \} from "@prisma/client";
const globalForPrisma = globalThis as unknown as \{
prisma: PrismaClient | undefined;
\};
export const prisma =
globalForPrisma.prisma ??
new PrismaClient(\{
log: process.env.NODE_ENV === "development" ? ["query", "error", "warn"] : ["error"],
\});
if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = prisma;app/page.tsx (Server Component)import \{ prisma \} from "@/lib/prisma";
import \{ addTodo \} from "./actions";
export default async function Home() \{
const todos = await prisma.todo.findMany(\{
orderBy: \{ createdAt: "desc" \},
\});
return (
<main className="mx-auto max-w-xl p-8">
<h1 className="text-2xl font-bold">Todos</h1>
<form action=\{addTodo\} className="mt-4 flex gap-2">
<input
name="title"
required
className="flex-1 rounded border px-3 py-2"
placeholder="New todo..."
/>
<button type="submit" className="rounded bg-black px-4 py-2 text-white">
Add
</button>
</form>
<ul className="mt-6 space-y-2">
\{todos.map((todo) => (
<li key=\{todo.id\} className="rounded border p-3">
\{todo.title\}
</li>
))\}
</ul>
</main>
);
\}app/actions.ts (Server Action)"use server";
import \{ prisma \} from "@/lib/prisma";
import \{ revalidatePath \} from "next/cache";
export async function addTodo(formData: FormData) \{
const title = formData.get("title");
if (typeof title !== "string" || title.trim() === "") return;
await prisma.todo.create(\{
data: \{ title: title.trim() \},
\});
revalidatePath("/");
\}O Prisma gera um cliente totalmente tipado com base no seu schema. O Server Component do Next.js é executado no servidor e consulta o Postgres diretamente através do cliente Prisma. Server Actions são endpoints POST criados automaticamente pelo Next.js - envios de formulário invocam a ação, que modifica o banco de dados e chama revalidatePath para limpar o render em cache, de modo que os novos dados apareçam na próxima solicitação.
O padrão singleton é crítico em desenvolvimento. O hot-reload do Next.js reavalia os módulos a cada alteração e, sem um singleton, você criaria um PrismaClient novo a cada recarregamento, esgotando rapidamente o pool de conexões do banco de dados.
DATABASE_URL.provider = "sqlite" e use url = "file:./dev.db" para um banco de dados local sem configuração.prisma/seed.ts e registre-o em "prisma": \{ "seed": "tsx prisma/seed.ts" \} no package.json, depois execute npx prisma db seed.npx prisma studio para um navegador de dados e editor baseado em navegador.prisma://, ideal para implantações serverless.import \{ Todo \} from "@prisma/client".Prisma.TodoGetPayload<\{ include: \{ author: true \} \}> para derivar um tipo exato da forma retornada.(formData: FormData) => Promise<void> ou usar o padrão mais novo useActionState com um argumento prevState.tsconfig.json para capturar campos anuláveis - o Prisma respeita ? no schema como T | null em TS.globalThis em lib/prisma.ts, o modo de desenvolvimento do Next.js vazará conexões até que o Postgres recuse novas.DATABASE_URL ausente. O Prisma nem sequer gerará sem ele. Coloque-o em .env (não apenas .env.local) porque a CLI do Prisma lê .env.prisma generate. Após alterar schema.prisma, regenere o cliente ou seus tipos ficarão desatualizados. prisma migrate dev faz isso automaticamente, mas prisma db push e edições manuais do schema podem não fazer.prisma migrate deploy em CI/CD, nunca prisma migrate dev - este último pode solicitar interativamente e descartar dados.binaryTargets = ["native", "rhel-openssl-3.0.x"] ao bloco do gerador, ou sua implantação falhará com "Query engine binary not found."| Ferramenta | Estilo | Ideal para |
|---|---|---|
| Prisma | ORM com DSL de schema | ORM tipado completo, migrações, Studio |
| Drizzle ORM | Construtor de SQL tipado | Leve, amigável ao edge, mais próximo do SQL |
| Kysely | Construtor de consulta tipado | Equipes que querem SQL-first com inferência TS |
| postgres.js | Driver SQL bruto | Controle máximo, sem sobrecarga de abstração |
| Supabase client | SDK REST/Realtime | Auth + DB + realtime em um pacote |
O modo de desenvolvimento do Next.js recarrega módulos em alterações de arquivo. Cada recarregamento cria uma nova instância PrismaClient se você simplesmente new PrismaClient() no escopo do módulo, e cada instância abre seu próprio pool de conexões. Em poucos minutos, você atinge o limite de conexões do seu banco de dados. O singleton globalThis sobrevive ao hot-reload porque vive no objeto global, não no módulo.
migrate dev gera arquivos de migração SQL em prisma/migrations/ que você commita no controle de versão - é o fluxo de trabalho seguro para produção. db push sincroniza o schema diretamente sem criar arquivos de migração - use-o apenas para prototipagem rápida.
Não com o cliente padrão. Você precisa do Prisma Accelerate (@prisma/extension-accelerate) ou dos Driver Adapters (atualmente em preview) que roteiam consultas através de um proxy baseado em fetch em vez de TCP do Node.
Execute npx prisma migrate deploy como parte da sua etapa de build ou release. Isso aplica quaisquer migrações pendentes de prisma/migrations/ sem solicitar e sem gerar novas.
Crie prisma/seed.ts, adicione "prisma": \{ "seed": "tsx prisma/seed.ts" \} ao package.json, depois execute npx prisma db seed. Ele também é executado automaticamente após prisma migrate reset.
Adicione os binaryTargets corretos em schema.prisma:
generator client \{
provider = "prisma-client-js"
binaryTargets = ["native", "rhel-openssl-3.0.x"]
\}Em seguida, commite, redeploy e certifique-se de que prisma generate seja executado no seu build ("postinstall": "prisma generate" em package.json é a correção padrão).
Execute npx prisma generate manualmente. O servidor TS do seu editor também pode precisar ser reiniciado. prisma migrate dev normalmente executa generate para você, mas db push e edições diretas do schema sem migração não o fazem.
Use Prisma.<Model>GetPayload:
import \{ Prisma \} from "@prisma/client";
type TodoWithAuthor = Prisma.TodoGetPayload<\{
include: \{ author: true \};
\}>;Isso lhe dá um tipo preciso que corresponde à forma exata que findMany(\{ include: \{ author: true \} \}) retorna.
Uma ação simples recebe FormData e retorna Promise<void>:
export async function addTodo(formData: FormData): Promise<void> \{ /* ... */ \}Para useActionState você adiciona um parâmetro prevState:
export async function addTodo(prevState: State, formData: FormData): Promise<State> \{ /* ... */ \}Não. O Prisma só roda no lado do servidor. Chame-o de Server Components, Server Actions, Route Handlers ou generateStaticParams. Se um Client Component precisar de dados, passe-os como props ou busque através de uma Server Action/Route Handler.
revalidatePath("/") diz ao Next.js para descartar o render em cache para esse caminho. Na próxima solicitação, o Server Component é reexecutado, consulta o Prisma novamente e obtém os dados atualizados. Sem isso, os usuários veem dados desatualizados até que o cache expire.
Prisma tem melhor DX, Studio e migrações. Drizzle é mais leve, nativo para edge e oferece sintaxe semelhante a SQL com tipos fortes. Escolha Prisma para a maioria dos aplicativos full-stack; escolha Drizzle se você se preocupa com o tamanho do bundle, compatibilidade com edge ou deseja permanecer próximo ao SQL.
Revisado por Chris St. John·Última atualização: 16 de jul. de 2026
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥