Skip to main content

FastAPI SQL Query Engine

Project description

🚀 DBAnu - FastAPI SQL Query Engine

Transform SQL queries into production-ready REST APIs in seconds.

DBAnu eliminates the boilerplate of creating database APIs. With just a few lines of code, expose any SQL query as a fully-featured FastAPI endpoint with built-in filtering, pagination, authentication, and middleware support.

✨ Why DBAnu?

The Problem

Building database APIs involves repetitive work:

  • Writing the same CRUD endpoints
  • Implementing pagination logic
  • Adding authentication checks
  • Creating filtering systems
  • Handling database connections

The Solution

DBAnu turns this:

# Traditional approach - 50+ lines
@app.get("/api/books")
async def get_books(author: str = None, limit: int = 100, offset: int = 0):
    # Validate inputs, build WHERE clause, handle pagination...
    # ...and much more boilerplate

Into this:

# DBAnu approach - 3 lines
serve_select(
    app=app,
    query_engine=query_engine,
    path="/api/books",
    select_query="SELECT * FROM books LIMIT ? OFFSET ?"
)

🎯 Key Features

🚀 Instant API Generation

Turn any SQL query into a REST endpoint in seconds

🔄 Multi-Database Union Queries

Combine results from SQLite, PostgreSQL, MySQL in a single API call

🛡️ Type-Safe Everything

Pydantic-powered validation for filters, responses, and middleware

🔧 Powerful Middleware System

Intercept and modify queries, add logging, authentication, and more. Middleware functions must be async.

🎛️ Dynamic Query Generation

Create queries on-the-fly based on request parameters

📊 Smart Pagination

Built-in pagination with priority-based union pagination across databases

🚀 Quick Start

Installation

pip install dbanu

From Zero to API in 60 Seconds

from fastapi import FastAPI
from dbanu import serve_select, SQLiteQueryEngine

app = FastAPI()

# Create a SQLite query engine
query_engine = SQLiteQueryEngine()

# 🎉 Create your first API endpoint
serve_select(
    app=app,
    query_engine=query_engine,
    path="/api/books",
    select_query="SELECT id, title, author FROM books LIMIT ? OFFSET ?",
    count_query="SELECT COUNT(*) FROM books"
)

That's it! You now have a fully functional API with:

  • ✅ Automatic pagination (limit and offset parameters)
  • ✅ Total count for frontend pagination
  • ✅ Proper error handling
  • ✅ FastAPI documentation at /docs

📚 Usage Examples

Basic Query Endpoint

from fastapi import FastAPI
from dbanu import serve_select, SQLiteQueryEngine

app = FastAPI()
query_engine = SQLiteQueryEngine()

# Simple books endpoint
serve_select(
    app=app,
    query_engine=query_engine,
    path="/api/books",
    select_query="SELECT * FROM books LIMIT ? OFFSET ?",
    count_query="SELECT COUNT(*) FROM books"
)

Usage:

GET /api/books?limit=10&offset=0

Advanced Filtering

from pydantic import BaseModel
from dbanu import serve_select, SQLiteQueryEngine

app = FastAPI()

# Define your filter model
class BookFilter(BaseModel):
    author: str | None = None
    min_year: int | None = None

query_engine = SQLiteQueryEngine()

serve_select(
    app=app,
    query_engine=query_engine,
    path="/api/books",
    filter_model=BookFilter,
    select_query=(
        "SELECT id, title, author, year FROM books "
        "WHERE (author = %s OR %s IS NULL) "
        "AND (year >= %s OR %s IS NULL) "
        "LIMIT %s OFFSET %s"
    ),
    select_param=lambda filters, limit, offset: [
        filters.author, filters.author,
        filters.min_year, filters.min_year,
        limit, offset
    ]
)

Usage:

# Get books by Stephen King published after 2000
GET /api/books?author=Stephen%20King&min_year=2000&limit=10&offset=0

Dynamic Queries

Create queries dynamically based on filters:

from typing import Callable
from pydantic import BaseModel
from dbanu import serve_select, SQLiteQueryEngine

app = FastAPI()

class DynamicFilter(BaseModel):
    table: str
    condition: str = "1=1"

def create_query(query_template: str) -> Callable[[DynamicFilter], str]:
    def query_builder(filters: DynamicFilter) -> str:
        if filters.table == "":
            raise ValueError("Table name cannot be empty")
        return query_template.format(
            _table=filters.table, 
            _filters=filters.condition
        )
    return query_builder

query_engine = SQLiteQueryEngine()

serve_select(
    app=app,
    query_engine=query_engine,
    path="/api/dynamic",
    filter_model=DynamicFilter,
    select_query=create_query("SELECT * FROM {_table} WHERE {_filters} LIMIT %s OFFSET %s"),
    count_query=create_query("SELECT COUNT(*) FROM {_table} WHERE {_filters}")
)

Usage:

# Query books table with author filter
GET /api/dynamic?table=books&condition=author='Stephen%20King'&limit=10&offset=0

Multi-Database Union Queries

Query multiple databases simultaneously and get unified results!

from fastapi import FastAPI
from dbanu import serve_union, SelectSource, SQLiteQueryEngine, PostgreSQLQueryEngine

app = FastAPI()

# Create engines for different databases
sqlite_engine = SQLiteQueryEngine(db_path="./classic_literature.db")
pgsql_engine = PostgreSQLQueryEngine(
    host="localhost", database="fantasy_books", 
    user="user", password="password"
)

