← Back to selected work

NL-to-SQL Analytics Agent

A self-hostable workflow that turns a natural-language question into SQL and a readable result.

Role
Lead Backend Engineer
Access
Public repository
Workflow preview

user › Show monthly sales by campaign

SELECT campaign, DATE_TRUNC('month', sold_at),
SUM(revenue) FROM sales
GROUP BY campaign, 2;
M1
M2
M3
M4
M5
[01]

Context

Non-technical users often depend on engineers for routine database questions. The agent explores a controlled path from a plain-language request to schema-aware SQL, database execution, and visual output.

[02]

My responsibility

I designed the orchestration flow, API boundary, database integration, containerized setup, and the contract that turns query results into chart-ready data.

[03]

System scheme

Engineering scheme

Controlled question-to-query pipeline

Input → Processing → Output

Model output stays inside a validation and read-only execution boundary before results reach the visualization layer.

  1. 01

    User question

    Capture analytical intent in natural language.

  2. 02

    Schema selection

    Provide only relevant tables, columns, and relationships.

  3. 03

    SQL generation

    Generate a query constrained by the selected schema.

  4. 04

    Safety validation

    Reject writes, unsupported statements, and invalid structures.

  5. 05

    Limited execution

    Run with read-only credentials, row limits, and timeouts.

  6. 06

    Result adapter

    Return typed rows and chart-ready metadata to the interface.

Schema awareness
Read-only access
Query validation
Execution limits
[04]

Architecture

  1. 01

    User question

    Captures the analytical intent in plain language.

  2. 02

    Schema context

    Provides relevant tables and columns to the model.

  3. 03

    SQL generation

    Produces a query constrained by the available schema.

  4. 04

    Validation

    Rejects unsafe or unsupported query behavior before execution.

  5. 05

    Read-only database

    Executes the accepted query within a restricted boundary.

  6. 06

    Result adapter

    Returns rows and chart-ready metadata to the interface.

[05]

Technical scope

Python01
FastAPI02
Node.js03
PostgreSQL04
OpenAI API05
Docker06
[06]

Example flow

QUESTION

Compare monthly ticket volume by priority for the last quarter.

GENERATED SQL

SELECT date_trunc('month', created_at) AS month,
       priority,
       COUNT(*) AS tickets
FROM support_tickets
WHERE created_at >= CURRENT_DATE - INTERVAL '3 months'
GROUP BY month, priority
ORDER BY month, priority;

RESULT VIEW

High128
Medium94
Low51

Illustrative query and result using representative support data.

[07]

Engineering decisions

01

Give the model only relevant schema context

Focused schema information reduces ambiguity and keeps prompts smaller than sending an unrestricted database description for every question.

02

Treat generated SQL as untrusted input

Validation, read-only credentials, statement limits, and execution timeouts form the security boundary. Model output is never trusted because it looks syntactically correct.

03

Separate query execution from presentation

The backend returns structured rows and metadata; the interface decides whether a table, bar chart, or another representation communicates the result best.

04

Make local evaluation repeatable

Docker Compose keeps the API, supporting services, and database setup reproducible for development and review.

[08]

Proof of ownership

  • Schema-aware natural-language to SQL flow
  • FastAPI orchestration boundary
  • Structured output for tables and charts
  • Docker Compose setup for self-hosting
[09]

What I learned

  • LLM output requires the same defensive treatment as any external input.
  • Good schema selection improves both reliability and prompt efficiency.
  • A useful analytics tool must explain results, not merely produce a query.

Need a system like this?

Tell me about the workflow, integration, or backend problem.

Discuss a project