Skip to main content

Crate djogi

Crate djogi 

Source
Expand description

Djogi — A Model-first web framework for Rust. Define your data schema as Rust structs, and the framework derives everything else: ORM, migrations, admin UI, audit trail, shell bindings, JSONB schema handling. Djogi’s core is web-framework-agnostic — it owns the data layer and delegates HTTP routing/middleware/rendering to whichever Rust web framework the adopter chooses. Axum is the most-supported integration target today and ships behind the opt-in axum feature flag; it is not a core dependency.

§Crate layout at a glance

ModuleRole
configDjogiConfig loaded from Djogi.toml + env (figment).
contextDjogiContext — carries either a pooled handle or active transaction. Replaces E: Executor generics on Model + QuerySet signatures .
descriptorModelDescriptor and friends — the single source of truth about every registered model. Populated by #[model] via inventory::submit!.
errorDjogiError — the one error type returned by every Model method.
modelThe Model trait the macro implements for every user struct. Defined in .
queryFilter AST: public API is Condition + FieldRef (plus QuerySet<T> and OrderExpr). Low-level enums (Leaf, LookupOp, FilterValue) live under djogi::query::internal for advanced/custom emitters. Filled in across .
relationRelation field types — ForeignKey<T>, OneToOneField<T>, resolved-cache wrappers, and OnDelete. Landed in ; extended by Tasks 2–6 with RelationPath, ManyToMany, and the macro glue.
typesDateTime, Date, and re-exports of HeerId/RanjId — the canonical types imported via prelude.
use djogi::prelude::*;

#[model(table = "posts")]
struct Post {
title: String,
body: String,
}

prelude::* brings in the #[model] attribute macro, Model trait, canonical types (DateTime, Date, HeerId, RanjId), and the DjogiError enum — everything a model definition needs.

Re-exports§

