Raw Queries
Quando Usare Raw Queries
Prisma Client copre la maggior parte dei casi d’uso, ma a volte serve SQL diretto per:
- Query molto complesse con JOIN multipli o subquery.
- Funzioni specifiche del database (es. funzioni geospaziali PostGIS).
- Ottimizzazioni di performance su query critiche.
- Operazioni non supportate dall’API Prisma.
$queryRaw
Esegue una query SQL e restituisce i risultati. Usa tagged template literals per la prevenzione automatica delle SQL injection.
Query Base
import { Prisma } from '@prisma/client'
// Tagged template literal (SICURO - previene SQL injection)
const users = await prisma.$queryRaw`
SELECT id, name, email FROM "User" WHERE role = 'ADMIN'
`
console.log(users)
// [{ id: 1, name: 'Marco', email: 'marco@example.com' }, ...]
Query con Parametri
I parametri vengono automaticamente sanitizzati:
const role = 'ADMIN'
const minAge = 18
// I parametri sono inseriti come $1, $2, ecc. (PostgreSQL)
const users = await prisma.$queryRaw`
SELECT id, name, email
FROM "User"
WHERE role = ${role}
AND age >= ${minAge}
ORDER BY name ASC
`
Parametri con Prisma.sql
Per query piu’ dinamiche, usa l’helper Prisma.sql:
const orderField = 'name'
const direction = 'ASC'
// Prisma.raw per valori NON parametrizzati (attenzione all'injection!)
const users = await prisma.$queryRaw`
SELECT * FROM "User"
ORDER BY ${Prisma.raw(`"${orderField}" ${direction}`)}
`
// Prisma.join per liste di valori
const ids = [1, 2, 3, 4, 5]
const users2 = await prisma.$queryRaw`
SELECT * FROM "User"
WHERE id IN (${Prisma.join(ids)})
`
// Comporre query complesse
const baseQuery = Prisma.sql`SELECT * FROM "User"`
const whereClause = Prisma.sql`WHERE role = ${'ADMIN'}`
const result = await prisma.$queryRaw`${baseQuery} ${whereClause}`
Tipizzare i Risultati
// Definisci un tipo per i risultati
type UserResult = {
id: number
name: string
email: string
post_count: bigint
}
const users = await prisma.$queryRaw<UserResult[]>`
SELECT u.id, u.name, u.email, COUNT(p.id) as post_count
FROM "User" u
LEFT JOIN "Post" p ON p."authorId" = u.id
GROUP BY u.id
HAVING COUNT(p.id) > ${5}
`
// Nota: COUNT restituisce BigInt in PostgreSQL
users.forEach((u) => {
console.log(`${u.name}: ${Number(u.post_count)} post`)
})
$executeRaw
Esegue una query SQL che non restituisce dati (INSERT, UPDATE, DELETE). Restituisce il numero di righe modificate.
// UPDATE
const updatedRows = await prisma.$executeRaw`
UPDATE "User"
SET "isActive" = false
WHERE "lastLoginAt" < ${new Date('2025-01-01')}
`
console.log(`${updatedRows} utenti disattivati`)
// DELETE
const deletedRows = await prisma.$executeRaw`
DELETE FROM "Post"
WHERE published = false
AND "createdAt" < ${new Date('2024-01-01')}
`
console.log(`${deletedRows} bozze eliminate`)
// INSERT
const inserted = await prisma.$executeRaw`
INSERT INTO "Category" (name, slug)
VALUES ('Tecnologia', 'tecnologia'),
('Design', 'design')
ON CONFLICT (name) DO NOTHING
`
$queryRawUnsafe e $executeRawUnsafe
Per query completamente dinamiche dove non puoi usare template literals. Attenzione: devi gestire manualmente la prevenzione delle SQL injection.
// ATTENZIONE: vulnerabile a SQL injection se i parametri non sono validati
const tableName = 'User'
// Unsafe - usare solo con input fidato
const results = await prisma.$queryRawUnsafe(
`SELECT * FROM "${tableName}" WHERE id = $1`,
1 // Parametro posizionale
)
// Unsafe con piu' parametri
const rows = await prisma.$executeRawUnsafe(
'UPDATE "User" SET role = $1 WHERE id = $2',
'ADMIN',
42
)
Quando Usare Unsafe
// Caso legittimo: nome tabella dinamico (validato)
const allowedTables = ['User', 'Post', 'Comment']
function queryTable(table: string) {
if (!allowedTables.includes(table)) {
throw new Error('Tabella non consentita')
}
return prisma.$queryRawUnsafe(`SELECT COUNT(*) FROM "${table}"`)
}
Raw Query con Transazioni
Le raw query possono essere usate dentro le transazioni:
const result = await prisma.$transaction(async (tx) => {
// Blocca le righe per aggiornamento (SELECT FOR UPDATE)
const products = await tx.$queryRaw<{ id: number; stock: number }[]>`
SELECT id, stock FROM "Product"
WHERE id IN (${Prisma.join([1, 2, 3])})
FOR UPDATE
`
// Aggiorna lo stock
for (const product of products) {
await tx.$executeRaw`
UPDATE "Product"
SET stock = stock - 1
WHERE id = ${product.id} AND stock > 0
`
}
return products
})
Best Practice
- Preferisci sempre Prisma Client quando possibile: hai type-safety e protezione automatica.
- Usa tagged template literals (
$queryRaw) invece di$queryRawUnsafeper la protezione da SQL injection. - Tipizza i risultati con generics per mantenere la type-safety.
- Attenzione ai nomi: in PostgreSQL i nomi di tabella/colonna sono case-sensitive e vanno tra virgolette doppie.
- BigInt: le funzioni aggregate PostgreSQL restituiscono
BigInt, converti conNumber()se necessario.