PRODPiattaforma BaaS europea sovranaApri Dashboard →

IA nativa · 8 lettura minima

Tutorial: crea un endpoint NL2SQL su Postgres

Affane Daylami · Fondateur · 28 agosto 2026

Torniamo al blog

Questo tutorial mostra come ricevere una domanda in francese, trasformarla in una query SQL convalidata e delimitata, quindi restituire il risultato, senza mai eseguire SQL non controllato. L'SQL effettivamente generato viene visualizzato in ogni passaggio, non solo il risultato finale.

Questo testo inglese è stato generato automaticamente dall'originale francese e non è stato ancora rivisto.
Questa pagina è stata tradotta automaticamente. Fa fede la versione inglese.

Il motore NL2SQL di Aurabase fa parte dell'intelligenza artificiale nativaintegrata nel backend: non è un servizio di terze parti da mettere insieme. Prerequisiti per seguire questa guida: un progetto Aurabase esistente, un semplice schema di database per l'esempio e una chiave API del progetto.

#
Obiettivo

Cosa costruirai

Un endpoint che riceve una domanda in linguaggio naturale, la trasforma in una query SQL convalidata e delimitata, quindi restituisce il risultato. Il motore non esegue mai l'SQL generato senza controllo: ogni query passa attraverso la validazione sintattica prima di raggiungere il database.

Informazioni

Questo tutorial utilizza @aurabase/aurabase-js JavaScript SDK e la chiamata HTTP non elaborata equivalente, quindi puoi seguire da qualsiasi lingua.

#
Sotto il cofano

Come funziona il motore NL2SQL

La domanda passa attraverso un LLM configurato – OpenAI, Anthropic (Claude) o Gemini, i tre fornitori nativi – che genera un SQL candidato. Questo SQL non viene mai eseguito così com'è: passa attraverso un validatore che ne analizza l'albero della sintassi (sqlparser), consente solo semplici query SELECT e aggiunge un LIMIT limitato se ne manca uno.

Il validatore rifiuta esplicitamente CTE/WITH, sottoquery, UNION, clausole di blocco (FOR UPDATE) e qualsiasi funzione esterna a una whitelist (count, sum, avg, min, max, lower, upper, coalesce, date_trunc, now). Sono supportate le unioni di più tabelle.

Domanda→LLM (OpenAI/Claude/Gemini)→Convalida AST (sqlparser)→LIMITE delimitato→Esecuzione SELECT

Lo schema interrogato non viene mai fornito dalla vostra richiesta: viene introspezionato dalla base reale del progetto. Un campo schema, allowed_schema o schema_context inviato nel corpo della richiesta viene esplicitamente rifiutato (errore 400) anziché ignorato silenziosamente: solo il server decide cosa esiste effettivamente.

#
Passaggio 1

Configurare l'endpoint NL2SQL

Con JavaScript SDK, il client Aurabase espone aura.ai.nl2sql(). La firma è nl2sql(question, options): lo schema non ne fa parte, viene introspezionato lato server.

app/api/ask/route.tstypescript
import { aura } from '@/lib/aurabase'

export async function POST(req: Request) {
  const { question } = await req.json()

  const { data, error } = await aura.ai.nl2sql(
    question,
    { limit: 50 }
  )

  if (error) return Response.json({ error }, { status: 400 })
  return Response.json(data)
}

In HTTP non elaborato, l'endpoint è POST /v1/ai/{project_id}/nl2sql, autenticato dalla chiave API del progetto.

terminalbash
curl -X POST https://<votre-gateway>/v1/ai/<project_id>/nl2sql \
  -H "apikey: <votre-cle-api>" \
  -H "Content-Type: application/json" \
  -d '{
    "question": "Combien de commandes ont été passées ce mois-ci par des clients premium ?",
    "limit": 50
  }'
Campi rifiutati dal server

Non inviare schema, né allowed_schema, né schema_context nel corpo della richiesta: lo schema interrogato è determinato dal server, questi campi vengono esplicitamente rifiutati (400) anziché sovrascritti silenziosamente.

#
Passaggio 2

Prova con una vera domanda in francese

