PostgreSQL
Build your data layer with Lily-managed connection pools and Diesel queries.
lily_postgresql manages the pool, TLS, configuration, and connection lifecycle. Schema, entities, and queries belong to Diesel. Using a repository is optional.
Dependencies and connection#
A running PostgreSQL instance and a pre-existing schema are required. Add the postgresql feature to the lilyrs dependency from the first application; it selects single mode. Also declare the Diesel dependency for the repository example:
lilyrs = { version = "0.1.0", features = ["http-api", "postgresql"] }
diesel = { version = "=2.3.11", features = ["postgres"] }[postgresql]
mode = "single"
connection_string = "postgresql://lily:lily@127.0.0.1:5432/lily"
[postgresql.tls]
mode = "disable"
[postgresql.pool]
max_size = 10
acquire_timeout_secs = 5
transaction_cleanup_timeout_secs = 5This connection is for local development. Production configuration validation requires verify-full TLS; disable is rejected. In production, use secret references and appropriate CA/hostname verification. PostgreSQL settings belong under [postgresql]; [database] is for MongoDB.
Choose a data-access path#
| Path | Use |
|---|---|
PgDatabaseService |
Direct Diesel access through with_connection(callback, cancellation) |
PgRepository derive |
Generated CRUD and *_in methods |
PgDbContext |
Share transactions between workflows and repositories within the same request |
The default single mode registers Singleton PgDatabaseService and Scoped PgDbContext. A direct callback receives (&mut AsyncPgConnection, Option<ExecutionCancellation>) and returns Box::pin(async move { ... }). Supported Diesel versions are re-exported by the crate.
Optional repository#
use std::sync::Arc;
use diesel::prelude::*;
use lilyrs::http_api::Injectable;
use lilyrs::http_api::ServiceTrait;
use lilyrs::postgresql::{PgDatabaseService, PgRepository};
diesel::table! {
users (id) {
id -> BigInt,
name -> Text,
}
}
#[derive(Clone, Queryable, Selectable, Identifiable, Insertable, AsChangeset)]
#[diesel(table_name = users)]
#[diesel(check_for_backend(diesel::pg::Pg))]
struct User {
#[diesel(skip_insertion)]
#[diesel(skip_update)]
id: i64,
name: String,
}
#[derive(Default, Injectable, PgRepository)]
#[service(lifetime = "Singleton")]
#[pg(entity = User)]
struct UserRepository {
#[inject]
database: Arc<PgDatabaseService>,
}
impl ServiceTrait for UserRepository {}PgRepository alone does not register a DI service; the example's Injectable and ServiceTrait are also required. Generated methods include create, create_many, find_by_id, find_by_ids, update, delete_by_id, count, exists, and their *_in forms.
Migrations and transactions#
run_pending_migrations is an explicit startup/deployment operation. Do not run DDL for each request. The next page shows a complete transaction spanning a controller and two repository calls.
Multiple databases#
In the facade, replace postgresql with postgresql-factory: features = ["http-api", "postgresql-factory"]. Access named databases through lilyrs::postgresql::PgFactory. The base postgresql feature enables single mode, so do not combine it with factory mode; feature unification with other workspace members can also cause this conflict. Obtain a database with get(name) on the injected factory. Create a named PgDbContext in an application-owned scoped wrapper, and forward wrapper cleanup to the context. Adding a feature to single-mode code is not sufficient; the configuration topology and initializer must also follow the factory model.
Sources for this page v0.1.0
This guide is based on the reviewed framework commit.