pub use migrate::DescriptorProvider;
pub use migrate::InventoryDescriptorProvider;
pub use visage_boundary::DjogiVisageOf;
pub use apps::App;
pub use apps::AppDescriptor;
pub use apps::AppIdentity;
pub use apps::AppRegistry;
pub use apps::CrossAppEdge;
pub use compose::Auditable;
pub use compose::SoftDeletable;
pub use context::DjogiContext;
pub use descriptor::ComputedFieldDescriptor;
pub use descriptor::DefaultVolatility;
pub use descriptor::DeferrabilitySpec;
pub use descriptor::DerivedProjection;
pub use descriptor::EnumDescriptor;
pub use descriptor::FieldDescriptor;
pub use descriptor::FieldSqlType;
pub use descriptor::GeographySubtype;
pub use descriptor::IndexColumnSpec;
pub use descriptor::IndexKind;
pub use descriptor::IndexNameKind;
pub use descriptor::IndexNameTarget;
pub use descriptor::IndexNullsOrder;
pub use descriptor::IndexOrder;
pub use descriptor::IndexSpec;
pub use descriptor::IndexTarget;
pub use descriptor::IndexType;
pub use descriptor::ModelDescriptor;
pub use descriptor::PartitionSpec;
pub use descriptor::PkType;
pub use descriptor::ProtectedFieldMetadata;
pub use descriptor::RangeSubtypeKind;
pub use descriptor::RedactionPolicy;
pub use descriptor::RetentionLabel;
pub use descriptor::RustSourceType;
pub use descriptor::Sensitivity;
pub use descriptor::VisageDescriptor;
pub use descriptor::index_name;
pub use pg::pool::DjogiPool;
pub use hooks::ModelHooks;
pub use jsonb::Jsonb;
pub use jsonb::JsonbPathComparable;
pub use jsonb::JsonbPathRef;
pub use jsonb::JsonbSchema;
pub use jsonb::JsonbSqlCast;
pub use jsonb::MirJzSON;
pub use jsonb::MirJzSONError;
pub use jsonb::UnknownField;
pub use jsonb::UnknownFieldExt;
pub use pg::decode::FromPgRow;
pub use primary_key::PrimaryKey;
pub use primary_key::PrimaryKeyClientGen;
pub use primary_key::PrimaryKeyDbGen;
pub use error::DbError;
pub use error::DjogiError;
pub use expr::AggregateExpr;
pub use expr::Case;
pub use expr::CaseBuilder;
pub use expr::DenseRank;
pub use expr::Exists;
pub use expr::Expr;
pub use expr::HypotheticalSetAgg;
pub use expr::KindEvidence;
pub use expr::MetadataAgg;
pub use expr::OrderedSetAgg;
pub use expr::OuterRef;
pub use expr::QualifyCondition;
pub use expr::QualifyOp;
pub use expr::Rank;
pub use expr::RowNumber;
pub use expr::Subquery;
pub use expr::ValueAgg;
pub use expr::WindowRanking;
pub use expr::grouping_of;
pub use field_codec::FieldCodec;
pub use fts::FtsDescriptor;
pub use fts::TsQuery;
pub use fts::TsVector;
pub use fts_query::FtsFieldRef;
pub use query::AggregateQuery;
pub use query::AnnotatedQuerySet;
pub use query::ArrayPredicate;
pub use query::CachedPortableQuerySet;
pub use query::ClosureModel;
pub use query::ConditionExt;
pub use query::ConflictAction;
pub use query::ConflictColumns;
pub use query::ConflictCondition;
pub use query::ConflictExpr;
pub use query::ConflictTarget;
pub use query::ConflictUpdate;
pub use query::CteQuerySet;
pub use query::DjogiPortableEq;
pub use query::ExcludedRef;
pub use query::ExplicitPgOrderable;
pub use query::FieldRef;
pub use query::FilterClause;
pub use query::InnerLateral;
pub use query::InsertSelectColumn;
pub use query::InsertSelectSource;
pub use query::InsertSelectStmt;
pub use query::IntoAggregateTuple;
pub use query::IntoConflictColumn;
pub use query::IntoConflictCondition;
pub use query::IntoConflictExpr;
pub use query::IntoConflictUpdates;
pub use query::IntoCteBody;
pub use query::IntoFieldFilterValue;
pub use query::IntoFilterValue;
pub use query::IntoInsertColumns;
pub use query::IntoPortableFieldValue;
pub use query::IntoSetOpArm;
pub use query::JoinedAnnotatedQuerySet;
pub use query::JoinedAnnotatedRow;
pub use query::JoinedQuerySet;
pub use query::LateralQuerySet;
pub use query::LeftLateral;
pub use query::Lookup;
pub use query::MaterializeClosureOptions;
pub use query::MaterializeClosureReport;
pub use query::MergeCounts;
pub use query::MergeStmt;
pub use query::ModelCursorStream;
pub use query::ModelFilter;
pub use query::OnConflictClause;
pub use query::OrderExpr;
pub use query::PairClosureKinshipSum;
pub use query::PairOrderExpr;
pub use query::PairSide;
pub use query::PairWindowExt;
pub use query::PortableQuerySet;
pub use query::Q;
pub use query::QuerySet;
pub use query::RawCursorStream;
pub use query::RecursiveArm;
pub use query::RecursiveDirection;
pub use query::RecursiveQuerySet;
pub use query::SetOpKind;
pub use query::SetOpQuerySet;
pub use query::UpdateAssignment;
pub use query::UpdateStmt;
pub use query::VisageExists;
pub use query::VisageQuerySet;
pub use relation::ForeignKey;
pub use relation::ForeignKeyResolved;
pub use relation::JoinedRow;
pub use relation::ManyToMany;
pub use relation::OnDelete;
pub use relation::OneToOneField;
pub use relation::OneToOneFieldResolved;
pub use relation::PrefetchedRow;
pub use tracked::Tracked;
pub use types::Date;
pub use types::DateTime;
pub use types::Interval;
pub use types::Range;
pub use types::RangeBound;
pub use visage::DjogiVisage;
pub use visage::VisageError;

Modules§

