Prisma com TypeScript: Guia Completo 2026
Defina schemas, use tipos gerados automaticamente pelo Prisma Client, faça CRUD tipado e configure relações entre modelos. Tudo com exemplos reais.
Por que isso é importante
Prisma com TypeScript: Guia Completo 2026. Defina schemas, use tipos gerados automaticamente pelo Prisma Client, faça CRUD tipado e configure relações entre modelos. Tudo com exemplos reais.
O Que É Prisma e Por Que Combina com TypeScript
Prisma é um ORM moderno pra Node.js e TypeScript. Diferente de ORMs tradicionais que te fazem criar classes e decorators, Prisma usa um arquivo de schema próprio (schema.prisma) pra definir seus modelos. A partir desse schema, ele gera um client com tipos perfeitos.
O pulo do gato é a geração automática de tipos. Quando você roda npx prisma generate, o Prisma lê seu schema e cria tipos TypeScript pra cada modelo, cada campo, cada relação. Se você tem um modelo User com name e email, o Prisma cria a interface User com exatamente esses campos.
Isso muda tudo. Em vez de escrever SQL e torcer pra não ter typo no nome da coluna, você escreve prisma.user.findMany() e o editor já mostra os campos disponíveis, os filtros possíveis, os includes de relação. Se você muda o schema, roda generate e o compilador aponta tudo que quebrou. Simples assim.
Prisma suporta PostgreSQL, MySQL, SQLite, SQL Server e MongoDB. O schema é o mesmo pra todos. Você troca de banco mudando uma linha de configuração.
Como Configurar Prisma com TypeScript Passo a Passo
Vamos do zero até ter um Prisma Client tipado rodando queries.
Exemplos Práticos: Prisma com TypeScript
Vamos ver código real. Schema, client, queries e relações.
Schema Prisma com Modelos e Relações
// prisma/schema.prisma
generator 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
role Role @default(USER)
posts Post[]
profile Profile?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model Post {
id String @id @default(cuid())
title String
content String?
published Boolean @default(false)
author User @relation(fields: [authorId], references: [id])
authorId String
tags Tag[]
createdAt DateTime @default(now())
}
model Profile {
id String @id @default(cuid())
bio String?
avatar String?
user User @relation(fields: [userId], references: [id])
userId String @unique
}
model Tag {
id String @id @default(cuid())
name String @unique
posts Post[]
}
enum Role {
USER
ADMIN
EDITOR
}
Instância Única do Prisma Client
// src/lib/prisma.ts
import { PrismaClient } from '@prisma/client';
// Evita múltiplas instâncias em desenvolvimento (hot reload)
const globalForPrisma = globalThis as unknown as {
prisma: PrismaClient | undefined;
};
export const prisma =
globalForPrisma.prisma ??
new PrismaClient({
log: ['query', 'error', 'warn'],
});
if (process.env.NODE_ENV !== 'production') {
globalForPrisma.prisma = prisma;
}
CRUD Completo com Tipos Gerados
import { prisma } from './lib/prisma';
import { User, Post, Prisma } from '@prisma/client';
// CREATE - tipos gerados garantem campos corretos
async function createUser(data: Prisma.UserCreateInput): Promise<User> {
return prisma.user.create({ data });
// data precisa ter: email (string), name (string)
// data pode ter: role (Role), posts, profile
}
// Usando:
await createUser({
email: 'maria@email.com',
name: 'Maria Silva',
role: 'ADMIN', // autocomplete mostra: USER, ADMIN, EDITOR
});
// READ - com filtros tipados
async function findUsers(where?: Prisma.UserWhereInput): Promise<User[]> {
return prisma.user.findMany({
where,
orderBy: { createdAt: 'desc' },
});
}
// Filtros com autocomplete:
await findUsers({
role: 'ADMIN',
email: { contains: '@empresa.com' },
createdAt: { gte: new Date('2025-01-01') },
});
// UPDATE - só campos do modelo
async function updateUser(
id: string,
data: Prisma.UserUpdateInput
): Promise<User> {
return prisma.user.update({ where: { id }, data });
}
// DELETE
async function deleteUser(id: string): Promise<User> {
return prisma.user.delete({ where: { id } });
}
Relações e Includes Tipados
// Include traz dados de relações com tipos corretos
async function getUserWithPosts(id: string) {
const user = await prisma.user.findUnique({
where: { id },
include: {
posts: {
where: { published: true },
orderBy: { createdAt: 'desc' },
include: { tags: true },
},
profile: true,
},
});
// user.posts é Post[] (com tags: Tag[])
// user.profile é Profile | null
return user;
}
// Select pra trazer só campos específicos
async function getUserEmails() {
const users = await prisma.user.findMany({
select: {
id: true,
email: true,
name: true,
// posts: false -- nao precisa explicitar
},
});
// Tipo retornado: { id: string; email: string; name: string }[]
// Sem campos extras. Tipo exato do que você pediu.
return users;
}
// Criar com relação
async function createPostWithTags() {
return prisma.post.create({
data: {
title: 'Meu Post',
content: 'Conteudo aqui',
author: { connect: { email: 'maria@email.com' } },
tags: {
connectOrCreate: [
{ where: { name: 'TypeScript' }, create: { name: 'TypeScript' } },
{ where: { name: 'Prisma' }, create: { name: 'Prisma' } },
],
},
},
include: { author: true, tags: true },
});
}
Usando Tipos Gerados do Prisma
import { Prisma, User, Post, Role } from '@prisma/client';
// Prisma gera tipos pra tudo:
// - User, Post, Profile, Tag = tipos dos modelos
// - Role = enum
// - Prisma.UserCreateInput = tipo do data pra create
// - Prisma.UserUpdateInput = tipo do data pra update
// - Prisma.UserWhereInput = tipo dos filtros
// - Prisma.UserSelect = tipo do select
// - Prisma.UserInclude = tipo do include
// Tipo de User com relações incluídas
type UserWithPosts = Prisma.UserGetPayload<{
include: { posts: true; profile: true };
}>;
function renderUserProfile(user: UserWithPosts) {
console.log(user.name);
console.log(user.posts.length); // posts é Post[]
console.log(user.profile?.bio); // profile é Profile | null
}
// Tipo derivado com select específico
type UserSummary = Prisma.UserGetPayload<{
select: { id: true; name: true; email: true };
}>;
function renderUserCard(user: UserSummary) {
// Só tem id, name, email. Nada mais.
console.log(`${user.name} - ${user.email}`);
}
Prisma.UserGetPayload é uma das features mais poderosas. Ele gera o tipo exato do retorno baseado nos includes e selects que você passou. Nada de tipo genérico User que não reflete o que a query realmente retorna.
Erros Comuns ao Usar Prisma com TypeScript
Armadilhas que todo dev encontra
Esquecer de rodar prisma generate: sempre que mudar o schema.prisma, rode npx prisma generate. Sem isso, os tipos ficam desatualizados e o autocomplete mostra campos que não existem mais.
Criar múltiplas instâncias do PrismaClient: em desenvolvimento com hot reload, cada restart cria uma nova conexão. Use o pattern do globalForPrisma pra reaproveitar a instância.
Confundir include com select: include traz todos os campos do modelo mais as relações. select traz só os campos que você especificar. Não use os dois juntos no mesmo nível.
Não tratar erros do Prisma: Prisma lança erros específicos como PrismaClientKnownRequestError com codes (P2002 pra unique violation, P2025 pra record not found). Capture e trate com try/catch tipado.
Ignorar os tipos de input gerados: Use Prisma.UserCreateInput e Prisma.UserUpdateInput. Não crie interfaces manuais que duplicam o schema. Se o schema muda, os tipos gerados atualizam sozinhos.
Checklist de Prisma com TypeScript
Domine Prisma num Projeto Real
Prisma com TypeScript é a stack mais produtiva pra backend Node.js hoje. No CrazyStack, você constrói um SaaS completo usando Prisma, Express e TypeScript com autenticação, relações complexas, migrations e deploy. Não é tutorial isolado: é um projeto real que vai pro ar.
Se você quer parar de escrever SQL na mão e ter um banco de dados tipado de ponta a ponta, esse é o próximo passo.
Continue lendo
Como Criar CRUD com Node.js e TypeScript
Construa um CRUD completo com Express, TypeScript e camadas de service e controller.
Como Criar API REST com Node.js e TypeScript
Construa uma API REST completa com Node.js e TypeScript do zero ao deploy.
Como Tipar Mongoose com TypeScript
Domine schemas e modelos Mongoose com tipagem forte no TypeScript.
Interface no TypeScript
Quando usar interface