00
:
00
:
00
:
00
Corso SEO AI - Usa SEOEMAIL al checkout per il 30% di sconto

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

  1. Preferisci sempre Prisma Client quando possibile: hai type-safety e protezione automatica.
  2. Usa tagged template literals ($queryRaw) invece di $queryRawUnsafe per la protezione da SQL injection.
  3. Tipizza i risultati con generics per mantenere la type-safety.
  4. Attenzione ai nomi: in PostgreSQL i nomi di tabella/colonna sono case-sensitive e vanno tra virgolette doppie.
  5. BigInt: le funzioni aggregate PostgreSQL restituiscono BigInt, converti con Number() se necessario.