Retour au blog
Tutoriel Technique · 8 min de lecture

Tutoriel : construire un endpoint NL2SQL sur Postgres

Affane Daylami · Fondateur· 23 août 2026

Ce tutoriel montre comment recevoir une question en français, la transformer en requête SQL validée et bornée, puis retourner le résultat — sans jamais exécuter de SQL non vérifié. Le SQL réellement généré est affiché à chaque étape, pas seulement le résultat final.

Le moteur NL2SQL d’Aurabase fait partie de l’IA native intégrée au backend : pas un service tiers à assembler. Prérequis pour suivre ce guide : un projet Aurabase existant, un schéma de base de données simple pour l’exemple, et une clé API projet.

#
Objectif

Ce que vous allez construire

Un endpoint qui reçoit une question en langage naturel, la transforme en requête SQL validée et bornée, puis retourne le résultat. Le moteur n’exécute jamais du SQL généré sans contrôle : chaque requête passe par une validation syntaxique avant d’atteindre la base.

Info
Ce tutoriel utilise le SDK JavaScript @aurabase/aurabase-js et l’appel HTTP brut équivalent, pour que vous puissiez suivre depuis n’importe quel langage.
#
Sous le capot

Comment fonctionne le moteur NL2SQL

La question passe par un LLM configuré — OpenAI, Anthropic (Claude) ou Gemini, les trois fournisseurs natifs — qui génère un SQL candidat. Ce SQL n’est jamais exécuté tel quel : il traverse un validateur qui parse son arbre syntaxique (sqlparser), n’autorise que des requêtes SELECT simples, et ajoute un LIMIT borné s’il en manque un.

Le validateur rejette explicitement les CTE/WITH, les sous-requêtes, les UNION, les clauses de verrouillage (FOR UPDATE), et toute fonction hors d’une liste blanche (count, sum, avg, min, max, lower, upper, coalesce, date_trunc, now). Les jointures multi-tables, elles, sont supportées.

QuestionLLM (OpenAI / Claude / Gemini)Validation AST (sqlparser)LIMIT bornéExécution SELECT

Le schéma interrogé n’est jamais fourni par votre requête : il est introspecté depuis la base réelle du projet. Un champ schema, allowed_schema ou schema_context envoyé dans le corps de la requête est explicitement refusé (erreur 400) plutôt qu’ignoré en silence — le serveur seul décide de ce qui existe réellement.

#
Étape 1

Configurer le endpoint NL2SQL

Avec le SDK JavaScript, le client Aurabase expose aura.ai.nl2sql(). L’argument schéma n’est plus accepté (il est introspecté côté serveur) : passez undefined à sa place.

app/api/ask/route.ts
TYPESCRIPT
import { aura } from '@/lib/aurabase'
export async function POST(req: Request) {
const { question } = await req.json()
const { data, error } = await aura.ai.nl2sql(
question,
undefined,
{ limit: 50 }
)
if (error) return Response.json({ error }, { status: 400 })
return Response.json(data)
}

En HTTP brut, l’endpoint est POST /v1/ai/{project_id}/nl2sql, authentifié par la clé API du projet.

terminal
BASH
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
}'
Champs refusés par le serveur
N’envoyez ni schema, ni allowed_schema, ni schema_context dans le corps de la requête : le schéma interrogé est déterminé par le serveur, ces champs sont explicitement rejetés (400) plutôt qu’écrasés en silence.
#
Étape 2

Tester avec une vraie question en français

Question envoyée : « Combien de commandes ont été passées ce mois-ci par des clients premium ? ». Voici la forme du SQL réellement rendu (noms de tables et colonnes dépendent de votre schéma) :

réponse (extrait)
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 indique si le LIMIT vient du modèle ou a été ajouté par le serveur. confidence est une heuristique sur la forme de la réponse (bloc SQL bien formé ou non) — pas une mesure d’exactitude sémantique du SQL généré. Une question mal formulée retourne une erreur explicite plutôt qu’un SQL halluciné : par exemple, si le SQL généré interroge une table absente de votre schéma, le message nomme les tables réellement disponibles.

#
Étape 3

Sécuriser la production

Trois vérifications avant de déployer : le plafond de lignes (LIMIT) est-il adapté à votre volumétrie, le rôle Postgres utilisé par le moteur reste-t-il restreint au schéma du projet, et les tables sensibles ont-elles une policy RLS active — le NL2SQL interroge la même base que le reste de votre application, il n’a pas de droits d’accès élargis par défaut.

  • Le plafond de lignes par défaut est configurable côté serveur ; une valeur demandée au-delà du plafond serveur est refusée explicitement plutôt que silencieusement réduite.
  • L’accès au catalogue système (pg_catalog, information_schema) et aux schémas hors projet est bloqué par le validateur, indépendamment de vos policies RLS.
  • RLS reste votre dernière ligne de défense sur les tables sensibles : le validateur borne la forme du SQL, pas les droits métier sur les données.
#
Honnêteté

Limites actuelles à connaître

Le moteur est strictement en lecture : seules les requêtes SELECT sont acceptées. Toute tentative d’INSERT, UPDATE, DELETE, DROP, CREATE ou ALTER générée par le modèle est rejetée avant exécution — ce n’est pas une convention de prompt, c’est une règle imposée au niveau de l’arbre syntaxique.

Autres limites structurelles : pas de sous-requêtes, pas de CTE/WITH, pas d’UNION, et une liste blanche fermée de dix fonctions SQL. Une question qui appelle naturellement une sous-requête (« les clients qui n’ont jamais commandé ») doit être reformulée pour tenir dans un SELECT simple, ou traitée autrement côté application.

#
Aller plus loin

RAG et agents

Le NL2SQL couvre les questions structurées sur vos données relationnelles. Pour des questions sur du contenu non structuré (documents, notes, tickets), le RAG natif d’Aurabase s’appuie sur pgvector et une recherche HNSW. Les deux capacités — et la manière de les combiner dans un agent — sont détaillées sur la page IA native sur Postgres.

#
Questions Fréquentes

FAQ

Le NL2SQL fonctionne-t-il avec un schéma complexe (jointures multiples) ?+
Les jointures multi-tables sont supportées par le validateur. En revanche, les sous-requêtes et les CTE/WITH sont explicitement rejetées : une question qui appelle naturellement une sous-requête doit être reformulée pour tenir dans un SELECT simple avec jointures, ou être traitée autrement côté application.
Quel fournisseur LLM choisir pour le NL2SQL ?+
Les trois fournisseurs natifs (OpenAI, Anthropic/Claude, Gemini) sont traités à égalité par le validateur : aucun n’a d’avantage structurel sur la validation du SQL généré. Le coût et la latence dépendent du modèle précis configuré pour votre projet — comparez-les sur votre propre volumétrie plutôt que de suivre une recommandation générique.
ESSAYEZ MAINTENANT

Ajoutez du NL2SQL à votre projet en quelques minutes.

Un projet Aurabase suffit, la validation SQL est déjà en place.

Créer un projet Documentation IA
Aucune carte bancaire requise · 500 MB gratuits · 50 000 MAU