Domanda inviata: “Quanti ordini sono stati effettuati questo mese da clienti premium?”. Ecco la forma dell'SQL effettivamente reso (i nomi delle tabelle e le colonne dipendono dallo schema):

risposta (estratto)json
{
  "data": {
    "sql": "SELECT count(*) FROM orders WHERE customer_plan = 'premium' AND created_at >= date_trunc('month', now()) LIMIT 50",
    "explanation": "Compte les commandes de ce mois pour les clients premium.",
    "confidence": 0.85,
    "tables": ["orders"],
    "columns": ["customer_plan", "created_at"],
    "limit": 50,
    "limit_injected": false
  },
  "meta": null
}

limit_injected indica se LIMIT proviene dal modello o è stato aggiunto dal server. confidence è un'euristica sulla forma della risposta (blocco SQL ben formato o meno) - non una misura della correttezza semantica dell'SQL generato. Una domanda mal formulata restituisce un errore esplicito anziché un SQL allucinato: ad esempio, se l'SQL generato interroga una tabella non presente nel tuo schema, il messaggio nomina le tabelle effettivamente disponibili.

#
Passaggio 3

Produzione sicura

Tre controlli prima della distribuzione: il limite massimo delle righe (LIMIT) è adatto al tuo volume, il ruolo Postgres utilizzato dal motore rimane limitato allo schema del progetto e le tabelle sensibili hanno una politica RLS attiva: NL2SQL esegue query sullo stesso database del resto dell'applicazione, non dispone di diritti di accesso estesi per impostazione predefinita.

  • Il limite di riga predefinito è configurabile sul lato server; un valore richiesto superiore al limite del server viene negato esplicitamente anziché ridotto silenziosamente.
  • L'accesso al catalogo di sistema (pg_catalog, information_schema) e agli schemi non di progetto è bloccato dal validatore, indipendentemente dai criteri RLS.
  • RLS rimane la tua ultima linea di difesa sulle tabelle sensibili: il validatore limita la forma dell'SQL, non i diritti commerciali sui dati.
#
Onestà

Limiti attuali di cui essere consapevoli

Il motore è rigorosamente leggibile: vengono accettate solo le richieste SELECT. Qualsiasi tentativo diINSERT, UPDATE, DELETE, DROP, CREATE o ALTER generato dal modello viene rifiutato prima dell'esecuzione: questa non è una convenzione di prompt, è una regola imposta a livello dell'albero della sintassi.

Altri limiti strutturali: nessuna sottoquery, nessuna CTE/WITH, nessuna UNION e una whitelist chiusa di dieci funzioni SQL. Una domanda che richiede naturalmente una sottoquery ("clienti che non hanno mai ordinato") deve essere riformulata per adattarsi a un semplice SELECTo gestita in modo diverso dal lato dell'applicazione.

#
Vai oltre

RAG e agenti

NL2SQL copre domande strutturate sui tuoi dati relazionali. Per domande su contenuti non strutturati (documenti, note, ticket), il RAG nativo di Aurabase si affida a pgvector e ad una ricerca HNSW. Entrambe le funzionalità, e come combinarle in un agente, sono descritte in dettaglio nella pagina Native AI su Postgres.

#
Domande frequenti

Domande frequenti

NL2SQL funziona con uno schema complesso (join multipli)?+
I join su più tabelle sono supportati dal validatore. D'altra parte, subquery e CTE/WITH vengono esplicitamente rifiutate: una domanda che richiede naturalmente una subquery deve essere riformulata per adattarsi a una semplice SELECT con join, o altrimenti gestita dal lato dell'applicazione.
Quale provider LLM scegliere per NL2SQL?+
I tre provider nativi (OpenAI, Anthropic/Claude, Gemini) vengono trattati allo stesso modo dal validatore: nessuno ha un vantaggio strutturale sulla validazione dell'SQL generato. Costo e latenza dipendono dal modello specifico configurato per il tuo progetto: confrontali sul tuo volume anziché seguire una raccomandazione generica.

PRONTO PER L'IMPLEMENTAZIONE?

Il tuo backend in cinque minuti.

Nessuna carta di credito richiesta · 500 MB gratuiti · 50.000 MAU