apps
Compile-time schema ownership domains — the apps subsystem. Users declare apps once per crate via the djogi::apps! function-like proc macro:
array
Array column operator helpers — contains, contained_by, overlap, len.
auth
Authentication substrate for Djogi.
cache
Re-exports sassi cache primitives so adopters can use djogi::cache::*; without an explicit sassi dep in their Cargo.toml. Spec: docs/spec/maahi/caching.md (“Why sassi”).
compose
Composition primitives — Auditable and SoftDeletable. These are the runtime trait surfaces a model picks up when adopters opt in via #[model(auditable)] (supersedes the legacy #[derive(Auditable)] per spec line 1037, locked 2026-05-03) or #[model(soft_deletable)] (supersedes the legacy #[derive(SoftDeletable)] for symmetry with the auditable surface and to de-risk automatic default-filter composition). Initial landing covered trait shapes only; the full macro emissions are the source of truth for behavior. Downstream code that only needs to bound a generic on “models with audit fields” or “models with soft-delete semantics” can import these traits today.
config
Configuration via Djogi.toml + environment variables. DATABASE_URL env var always overrides [database].url. Secrets live in env vars, never in Djogi.toml.
context
The DjogiContext type — carries either a pooled handle or an active transaction. Per v3 specification, DjogiContext replaces the E: Executor generic on every Model CRUD and QuerySet method signature. This change unifies the API: the same method can be called against a pool or inside a transaction without reborrows or type juggling.
descriptor
Runtime model descriptors — emitted by #[model] via inventory, consumed by the migration system, djogi docs, RLS generation, and the partitioning analyzer .
enum_
Runtime glue for #[derive(DjogiEnum)]. Most of the codec logic (ToSql / FromSql impls) is generated per-enum by the proc macro. This module holds shared error types and re-exports that complete the runtime surface.
error
The single error type returned by every framework CRUD operation. DjogiError wraps the sources of failure that can occur when a Model method runs: database driver errors, expected-row-count violations, and ID generation failures. Keeping one error type at the public API makes ?-propagation ergonomic: user code calls Post::get(&pool, id).await? and gets a DjogiError without having to juggle per-subsystem errors. The variants correspond 1:1 to the failure modes the generated CRUD impls can produce:
expr
Typed expression IR — the substrate for field-vs-field filters, arithmetic assignments, aggregates, and subqueries.
field_codec
Field-level codecs for at-rest transformations of protected fields. Trait + compile-time registry surface.
fts
Full-text search types — TsVector and TsQuery.
fts_query
FTS query builder types — FtsFieldRef with .matches() and .rank().
hooks
Lifecycle hooks for Model CRUD operations. Six methods, each defaulted to a no-op that returns Ok(()). Adopters impl ModelHooks for MyModel selectively — methods they don’t override stay no-op. The HasHooks marker trait is sealed and emitted by the macro layer (#[model(hooks)]) to gate monomorphic dispatch in the CRUD terminals.
intent
IntentFile reader + precedence resolver. Adopters ship .djogi/intent.json at the workspace root carrying human-readable rationale for models and fields. The file is side-channel — its contents never reach the schema, migrations, or runtime; only djogi docs reads it, merging the rationale into the generated Markdown. Absent file ⇒ Ok(None) from load. I/O and parse errors are hard Errs so a typo doesn’t silently disable rationale rendering. Resolver precedence (resolve_model_rationale / resolve_field_rationale): macro attribute wins, intent.json falls back.
jsonb
Jsonb<T> — typed JSONB column wrapper with unknown-field preservation.
live_migrate
Live-migration layer — skeleton. live_migrate is the home for operator-driven compatibility-window rollouts (the spec’s expand → backfill → flip → contract sequence). It sits above the segment planner: every type that flows through this module is imported from crate::migrate rather than redefined here. The boundary between the two layers is the OnlineSafetyClassification enum frozen in migrate::schema.
migrate
Migration system — the home for everything that takes ModelDescriptor inventory and lowers it to executable Postgres migrations. The module fans out to several concerns:
model
The Model trait — the contract every #[model] struct satisfies. All CRUD methods take &mut DjogiContext, so the same call site works against a pool or inside a transaction without re-borrows or type juggling:
outbox
Transactional outbox — (write side) + (worker side). Models annotated with #[model(events)] emit one outbox row into {table}_outbox on every successful create, save, delete, update_returning_pair, or delete_returning performed through a DjogiContext. Because the outbox INSERT shares the same context (and therefore the same active transaction), the outbox row commits or rolls back atomically with the primary write — no separate “event publisher” fan-out is required at the write side. A downstream poller / CDC consumer drains {table}_outbox asynchronously.
pg
Internal Postgres substrate — SqlAccumulator, DjogiPool, and PgConnection. This module is pub(crate) — it is an implementation detail of the framework, not part of the public API. Macro-emitted code routes through ::djogi::__private::pg (re-exported from lib.rs), not through ::djogi::pg directly.
pg_types
Postgres typed-surface newtypes —). This module owns the Rust newtypes that wrap Postgres column types postgres-types does not encode natively (or whose native encoding does not fit the typed surface djogi wants to expose). Each newtype ships its own postgres_types::{ToSql, FromSql} impl against the raw Postgres wire format — djogi pulls in no third-party crate to bridge these types.
prelude
The canonical adopter import. use djogi::prelude::*; brings the framework’s adopter-facing surface into scope in a single line: every type a model definition or CRUD call site needs, every macro the model surface depends on, and the canonical type re-exports (HeerId, RanjId, Date, DateTime).
presentation
Per-scope presentation codecs for protected fields. This module provides the runtime trait surface for the #[field(protected(per_scope = { ... }))] presentation-codec feature (GH #227). A presentation codec transforms a model’s stored field value at visage-projection time — after at-rest decode and before the visage is serialized or returned to the caller.
primary_key
Primary-key trait surface. Three-trait split per docs/spec/decisions.md:
query
Query API — lazy QuerySet<T>, typed filters, SQL emission. The public surface is re-exported at crate root and in prelude: users write use djogi::prelude::*; and get QuerySet, FieldRef, Lookup, Q, etc. without a second import. Internally: queryset holds the builder state, condition the filter tree (the legacy substrate, retired in favour of Q<T>), q the public Q-algebra, field the typed column handles, order ordering expressions, filter the programmatic-builder types, update bulk-update assignments, sql the ConditionBuilder + SQL emitters, and terminal the fetch_* methods. Splitting by responsibility keeps each file auditable.
range
Postgres range predicate payloads. Range<T> itself lives in crate::pg_types. This module owns the query-side payloads for PostgreSQL range operators so query::condition and query::field can share a small typed representation without storing SQL fragments in the condition tree. Range predicates are SQL-only. They are exposed from root model fields through explicit_pg_predicate() because Postgres range canonicalization and operator semantics are not portable to Punnu/Rust evaluation.
relation
Relation field types and (later) relation-aware query extensions. Lands the runtime wrappers only:
snapshot
Migration snapshot integrity primitives. migrations/schema_snapshot.json records the schema-of-record after every successful djogi migrations apply. The runner persists it atomically the descriptor differ then reads it back on the next cargo build to decide whether to emit a new pending migration. If a snapshot file is tampered with (e.g. someone hand-edits it to suppress a drift warning, or a corrupted CI cache resurfaces a stale copy), the differ will silently mis-classify drift and the migration history goes out of sync with the live schema. The snapshot::sign submodule (this module’s first inhabitant) provides the HMAC-SHA256 sign-and-verify primitives that detect such tampering. The higher-level surfaces built on this module:
testing
Test-harness runtime helpers for #[djogi_test]. This module provides the per-test-database lifecycle machinery used by the #[djogi_test] proc-macro attribute:
tracked
Dirty-tracking wrapper for model fields — Tracked<T>.
trait_registry
Trait registry for cross-type queries via #[djogi::trait_impl].
transaction
atomic(...) — the canonical transaction scope + retry helper.
types
Core type aliases and re-exports used in #[model] struct definitions. Every #[model] struct pulls these in via djogi::prelude::*, so users never need per-field imports for the framework’s canonical types. The aliases (DateTime, Date) pin the project to the time crate — the framework explicitly avoids chrono (see CLAUDE.md “Dependencies excluded”).
visage
Visage runtime — trait surface, error types, and sealed projection metadata for generated visage structs. #[model] emits four visage structs per model ({Model}Public, {Model}SelfView, {Model}Admin, {Model}Export) plus conversion impls. This module holds the runtime-side types those emissions depend on:
visage_boundary
Sealed boundary marker trait for visage projections. impl DjogiVisageOf<M> for V asserts that V is a visage (or the model itself) projecting fields from model M. The reflexive blanket impl<M: Model> DjogiVisageOf<M> for M lets model-scoped code that expects “something that is-or-projects-M” accept either the raw model or one of its visages uniformly.

