From litestar
Type-safe SQL query mapper for Python with 15+ database adapters, query builder, SQL file loading, and framework integrations (Litestar, FastAPI, Flask, Starlette).
How this skill is triggered — by the user, by Claude, or both
Slash command
/litestar:sqlspecThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
SQLSpec is a **type-safe SQL query mapper for Python** -- NOT an ORM. It provides flexible connectivity with consistent interfaces across 15+ database adapters. Write raw SQL, use the builder API, or load SQL from files. All statements pass through a sqlglot-powered AST pipeline for validation and dialect conversion.
references/adapters.mdreferences/adk.mdreferences/architecture.mdreferences/arrow.mdreferences/commit-modes.mdreferences/data-dictionary.mdreferences/dishka-integration.mdreferences/driver_api.mdreferences/events.mdreferences/extensions.mdreferences/fastapi-integration.mdreferences/filters.mdreferences/flask-integration.mdreferences/loader.mdreferences/migrations.mdreferences/multi-database.mdreferences/observability.mdreferences/patterns.mdreferences/query_builder.mdreferences/service-patterns.mdSQLSpec is a type-safe SQL query mapper for Python -- NOT an ORM. It provides flexible connectivity with consistent interfaces across 15+ database adapters. Write raw SQL, use the builder API, or load SQL from files. All statements pass through a sqlglot-powered AST pipeline for validation and dialect conversion.
sqlspec ships first-party extensions for four web frameworks. If your project uses one of these, jump directly to the matching integration guide and skip the others:
SQLSpecPlugin with full DI, CLI, observability. The rest of this SKILL.md covers Litestar by default; also see references/extensions.md.references/fastapi-integration.md — Depends(plugin.provide_session()) DI, Annotated[...] handlers, filter providers.references/flask-integration.md — plugin.init_app(app), pull-based plugin.get_session(), async-via-portal.references/starlette-integration.md — request.state-based session access, lifespan wrapping, middleware variants.sqlspec has no first-party Sanic integration — other frameworks are not supported out-of-the-box.
Shared topics that apply to every framework live in references/commit-modes.md (autocommit / manual middleware) and references/multi-database.md (multi-config registry). Read the framework guide first, then those for depth.
The rest of this SKILL.md covers framework-agnostic topics: adapter setup, query builder, driver methods, filters, observability, migrations, the ADK extension, and data-dictionary introspection.
from __future__ import annotations rule — SQLSpec adapter config modules and driver definitions avoid from __future__ import annotations because configs are introspected at runtime. Consumer application modules (handlers, services, tests that use a configured driver) MAY and typically SHOULD use it — canonical Litestar apps use it in 100+ files.from sqlspec.adapters.asyncpg import AsyncpgConfig, AsyncpgDriver
# Configure the adapter with connection details
config = AsyncpgConfig(
connection_config={
"dsn": "postgresql://user:pass@localhost:5432/mydb",
"min_size": 2,
"max_size": 10,
},
)
# Use the driver as a context manager for connection lifecycle
async with config.create_driver() as db:
users = await db.select_many(
"SELECT * FROM users WHERE active = $1",
[True],
schema_type=User,
)
from sqlspec import sql
# SELECT with filters
stmt = (
sql.select("id", "name", "email")
.from_("users")
.where_eq("status", "active")
.where("created_at > :since", since=cutoff_date)
.order_by("created_at", desc=True)
.limit(50)
.to_statement()
)
# INSERT
stmt = (
sql.insert_into("users")
.columns("name", "email")
.values(name="Alice", email="[email protected]")
.to_statement()
)
# MERGE / upsert
stmt = (
sql.merge_("inventory")
.using("updates", on="inventory.product_id = updates.product_id")
.when_matched().do_update(qty="updates.qty")
.when_not_matched().do_insert(product_id="updates.product_id", qty="updates.qty")
.to_statement()
)
| Method | Returns | Use Case |
|---|---|---|
select_value() | Single scalar | COUNT(*), MAX(), existence checks |
select_one() | One row (strict) | Get-by-ID, raises NotFoundError |
select_one_or_none() | One row or None | Optional lookup |
select_many() | List of rows | Filtered queries, listing |
select_to_arrow() | pyarrow.Table | Bulk data export, analytics |
execute() | Row count | INSERT/UPDATE/DELETE |
execute_many() | Row count | Batch operations |
# Zero-copy on DuckDB, ADBC adapters; conversion on others
arrow_table = await db.select_to_arrow(
"SELECT * FROM large_dataset WHERE region = $1", [region]
)
# Bulk load from Arrow
await db.copy_from_arrow(arrow_table, target_table="users")
| Need | Adapter | Key Feature |
|---|---|---|
| PostgreSQL async | asyncpg, psycopg | Async, NUMERIC/PYFORMAT params |
| PostgreSQL sync | psycopg | Sync+async, PYFORMAT params |
| SQLite | sqlite, aiosqlite | QMARK params, local dev |
| DuckDB analytics | duckdb | Arrow-native, zero-copy |
| MySQL async | asyncmy | PYFORMAT params |
| Oracle | oracledb | NAMED_COLON params, sync+async |
| BigQuery / Spanner | bigquery, spanner | NAMED_AT params |
| Raw SQL strings | Driver methods | select_many(), execute() |
| Dynamic queries | Query builder | sql.select()...to_statement() |
| SQL from files | SQLFileLoader | Metadata directives, caching |
create_driver() context manager for connection lifecycleschema_type parameter for typed results (Pydantic or msgspec models)LimitOffsetFilter, OrderByFilter, SearchFilterRun through the validation checkpoint below before considering the work complete.
schema_type for query results -- get typed objects, not raw dictsasync with config.create_driver() as db:SQLFileLoader for static queries -- keeps SQL out of Python, enables caching$1 for asyncpg, %s for psycopg, ? for sqlite, :name for oracledbfrom __future__ import annotations. Consumer app modules MAY use it.Before delivering SQLSpec code, verify:
sqlspec.adapters.<name>)create_driver() context managerschema_type for type-safe mappingLimitOffsetFilter, etc.) not manual LIMIT/OFFSETTask: "Set up an asyncpg adapter, define a typed model, and execute a parameterized query with pagination."
from dataclasses import dataclass
from sqlspec.adapters.asyncpg import AsyncpgConfig
from sqlspec.core.filters import LimitOffsetFilter, OrderByFilter
# --- Typed model ---
@dataclass
class User:
id: int
name: str
email: str
active: bool
# --- Adapter setup ---
config = AsyncpgConfig(
connection_config={
"dsn": "postgresql://user:pass@localhost:5432/mydb",
"min_size": 2,
"max_size": 10,
},
)
# --- Query execution ---
async def list_active_users(page: int = 1, page_size: int = 25) -> list[User]:
filters = [
OrderByFilter(columns=[("name", "asc")]),
LimitOffsetFilter(limit=page_size, offset=(page - 1) * page_size),
]
async with config.create_driver() as db:
users = await db.select_many(
"SELECT id, name, email, active FROM users WHERE active = $1",
[True],
*filters,
schema_type=User,
)
return users
async def get_user_count() -> int:
async with config.create_driver() as db:
count = await db.select_value(
"SELECT COUNT(*) FROM users WHERE active = $1", [True]
)
return count
The sql factory provides a fluent builder API with full method chaining. All builders terminate with .to_statement() and pass through sqlglot for validation and dialect conversion.
| Builder | Entry Point | Key Methods |
|---|---|---|
| SELECT | sql.select(*cols) | .from_(), .where(), .where_eq(), .join(), .order_by(), .limit(), .offset() |
| INSERT | sql.insert_into(table) | .columns(), .values(), .returning() |
| UPDATE | sql.update(table) | .set_(), .where(), .returning() |
| DELETE | sql.delete_from(table) | .where(), .returning() |
| MERGE | sql.merge_(target) | .using(), .when_matched(), .when_not_matched() |
| CREATE TABLE | sql.create_table(name) | .column(), .primary_key(), .if_not_exists() |
| DROP TABLE | sql.drop_table(name) | .if_exists(), .cascade() |
select_to_arrow() returns an Apache Arrow Table for bulk and analytical workloads:
copy_from_arrow(table, target_table) for bulk loads back into the databaseSQLSpec filter objects are passed directly to driver methods alongside the SQL string. They modify the statement before execution.
| Filter | Purpose | Example Use |
|---|---|---|
BeforeAfterFilter | Date range bounds (before, after) | Audit log queries, time-range pagination |
InCollectionFilter | SQL IN (...) clause | Filter by a set of IDs or enum values |
LimitOffsetFilter | Page-based pagination | limit=25, offset=50 |
OrderByFilter | Dynamic sort columns and direction | User-supplied sort fields |
SearchFilter | Text search (ILIKE / LIKE) | Full-text style search on string columns |
Filters are composable — pass multiple to a single select_many() call and they are applied in order.
| Framework | Integration | Key Feature |
|---|---|---|
| Litestar | SQLSpecPlugin | Dependency injection of typed driver; auto session lifecycle |
| FastAPI / Starlette | Middleware | Request-scoped connection; injects driver into route dependencies |
| Flask | Extension | init_app() pattern; driver available via g or current_app |
SQLSpecPlugin for Litestar registers the driver as a DI provider — inject it into route handlers via type annotation without manual context management.
For databases that support server-side pub/sub (e.g., PostgreSQL LISTEN/NOTIFY):
AsyncEventChannel to subscribe to named channelsNOTIFY channel, payload from SQL or from the publish() methodEventMessage objects with channel name, payload, and PIDSQL object holds all state for a given statementSQL object return new instancesChoosing between
sqlspecandadvanced-alchemy:advanced-alchemygives you an opinionated ORM service layer withUUIDAuditBase, lifecycle hooks, repository / service / Alembic integration, andOffsetPagination[T]out of the box — pick it when you want a complete CRUD surface with attribute-style row access and you're happy inside the SQLAlchemy ecosystem.sqlspecgives you direct SQL control, 15+ driver adapters (asyncpg, oracledb, DuckDB, BigQuery, SQLite, and more), Arrow-native result streams for analytics, and a builder API when you need it — pick it when you want explicit SQL, heterogeneous database backends, or Arrow integration. Both skills integrate with Litestar via first-party plugins; see../advanced-alchemy/SKILL.mdfor the ORM path.
For detailed instructions, patterns, and API guides, refer to the following documents:
copy=False pattern.get_tables, get_columns, get_indexes), driver-side metadata API.sql factory: select, insert, update, delete, merge.select_value(), select_one(), select_many(), select_to_arrow().LimitOffsetFilter, OrderByFilter, SearchFilter.select_to_arrow() zero-copy, copy_from_arrow() bulk loading.SQLFileLoader with search paths, metadata directives.AsyncEventChannel, subscribe/publish patterns.SQLSpecSessionService, SQLSpecMemoryService, SQLSpecArtifactService, per-adapter ADK stores.sqlspec database CLI, timestamp versioning, ddl_migrations tracker, extension migrations, Litestar litestar db integration.npx claudepluginhub litestar-org/litestar-skills --plugin litestarSearches MemPalace before answering questions about past work, people, projects, or prior decisions. Returns verbatim stored content instead of guessing from model memory.
Guides Payload CMS config (payload.config.ts), collections, fields, hooks, access control, APIs. Debugs validation errors, security, relationships, queries, transactions, hook behavior.
Implements vector databases with Pinecone, Weaviate, Qdrant, Milvus, pgvector for semantic search, RAG, recommendations, and similarity systems. Optimizes embeddings, indexing, and hybrid search.