O Prisma é um ORM (Object-Relational Mapping) moderno para Node.js e TypeScript que simplifica o acesso a bancos de dados. Ele oferece type safety, auto-completion e uma API intuitiva para trabalhar com dados.
# Criar projeto Next.js com TypeScript
npx create-next-app@latest meu-projeto --typescript
# Navegar para o diretório
cd meu-projeto
# Instalar Prisma
npm install prisma @prisma/client
### Instalação de Drivers por Banco de Dados
#### PostgreSQL
```bash
npm install pg @types/pg
npm install mysql2
npm install tedious
# Não precisa instalar driver adicional
### 2. Inicializando o Prisma
```bash
npx prisma init
Este comando cria:
prisma/schema.prisma (esquema do banco).env (variáveis de ambiente)prisma/schema.prisma// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql" // ou "mysql", "sqlserver", "sqlite"
url = env("DATABASE_URL")
}
model User {
id Int @id @default(autoincrement())
email String @unique
name String?
posts Post[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
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())
updatedAt DateTime @updatedAt
}
No arquivo .env:
# Database
DATABASE_URL="postgresql://usuario:senha@localhost:5432/meudb?schema=public"
# Database
DATABASE_URL="mysql://usuario:senha@localhost:3306/meudb"
# Database
DATABASE_URL="sqlserver://localhost:1433;database=meudb;user=usuario;password=senha;encrypt=true;trustServerCertificate=true"
# Database
DATABASE_URL="file:./dev.db"
# Criar uma migration
npx prisma migrate dev --name init
# Gerar o cliente Prisma
npx prisma generate
O Prisma tem 3 componentes principais:
schema.prisma) - O “blueprint” do seu bancoSchema.prisma → Migrations → Banco de Dados
↓
Generate → Cliente TypeScript → Sua Aplicação
prisma/schema.prisma)É o arquivo onde você descreve como quer que seu banco seja estruturado:
model User {
id Int @id @default(autoincrement())
email String @unique
name String?
posts Post[]
}
Analogia: É como uma “planta baixa” de uma casa - mostra como você quer que seja, mas ainda não construiu nada.
São as instruções SQL que transformam seu banco do estado atual para o estado desejado:
-- Migration: 001_create_users_table
CREATE TABLE "User" (
"id" SERIAL NOT NULL,
"email" TEXT NOT NULL,
"name" TEXT,
PRIMARY KEY ("id")
);
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");
Analogia: É como o “passo a passo de construção” - as instruções reais para o pedreiro construir a casa.
É o código TypeScript que permite você usar o banco na sua aplicação:
// Código gerado automaticamente
export const prisma = new PrismaClient()
// Tipos gerados automaticamente
export type User = {
id: number
email: string
name: string | null
}
Analogia: É como ter um “manual de uso” da casa pronta - te ensina como usar cada cômodo.
# 1. Edita schema.prisma
# 2. Cria migration E aplica E gera cliente (tudo junto)
npx prisma migrate dev --name add_age_field
# Resultado: banco atualizado + cliente atualizado
# 1. Aplica migrations pendentes no banco
npx prisma migrate deploy
# 2. Atualiza cliente TypeScript
npx prisma generate
# 3. Reinicia aplicação
npm run build && pm2 restart app
MIGRATION trabalha com o BANCO DE DADOS:
GENERATE trabalha com o CÓDIGO TYPESCRIPT:
lib/prisma.ts)import { PrismaClient } from '@prisma/client'
const globalForPrisma = globalThis as unknown as {
prisma: PrismaClient | undefined
}
export const prisma = globalForPrisma.prisma ?? new PrismaClient()
if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma
Esta configuração evita múltiplas instâncias do Prisma em desenvolvimento.
pages/api/users/index.ts (para listar e criar usuários):import { NextApiRequest, NextApiResponse } from 'next'
import { prisma } from '../../../lib/prisma'
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
if (req.method === 'GET') {
try {
const users = await prisma.user.findMany({
include: {
posts: true, // Incluir posts relacionados
},
})
res.status(200).json(users)
} catch (error) {
res.status(500).json({ error: 'Erro ao buscar usuários' })
}
}
if (req.method === 'POST') {
try {
const { email, name } = req.body
const user = await prisma.user.create({
data: {
email,
name,
},
})
res.status(201).json(user)
} catch (error) {
res.status(500).json({ error: 'Erro ao criar usuário' })
}
}
}
pages/api/users/[id].ts (para operações específicas por ID):import { NextApiRequest, NextApiResponse } from 'next'
import { prisma } from '../../../lib/prisma'
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const { id } = req.query
if (req.method === 'GET') {
try {
const user = await prisma.user.findUnique({
where: { id: Number(id) },
include: { posts: true },
})
if (!user) {
return res.status(404).json({ error: 'Usuário não encontrado' })
}
res.status(200).json(user)
} catch (error) {
res.status(500).json({ error: 'Erro ao buscar usuário' })
}
}
if (req.method === 'PUT') {
try {
const { email, name } = req.body
const user = await prisma.user.update({
where: { id: Number(id) },
data: { email, name },
})
res.status(200).json(user)
} catch (error) {
res.status(500).json({ error: 'Erro ao atualizar usuário' })
}
}
if (req.method === 'DELETE') {
try {
await prisma.user.delete({
where: { id: Number(id) },
})
res.status(204).end()
} catch (error) {
res.status(500).json({ error: 'Erro ao deletar usuário' })
}
}
}
components/UserList.tsx:import { useState, useEffect } from 'react'
interface User {
id: number
email: string
name: string | null
posts: Post[]
}
interface Post {
id: number
title: string
content: string | null
published: boolean
}
export default function UserList() {
const [users, setUsers] = useState<User[]>([])
const [loading, setLoading] = useState(true)
useEffect(() => {
fetchUsers()
}, [])
const fetchUsers = async () => {
try {
const response = await fetch('/api/users')
const data = await response.json()
setUsers(data)
} catch (error) {
console.error('Erro ao carregar usuários:', error)
} finally {
setLoading(false)
}
}
if (loading) return <div>Carregando...</div>
return (
<div>
<h1>Lista de Usuários</h1>
{users.map((user) => (
<div key={user.id} className="user-card">
<h3>{user.name || 'Sem nome'}</h3>
<p>Email: {user.email}</p>
<p>Posts: {user.posts.length}</p>
</div>
))}
</div>
)
}
# Atualizar sistema (Ubuntu/Debian)
sudo apt update && sudo apt upgrade -y
# Instalar Node.js (versão LTS)
curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash -
sudo apt-get install -y nodejs
# Verificar instalação
node --version
npm --version
# Clonar repositório
git clone https://github.com/seu-usuario/seu-projeto.git
cd seu-projeto
# Instalar dependências de produção
npm ci --only=production
# Instalar Prisma CLI globalmente (opcional)
npm install -g prisma
# Criar arquivo de ambiente para produção
cp .env.example .env.production
# Editar variáveis (use editor de sua preferência)
nano .env.production
Exemplo do .env.production:
NODE_ENV=production
DATABASE_URL="postgresql://user:password@localhost:5432/proddb"
NEXTAUTH_SECRET="seu-secret-super-seguro"
NEXTAUTH_URL="https://seudominio.com"
# Criar banco de dados
sudo -u postgres createdb proddb
sudo -u postgres psql -c "CREATE USER userapp WITH PASSWORD 'senha123';"
sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE proddb TO userapp;"
# Ou usar Docker
docker run --name postgres-prod -e POSTGRES_DB=proddb -e POSTGRES_USER=userapp -e POSTGRES_PASSWORD=senha123 -p 5432:5432 -d postgres:15
# Criar banco de dados
mysql -u root -p -e "CREATE DATABASE proddb;"
mysql -u root -p -e "CREATE USER 'userapp'@'%' IDENTIFIED BY 'senha123';"
mysql -u root -p -e "GRANT ALL PRIVILEGES ON proddb.* TO 'userapp'@'%';"
mysql -u root -p -e "FLUSH PRIVILEGES;"
# Ou usar Docker
docker run --name mysql-prod -e MYSQL_DATABASE=proddb -e MYSQL_USER=userapp -e MYSQL_PASSWORD=senha123 -e MYSQL_ROOT_PASSWORD=rootpass -p 3306:3306 -d mysql:8
# Usar Docker (recomendado)
docker run --name mssql-prod -e "ACCEPT_EULA=Y" -e "SA_PASSWORD=SuaSenhaForte123!" -p 1433:1433 -d mcr.microsoft.com/mssql/server:2019-latest
# Conectar e criar banco
docker exec -it mssql-prod /opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P "SuaSenhaForte123!"
> CREATE DATABASE proddb;
> GO
> EXIT
# Gerar cliente Prisma
npx prisma generate
# Executar migrations em produção (primeira vez)
npx prisma migrate deploy
# Verificar status das migrations
npx prisma migrate status
# (Opcional) Popular banco com dados iniciais
npx prisma db seed
# Build da aplicação Next.js
npm run build
# Iniciar aplicação em produção
npm start
# Ou usar PM2 para gerenciamento de processos
npm install -g pm2
pm2 start npm --name "minha-app" -- start
pm2 save
pm2 startup
##### PostgreSQL
```bash
# Backup do banco PostgreSQL
pg_dump -h localhost -U userapp -d proddb > backup_$(date +%Y%m%d_%H%M%S).sql
# Ou usando Docker
docker exec postgres-prod pg_dump -U userapp proddb > backup_$(date +%Y%m%d_%H%M%S).sql
# Backup do banco MySQL
mysqldump -h localhost -u userapp -p proddb > backup_$(date +%Y%m%d_%H%M%S).sql
# Ou usando Docker
docker exec mysql-prod mysqldump -u userapp -p proddb > backup_$(date +%Y%m%d_%H%M%S).sql
# Backup usando Docker
docker exec -it mssql-prod /opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P "SuaSenhaForte123!" -Q "BACKUP DATABASE proddb TO DISK = '/var/opt/mssql/backup_$(date +%Y%m%d_%H%M%S).bak'"
# Copiar backup para host
docker cp mssql-prod:/var/opt/mssql/backup_$(date +%Y%m%d_%H%M%S).bak ./
tar -czf uploads_backup_$(date +%Y%m%d_%H%M%S).tar.gz uploads/
#### Passo 2: Atualização do Código
```bash
# Parar aplicação
pm2 stop minha-app
# Fazer backup do código atual
cp -r /caminho/para/aplicacao /caminho/para/aplicacao_backup_$(date +%Y%m%d)
# Atualizar código
git fetch origin
git pull origin main
# Ou substituir arquivos se não usar Git
# rsync -av /caminho/nova-versao/ /caminho/aplicacao/
# Atualizar dependências (cuidado com breaking changes)
npm ci --only=production
# Ou atualização conservadora
npm install --production
# Verificar status das migrations pendentes
npx prisma migrate status
# Visualizar diferenças do schema (desenvolvimento)
npx prisma migrate diff --from-url $DATABASE_URL --to-schema-datamodel prisma/schema.prisma
# Executar migrations pendentes
npx prisma migrate deploy
# Regenerar cliente Prisma (importante após mudanças no schema)
npx prisma generate
# Novo build da aplicação
npm run build
# Restart da aplicação
pm2 restart minha-app
# Verificar logs
pm2 logs minha-app --lines 50
# Verificar status
pm2 status
# Testar conectividade do banco
npx prisma db pull --print
# Verificar saúde da aplicação
curl -I http://localhost:3000/api/health
# Monitorar logs em tempo real
pm2 logs minha-app --follow
// Buscar usuários com SQL direto
const users = await prisma.$queryRaw`
SELECT * FROM "User"
WHERE "email" LIKE ${'%@gmail.com'}
ORDER BY "createdAt" DESC
`
// Buscar com parâmetros tipados
const usersByDomain = await prisma.$queryRaw<User[]>`
SELECT u.*, COUNT(p.id) as post_count
FROM "User" u
LEFT JOIN "Post" p ON u.id = p."authorId"
WHERE u.email LIKE ${`%${domain}`}
GROUP BY u.id
`
// Query SQL sem retorno tipado
const result = await prisma.$executeRaw`
UPDATE "User"
SET "updatedAt" = NOW()
WHERE "lastLogin" < NOW() - INTERVAL '30 days'
`
console.log(`${result} usuários atualizados`)
// Query complexa com subquery
const activeUsers = await prisma.$queryRaw`
SELECT u.*,
(SELECT COUNT(*) FROM "Post" p WHERE p."authorId" = u.id AND p.published = true) as published_posts
FROM "User" u
WHERE u.id IN (
SELECT DISTINCT p."authorId"
FROM "Post" p
WHERE p."createdAt" > NOW() - INTERVAL '7 days'
)
`
// Insert simples com SQL
const result = await prisma.$executeRaw`
INSERT INTO "User" (email, name, "createdAt", "updatedAt")
VALUES (${'novo@email.com'}, ${'Novo Usuário'}, NOW(), NOW())
`
// Insert em lote com SQL
const batchInsert = await prisma.$executeRaw`
INSERT INTO "Post" (title, content, "authorId", published, "createdAt", "updatedAt")
VALUES
(${'Post 1'}, ${'Conteúdo 1'}, ${1}, ${true}, NOW(), NOW()),
(${'Post 2'}, ${'Conteúdo 2'}, ${1}, ${false}, NOW(), NOW()),
(${'Post 3'}, ${'Conteúdo 3'}, ${2}, ${true}, NOW(), NOW())
`
// Insert com retorno do ID (PostgreSQL)
const newUser = await prisma.$queryRaw<{id: number}[]>`
INSERT INTO "User" (email, name, "createdAt", "updatedAt")
VALUES (${'user@test.com'}, ${'Test User'}, NOW(), NOW())
RETURNING id
`
// Insert condicional (upsert com SQL)
const upsertUser = await prisma.$executeRaw`
INSERT INTO "User" (email, name, "createdAt", "updatedAt")
VALUES (${'user@example.com'}, ${'João Silva'}, NOW(), NOW())
ON CONFLICT (email)
DO UPDATE SET
name = EXCLUDED.name,
"updatedAt" = NOW()
`
// Usar aspas duplas para nomes de tabelas/colunas
const users = await prisma.$queryRaw`
SELECT * FROM "User" WHERE "createdAt" > ${new Date('2024-01-01')}
`
// RETURNING funciona
const newUser = await prisma.$queryRaw`
INSERT INTO "User" (email, name, "createdAt", "updatedAt")
VALUES (${'test@test.com'}, ${'Test'}, NOW(), NOW())
RETURNING id, email
`
// Usar backticks para nomes de tabelas/colunas
const users = await prisma.$queryRaw`
SELECT * FROM \`User\` WHERE \`createdAt\` > ${new Date('2024-01-01')}
`
// Para auto-increment, usar LAST_INSERT_ID()
await prisma.$executeRaw`
INSERT INTO \`User\` (email, name, createdAt, updatedAt)
VALUES (${'test@test.com'}, ${'Test'}, NOW(), NOW())
`
const lastId = await prisma.$queryRaw<{id: number}[]>`
SELECT LAST_INSERT_ID() as id
`
// Usar colchetes para nomes de tabelas/colunas
const users = await prisma.$queryRaw`
SELECT * FROM [User] WHERE [createdAt] > ${new Date('2024-01-01')}
`
// Usar OUTPUT para retorno
const newUser = await prisma.$queryRaw`
INSERT INTO [User] (email, name, createdAt, updatedAt)
OUTPUT INSERTED.id, INSERTED.email
VALUES (${'test@test.com'}, ${'Test'}, GETDATE(), GETDATE())
`
// ✅ Sempre use parâmetros para evitar SQL Injection
const users = await prisma.$queryRaw`
SELECT * FROM "User" WHERE email = ${email}
`
// ❌ NUNCA faça isso (vulnerável a SQL Injection)
const users = await prisma.$queryRaw`
SELECT * FROM "User" WHERE email = '${email}'
`
// ✅ Use transaction para operações complexas
const result = await prisma.$transaction(async (prisma) => {
const user = await prisma.$queryRaw`
INSERT INTO "User" (email, name, "createdAt", "updatedAt")
VALUES (${'user@test.com'}, ${'Test User'}, NOW(), NOW())
RETURNING id
`
const posts = await prisma.$executeRaw`
INSERT INTO "Post" (title, "authorId", "createdAt", "updatedAt")
VALUES (${'Welcome Post'}, ${user[0].id}, NOW(), NOW())
`
return { user, posts }
})
// ✅ Use tipos TypeScript quando possível
interface UserWithPostCount {
id: number
email: string
name: string | null
post_count: bigint
}
const usersWithPosts = await prisma.$queryRaw<UserWithPostCount[]>`
SELECT u.*, COUNT(p.id) as post_count
FROM "User" u
LEFT JOIN "Post" p ON u.id = p."authorId"
GROUP BY u.id
`
// Buscar usuários com filtros
const users = await prisma.user.findMany({
where: {
email: {
contains: '@gmail.com',
},
posts: {
some: {
published: true,
},
},
},
include: {
posts: {
where: {
published: true,
},
},
},
orderBy: {
createdAt: 'desc',
},
take: 10, // Limitar a 10 resultados
skip: 0, // Paginação
})
// Agregações
const userStats = await prisma.user.aggregate({
_count: {
id: true,
},
_avg: {
id: true,
},
})
// Transações
const result = await prisma.$transaction(async (prisma) => {
const user = await prisma.user.create({
data: {
email: 'user@example.com',
name: 'João',
},
})
const post = await prisma.post.create({
data: {
title: 'Primeiro Post',
content: 'Conteúdo do post',
authorId: user.id,
},
})
return { user, post }
})
// Middleware do Prisma
const prisma = new PrismaClient()
prisma.$use(async (params, next) => {
console.log('Query executada:', params.model, params.action)
const result = await next(params)
return result
})
# Visualizar banco no navegador
npx prisma studio
# Reset do banco
npx prisma migrate reset
# Deploy de migrations em produção
npx prisma migrate deploy
# Gerar cliente após mudanças no schema
npx prisma generate
# Verificar status das migrations
npx prisma migrate status
sudo apt update && sudo apt upgrade -y
curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash -
npm ci --only=production
git fetch origin
git pull origin main
npx prisma generate
npx prisma migrate deploy
npx prisma migrate dev --name nome_da_migration
npx prisma migrate status
npx prisma migrate diff
npx prisma db pull
npx prisma db push
npx prisma db seed
npx prisma studio
npx prisma migrate reset
npx prisma format
npx prisma validate
prisma.$queryRaw
prisma.$executeRaw
prisma.$queryRawUnsafe
prisma.$executeRawUnsafe
sudo -u postgres createdb proddb
pg_dump -h localhost -U userapp -d proddb > backup.sql
docker exec postgres-prod pg_dump -U userapp proddb > backup.sql
npm run build
npm start
pm2 start npm --name "minha-app" -- start
pm2 stop minha-app
pm2 restart minha-app
pm2 logs minha-app --lines 50
pm2 logs minha-app --follow
pm2 status
pm2 save
pm2 startup
tar -czf arquivo.tar.gz pasta/
cp -r origem destino
rsync -av origem/ destino/
curl -I http://localhost:3000/api/health
node --version
npm --version
Schema → migrate dev (faz tudo)
1. migrate deploy (atualiza banco)
2. generate (atualiza código)
3. build + restart (usa código novo)
select e include conscientemente para evitar over-fetchingA lógica fundamental é: Sempre mantenha banco e código TypeScript sincronizados com o schema, mas em produção você faz isso em etapas controladas para maior segurança.