Macros§

apps
Declare the crate’s compile-time schema ownership domains. djogi::apps! takes a block of unit-struct declarations, each carrying an #[app(...)] attribute describing the database target and (optionally) an explicit label:
link_anchor
Emit a once-per-crate linkage anchor so an adopter binary can force link-time retention of THIS crate’s #[derive(Model)] registrations without listing every model type (#370, branch b).
many_to_many
Emit one direction of a many-to-many relation — the ManyToMany<Target> trait impl, the named inherent accessor on the source type, and an inventory marker for . Invocation form:
primary_key
Declarative-style macro for declaring custom primary-key types. Emits a pub struct <Name>(<Inner>); newtype plus the trait impls the #[model(pk = <Name>)] attribute relies on — PrimaryKey (with its KIND / SQL_TYPE / DEFAULT_SQL associated consts), ToSql / FromSql delegation to the inner type, and optionally PrimaryKeyDbGen (when bulk_sql = "..." is set) or PrimaryKeyClientGen (when generate = |...| expr is set).
reverse_one_to_many
Emit a reverse one-to-many accessor on a model. Invocation form:
reverse_one_to_one
Emit a reverse one-to-one accessor on a model. Invocation form:

Structs§

HeerId
64-bit time-ordered identifier with ascending raw-bit order.
HeerIdDesc
64-bit time-ordered identifier with descending raw-bit order; the reverse-chronologically-sorted sibling of HeerId.
HeerIdRecencyBiased
64-bit time-ordered identifier with descending raw-bit order; the reverse-chronologically-sorted sibling of HeerId.
RanjId
128-bit UUIDv8 time-ordered identifier with ascending raw-bit order.
RanjIdDesc
128-bit UUIDv8 time-ordered identifier with descending raw-bit order; the reverse-chronologically-sorted sibling of RanjId.
RanjIdRecencyBiased
128-bit UUIDv8 time-ordered identifier with descending raw-bit order; the reverse-chronologically-sorted sibling of RanjId.

