March Docs

Depot.Middleware

lib/depot_middleware.march — Depot (PostgreSQL) pool integration for Bastion.

Checks out a database connection at the start of a request and returns it to the pool after the response is sent via the "pool_checkin" after-send hook.

The pool is created once at application startup (in the supervisor) and passed to with_pool/2 in the endpoint pipeline:

-- In the app supervisor (app.march):
let pool = Pool.start(Pool.default_pool_config(conn_cfg))

-- In the endpoint pipeline:
conn
|> Depot.Middleware.with_pool(pool)
|> Session.load(secret_key_base)
|> MyApp.Router.route()

-- In a handler:
match Depot.Middleware.get_conn(conn) do
Some(db) -> Depot.Query.exec_sql(query, db)
None     -> Error("no db connection")
end

The after-send hook "pool_checkin" is dispatched by BastionServer. BastionServer.dispatch_hook/2 must have a case for "pool_checkin": "pool_checkin" -> Depot.Middleware.checkin_from_conn(conn) (This is already wired in bastion_server.march.)

Functions

fncheckin_from_conncheckin_from_conn(conn)#

Return the checked-out connection to the pool.

Called automatically by BastionServer via the `"pool_checkin"` after-send
hook registered by `with_pool/2`.  Safe to call even if no connection was
checked out (no-op in that case).

You should not need to call this directly — it is invoked by the server
after every response when `with_pool/2` was used.
fnget_connget_conn(conn)#

Get the checked-out database connection from the request assigns.

Returns `Some(db_conn)` if a connection was successfully checked out,
or `None` if the pool was exhausted or `with_pool/2` was not called.

    match Depot.Middleware.get_conn(conn) do
    Some(db) -> run_query(db)
    None     -> send_error(conn, 503, "database unavailable")
    end
fnget_poolget_pool(conn)#

Get the pool Pid stored by with_pool/2.

Returns `Some(pool)` if `with_pool/2` was called, `None` otherwise.
fnquery_with_telemetryquery_with_telemetry(conn, sql : String, _params)#

Execute a raw SQL query wrapped in a telemetry span.

Emits `["bastion", "depot", "query", "start"]` and
`["bastion", "depot", "query", "stop"]` around the query, including
the SQL string in metadata.  On error, also emits
`["bastion", "depot", "query", "exception"]`.

    match Depot.Middleware.query_with_telemetry(conn, "SELECT 1", []) do
    Ok(rows) -> ...
    Err(msg) -> ...
    end
fnwith_poolwith_pool(conn, pool)#

Attach a Depot connection pool to the request and check out a connection.

Stores the pool Pid under `"_db_pool"` and the checked-out connection
under `"_db_conn"`.  Registers the `"pool_checkin"` after-send hook so
the connection is automatically returned to the pool after the response.

If checkout fails (pool exhausted, DB unreachable), assigns `None` for
`"_db_conn"` and does NOT register the checkin hook (nothing to return).
Handlers should check `Depot.Middleware.get_conn/1` before using the
connection.

Call early in the request pipeline, typically right after body parsing:

    conn
    |> Depot.Middleware.with_pool(pool)
    |> Session.load(secret_key_base)
    |> MyApp.Router.route()