# Combine all databases in one endpoint
serve_union(
    app=app,
    sources={
        "classics": SelectSource(
            query_engine=sqlite_engine,
            select_query="SELECT *, 'classic' as genre FROM books LIMIT %s OFFSET %s",
            count_query="SELECT COUNT(*) FROM books"
        ),
        "fantasy": SelectSource(
            query_engine=pgsql_engine,
            select_query="SELECT *, 'fantasy' as genre FROM books LIMIT %s OFFSET %s",
            count_query="SELECT COUNT(*) FROM books"
        ),
    },
    path="/api/all-books",
    source_priority=["fantasy", "classics"]  # Default priority order
)

Usage:

# Get books from ALL databases in one call
GET /api/all-books?limit=20&offset=0

# Control source priority
GET /api/all-books?limit=20&offset=0&sources=fantasy,classics

Enterprise-Grade with Middleware

from fastapi import Depends, FastAPI, HTTPException
from dbanu import serve_select, SQLiteQueryEngine, QueryContext

app = FastAPI()
query_engine = SQLiteQueryEngine()

# Middleware dependency for authentication
async def get_current_user():
    return {"user_id": 1, "username": "demo_user", "role": "admin"}

# Middleware: Logging
# IMPORTANT: Middleware functions MUST be async
async def logging_middleware(context: QueryContext, next_handler):
    user_info = context.dependency_results.get("get_current_user", {})
    username = user_info.get("username", "anonymous")
    print(f"📝 Request from {username}: {context.filters.model_dump()}")
    return await next_handler(context)

# Middleware: Authorization
# IMPORTANT: Middleware functions MUST be async
async def authorization_middleware(context: QueryContext, next_handler):
    current_user = context.dependency_results.get("get_current_user")
    if not current_user:
        raise HTTPException(status_code=401, detail="Authentication required")
    
    # Role-based access control
    if current_user.get("role") not in ["admin", "editor"]:
        raise HTTPException(status_code=403, detail="Insufficient permissions")
    
    return await next_handler(context)

# Create the secure endpoint
serve_select(
    app=app,
    query_engine=query_engine,
    path="/api/secure/books",
    dependencies=[Depends(get_current_user)],
    middlewares=[logging_middleware, authorization_middleware],
    select_query="SELECT id, title, author FROM books LIMIT %s OFFSET %s"
)

🏗️ Database Engines

SQLite

from dbanu import SQLiteQueryEngine
query_engine = SQLiteQueryEngine(db_path="./database.db")

PostgreSQL

from dbanu import PostgreSQLQueryEngine
query_engine = PostgreSQLQueryEngine(
    host="localhost",
    database="mydb",
    user="user",
    password="password"
)

MySQL

from dbanu import MySQLQueryEngine
query_engine = MySQLQueryEngine(
    host="localhost",
    database="mydb",
    user="user",
    password="password"
)

🚀 Running the Example

Quick Demo

  1. Start the demo environment:
cd example
docker-compose up -d
  1. Run the example server:
python -m example.server
  1. Explore the APIs:
    • Visit http://localhost:8000/docs
    • Test different endpoints:
      • /api/v1/sqlite/books - Classic literature
      • /api/v1/pgsql/books - Fantasy books
      • /api/v1/mysql/books - Science fiction
      • /api/v1/all/books - All books combined!

🧪 Testing

# Install test dependencies
pip install pytest pytest-asyncio

# Run all tests
python -m pytest tests/ -v

📖 API Reference

serve_select Parameters

  • app: FastAPI application instance
  • query_engine: Database query engine (SQLite, PostgreSQL, or MySQL)
  • select_query: SQL SELECT query string or callable function that receives filters
  • select_param: Function to generate query parameters from filters
  • count_query: Optional SQL COUNT query for pagination (string or callable function)
  • path: API endpoint path (default: "/get")
  • filter_model: Pydantic model for filtering
  • data_model: Pydantic model for response data
  • dependencies: List of FastAPI dependencies
  • middlewares: List of middleware functions that receive QueryContext (MUST be async functions)

serve_union Parameters

  • app: FastAPI application instance
  • sources: Dictionary of SelectSource objects for each database
  • path: API endpoint path (default: "/get")
  • filter_model: Pydantic model for filtering (applies to all sources)
  • data_model: Pydantic model for response data
  • dependencies: List of FastAPI dependencies
  • middlewares: List of middleware functions (MUST be async functions)
  • source_priority: List of source names for default priority ordering

📄 License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

dbanu-0.1.0.tar.gz (13.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

dbanu-0.1.0-py3-none-any.whl (18.2 kB view details)

Uploaded Python 3

File details

Details for the file dbanu-0.1.0.tar.gz.

File metadata

  • Download URL: dbanu-0.1.0.tar.gz
  • Upload date:
  • Size: 13.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.2.1 CPython/3.11.0 Linux/6.6.87.2-microsoft-standard-WSL2

File hashes

Hashes for dbanu-0.1.0.tar.gz
Algorithm Hash digest
SHA256 7673f09eb36d0a0753116af208816ac1c72acd5f3f4655431a6122fe7b8ceb44
MD5 8869798bd305290eadbf15ecd10ee3ab
BLAKE2b-256 6f8541028f01e1a52170c82f06a5a50274c24b7000b1866bfdec5f395ae42ab7

See more details on using hashes here.

File details

Details for the file dbanu-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: dbanu-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 18.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.2.1 CPython/3.11.0 Linux/6.6.87.2-microsoft-standard-WSL2

File hashes

Hashes for dbanu-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 79ed8ae8f641f2c95f888940ab4612c6c3b5db4057b50f6b40f9829494e49db6
MD5 942bae12c95d4950ea13ac5ce81a969a
BLAKE2b-256 ca654f3b0c1f5745ea0d8b0c0a5a303043516603f08df45769bbf13b9a192c87

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page