Build a Natural Language-to-SQL Generator for SaaS Dashboards
Business intelligence (BI) teams and product managers frequently rely on data engineers to run custom database queries. Questions like "What was our monthly recurring revenue (MRR) by state last quarter?" or "Which 10 customers have the highest API token usage this week?" often require writing SQL by hand.
In this guide, we will build a Text-to-SQL Query Generator using Python, FastAPI, and Velona's AI Gateway. Non-technical team members can type questions in plain English or Hindi, and the AI translates them into read-only SQL that runs safely against PostgreSQL or MySQL. With a low-cost coding model, each generated query costs well under one paisa.
Text-to-SQL Architecture and Security Guardrails
When translating natural language to SQL, safety and schema awareness are critical:
- Schema Injection: Provide the table structures, column names, and data types in the system prompt so the AI understands table relationships.
- Read-Only Access: Execute generated queries with a database role that can only run
SELECT, so an accidentalDELETE,DROP, orUPDATEcan never succeed. - Query Validation: Check the SQL before execution to block multi-statement injection.
Step 1: Set Up Your Velona Account and API Key
To access coding models on Velona:
- Register on the Velona Sign-Up Page.
- Open your Prepaid Wallet Dashboard and top up ₹10 or more using GPay, PhonePe, or RuPay. Usage is billed in rupees at transparent rates shown on the pricing page.
- Go to API Key Management and create a new key named
bi-dashboard-sql. - Set a spending budget on the key to prevent unexpected API costs.
Step 2: Build the FastAPI Text-to-SQL Endpoint
Create a file named text_to_sql.py. We will use Velona's native endpoint (/gateway/v1/inference/run) to turn plain text into SQL.
pip install fastapi uvicorn requests pydantic
import os
import re
import requests
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
app = FastAPI()
VELONA_API_KEY = os.environ["VELONA_API_KEY"]
VELONA_URL = "https://velona.in/gateway/v1/inference/run"
MODEL = "deepseek/deepseek-v4-flash"
headers = {
"Authorization": f"Bearer {VELONA_API_KEY}",
"Content-Type": "application/json",
}
DB_SCHEMA = """
Database: PostgreSQL
Table: users (id INT, email VARCHAR, created_at TIMESTAMP, plan_type VARCHAR)
Table: subscriptions (id INT, user_id INT, amount_paise INT, status VARCHAR, created_at TIMESTAMP)
Table: api_usage (id INT, user_id INT, model VARCHAR, prompt_tokens INT, completion_tokens INT, created_at TIMESTAMP)
"""
SYSTEM_PROMPT = f"""You are a strict PostgreSQL expert data analyst.
Translate the user's question into a valid SQL query based ONLY on the schema below.
Schema:
{DB_SCHEMA}
Rules:
1. Return ONLY the raw SQL query, with no markdown or explanation.
2. Only write SELECT queries (a WITH clause is fine). Never write INSERT, UPDATE, DELETE, or DROP.
3. Use proper PostgreSQL date functions where applicable.
"""
FORBIDDEN = re.compile(
r"\b(insert|update|delete|drop|alter|truncate|grant|revoke|create)\b", re.I
)
class QueryRequest(BaseModel):
question: str
@app.post("/generate-sql")
def generate_sql(req: QueryRequest):
payload = {
"model": MODEL,
"turns": [
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": req.question},
],
}
response = requests.post(VELONA_URL, json=payload, headers=headers, timeout=60)
response.raise_for_status()
# The native endpoint returns the reply text in data.output
raw = response.json()["data"]["output"]
sql = re.sub(r"```(sql)?", "", raw).strip().rstrip(";").strip()
# Guardrails: one statement, read-only keywords only
if ";" in sql:
raise HTTPException(400, "Multiple statements are not allowed.")
if not re.match(r"^(select|with)\b", sql, re.I) or FORBIDDEN.search(sql):
raise HTTPException(400, "Only read-only SELECT queries are allowed.")
return {"user_question": req.question, "generated_sql": sql}
Prefer the OpenAI SDK? Point it at https://velona.in/v1 and use the same model IDs. The Python guide shows the setup.
Step 3: Run the Query Safely
The keyword check is a first filter, not your real protection. The real protection is a database role that cannot write:
CREATE ROLE bi_readonly LOGIN PASSWORD 'choose-a-strong-password';
GRANT CONNECT ON DATABASE app TO bi_readonly;
GRANT USAGE ON SCHEMA public TO bi_readonly;
GRANT SELECT ON users, subscriptions, api_usage TO bi_readonly;
ALTER ROLE bi_readonly SET statement_timeout = '10s';
Connect with this role when executing the generated SQL, and add a LIMIT on results shown in the dashboard.
Step 4: Choosing the Right Model for SQL Generation
A typical request is about 500 input tokens (schema plus question) and 150 output tokens. Cost per generated query at the time of writing:
| Model ID | ₹ per 1M input / output | Per query | Best for |
|---|---|---|---|
deepseek/deepseek-v4-flash | ₹2.90 / ₹5.80 | ₹0.002 | Default: most JOIN and GROUP BY questions |
deepseek/deepseek-v4-pro | ₹21.63 / ₹43.27 | ₹0.017 | Window functions and tricky date logic |
qwen/qwen3-coder | ₹31.08 / ₹103.61 | ₹0.03 | Code-specialised alternative |
anthropic/claude-sonnet-5 | ₹207.22 / ₹1,036 | ₹0.26 | Large, messy multi-table schemas |
Even on Claude Sonnet 5, a team running 1,000 questions a month spends about ₹260. On DeepSeek V4 Flash, ₹10 covers several thousand queries. Your usage dashboard shows the rupee cost of each request.
Developer Utilities and Testing
When formatting schemas or checking generated queries, use Velona's free developer tools, including the SQL Beautifier, JSON Formatter, and Regex Evaluator. They run in your browser and never touch your wallet.
To compare SQL accuracy across models before you choose one, run the same question through the Model Evaluator.
Conclusion
A Text-to-SQL generator opens up data access across your organisation and saves engineering teams hours of manual reporting. With Velona, Indian developers can build secure internal data tools on top coding models and pay with a UPI top-up.
Ready to build your AI database tools? Create a free Velona account, add ₹10, and generate your first queries today.