Enums§

BasicPredicate
Universal predicate algebra over T.

Type Aliases§

Result
Crate-scoped Result alias — Result<T, DjogiError>. Every fallible Djogi operation returns a DjogiError from a single public error type, so adopter code can name the success type alone:

Attribute Macros§

deliberately_bypass_convention_with_raw_sql
Brings djogi::__bypass::{RawAccessExt, RawPoolAccessExt} into scope for the decorated item, unlocking direct access to djogi’s raw SQL escape hatches at explicit, auditable sites.
djogi_test
Per-test database lifecycle harness. Transforms an async fn my_test(ctx: DjogiContext) into a plain #[test] wrapper that builds a Tokio runtime through djogi and:
trait_impl
#[djogi::trait_impl] — trait-registry attribute. Wraps a trait impl block so cross-cutting consumers (Sassi::all_impl::<dyn T>) can iterate every model that implements the trait at runtime without enumerating each impl site by hand.

Derive Macros§

DjogiEnum
Derive typed Postgres enum support. Emits postgres_types::ToSql + FromSql impls that encode/decode the enum as its mapped Postgres string label, plus an inventory::submit! of an EnumDescriptor for the migration differ.
JsonbSchema
Derive the typed JSONB deep-path API for a schema struct. Applying this derive to a named struct causes the macro to emit a {T}Path<M> struct with one method per field. Scalar fields (from the cast-matrix allowlist: i16, i32, i64, f32, f64, bool, String, time::OffsetDateTime, time::Date, uuid::Uuid, rust_decimal::Decimal, serde_json::Value, HeerId, RanjId) return a JsonbPathRef<M, FieldType> ready for comparison. All other field types are assumed to implement JsonbSchema and their method returns the nested type’s Path<M> with the path accumulator extended.
Model
No-op stub — field injection requires #[model] (attribute macro). Kept as a placeholder for future derive-based extensions, and as the registration site for helper attributes that adopters write on the derived struct. NOTE: Only field and derived are listed as helper attributes here, not model. Listing model as a helper would shadow the #[model] proc_macro_attribute and cause ambiguous resolution (Post-Review Fix #4).