Sanic

SQLSpec provides a Sanic extension that uses Sanic-native application and request context. Connection pools are stored on app.ctx during the worker lifecycle, and request-scoped connections and sessions are stored on request.ctx.

Installation

Install SQLSpec with the Sanic extra:

uv add "sqlspec[sanic]"
pip install "sqlspec[sanic]"
poetry add "sqlspec[sanic]"
pdm add "sqlspec[sanic]"

Basic Setup

Create a SQLSpec instance, register your database config, and attach SQLSpecPlugin to your Sanic app. Configure Sanic-specific options under extension_config["sanic"].

sanic basic setup
from sanic import Request, Sanic, response
from sanic.response import HTTPResponse

from sqlspec import SQLSpec
from sqlspec.adapters.aiosqlite import AiosqliteConfig, AiosqliteDriver
from sqlspec.extensions.sanic import SQLSpecPlugin

sqlspec = SQLSpec()
sqlspec.add_config(
    AiosqliteConfig(
        connection_config={"database": ":memory:"},
        extension_config={"sanic": {"commit_mode": "manual", "session_key": "db"}},
    )
)

db_plugin = SQLSpecPlugin(sqlspec)
app = Sanic("SQLSpecSanicBasicExample")

@app.get("/health")
async def health(request: Request) -> HTTPResponse:
    db: AiosqliteDriver = db_plugin.get_session(request, "db")
    result = await db.execute("select 1 as ok")
    return response.json(result.one())

db_plugin.init_app(app)

Transaction Modes

Sanic supports the same transaction modes as the other request-oriented framework integrations:

manual

SQLSpec manages connection scope only. Your handler commits or rolls back.

autocommit

SQLSpec commits 2xx responses and rolls back other responses.

autocommit_include_redirect

SQLSpec commits 2xx and 3xx responses and rolls back other responses.

Extra commit and rollback statuses are configured per database config.

sanic commit modes
from sanic import Request, Sanic, response
from sanic.response import HTTPResponse

from sqlspec import SQLSpec
from sqlspec.adapters.aiosqlite import AiosqliteConfig
from sqlspec.extensions.sanic import SQLSpecPlugin

sqlspec = SQLSpec()
sqlspec.add_config(
    AiosqliteConfig(
        connection_config={"database": "app.db"},
        extension_config={
            "sanic": {"commit_mode": "autocommit", "extra_rollback_statuses": {409}, "session_key": "db"}
        },
    )
)

db_plugin = SQLSpecPlugin(sqlspec)
app = Sanic("SQLSpecSanicCommitModesExample")

@app.post("/users")
async def create_user(request: Request) -> HTTPResponse:
    db = db_plugin.get_session(request, "db")
    await db.execute("insert into users (name) values (:name)", {"name": request.json["name"]})
    return response.json({"created": True}, status=201)

db_plugin.init_app(app)

Multiple Databases

For multiple databases, give each config unique connection_key, pool_key, and session_key values. The plugin can then look up sessions by session key.

sanic multi database
from sanic import Request, Sanic, response
from sanic.response import HTTPResponse

from sqlspec import SQLSpec
from sqlspec.adapters.aiosqlite import AiosqliteConfig, AiosqliteDriver
from sqlspec.adapters.sqlite import SqliteConfig, SqliteDriver
from sqlspec.extensions.sanic import SQLSpecPlugin

sqlspec = SQLSpec()
sqlspec.add_config(
    AiosqliteConfig(
        bind_key="primary",
        connection_config={"database": "primary.db"},
        extension_config={
            "sanic": {
                "connection_key": "primary_connection",
                "pool_key": "primary_pool",
                "session_key": "primary_db",
            }
        },
    )
)
sqlspec.add_config(
    SqliteConfig(
        bind_key="analytics",
        connection_config={"database": "analytics.db"},
        extension_config={
            "sanic": {
                "connection_key": "analytics_connection",
                "pool_key": "analytics_pool",
                "session_key": "analytics_db",
            }
        },
    )
)

db_plugin = SQLSpecPlugin(sqlspec)
app = Sanic("SQLSpecSanicMultiDatabaseExample")

@app.get("/report")
async def report(request: Request) -> HTTPResponse:
    primary: AiosqliteDriver = db_plugin.get_session(request, "primary_db")
    analytics: SqliteDriver = db_plugin.get_session(request, "analytics_db")
    users = await primary.select("select 1 as id, 'Alice' as name")
    metrics = analytics.select("select 'active_users' as name, 100 as value")
    return response.json({"users": users, "metrics": metrics})

db_plugin.init_app(app)

Request Access

Use plugin.get_session(request, key=None) for driver sessions and plugin.get_connection(request, key=None) for raw connection access. The default key is the first registered config's session_key; passing a key is recommended when an app has multiple configs.

get_connection_from_request(request, config_state) and get_or_create_session(request, config_state) are lower-level helpers for custom integrations.

disable_di

Set disable_di=True when another dependency injection system owns request-scoped connection management. SQLSpec still creates and closes pools on Sanic startup and shutdown, but it does not put connections or sessions on request.ctx.

sanic disable di
from sanic import Request, Sanic, response
from sanic.response import HTTPResponse

from sqlspec import SQLSpec
from sqlspec.adapters.aiosqlite import AiosqliteConfig
from sqlspec.extensions.sanic import SQLSpecPlugin

sqlspec = SQLSpec()
config = AiosqliteConfig(
    connection_config={"database": "app.db"},
    extension_config={"sanic": {"disable_di": True, "pool_key": "db_pool"}},
)
sqlspec.add_config(config)

db_plugin = SQLSpecPlugin(sqlspec)
app = Sanic("SQLSpecSanicDisableDIExample")

@app.get("/health")
async def health(request: Request) -> HTTPResponse:
    async with config.provide_connection(request.app.ctx.db_pool) as connection:
        db = config.driver_type(connection=connection, statement_config=config.statement_config)
        result = await db.execute("select 1 as ok")
        return response.json(result.one())

db_plugin.init_app(app)

Correlation IDs and SQLCommenter

Set enable_correlation_middleware=True to extract request correlation IDs from headers and return X-Correlation-ID on the response. The active value is also available from request.ctx.correlation_id and CorrelationContext during the request.

SQLCommenter is enabled when the driver StatementConfig has enable_sqlcommenter=True and the Sanic config keeps enable_sqlcommenter_middleware=True. Request attributes include framework="sanic", route, and action.

sanic observability
from sanic import Request, Sanic, response
from sanic.response import HTTPResponse

from sqlspec import SQLSpec
from sqlspec.adapters.aiosqlite import AiosqliteConfig
from sqlspec.core import StatementConfig
from sqlspec.extensions.sanic import SQLSpecPlugin

sqlspec = SQLSpec()
sqlspec.add_config(
    AiosqliteConfig(
        connection_config={"database": "app.db"},
        statement_config=StatementConfig(enable_sqlcommenter=True),
        extension_config={
            "sanic": {
                "enable_correlation_middleware": True,
                "enable_sqlcommenter_middleware": True,
                "session_key": "db",
            }
        },
    )
)

db_plugin = SQLSpecPlugin(sqlspec)
app = Sanic("SQLSpecSanicObservabilityExample")

@app.get("/health")
async def health(request: Request) -> HTTPResponse:
    return response.json({"correlation_id": request.ctx.correlation_id})

db_plugin.init_app(app)