AppContext & prelude
AppContext is the cloneable, axum-State-compatible struct that carries every shared resource of a Loco app (DB connection, cache, queue, mailer, storage, config, and an open-ended DI slot). loco_rs::prelude is the single-import surface app code pulls in instead of naming individual loco_rs and axum paths. Both are declared in src/app.rs and src/prelude.rs.
AppContext
Defined at src/app.rs:253-273:
#[derive(Clone, FromRef)]pub struct AppContext { pub environment: Environment, #[cfg(feature = "with-db")] pub db: DatabaseConnection, pub queue_provider: Option<Arc<bgworker::Queue>>, pub config: Config, pub mailer: Option<EmailSender>, pub storage: Arc<Storage>, pub cache: Arc<cache::Cache>, pub shared_store: Arc<SharedStore>,}Derives
#[derive(Clone, FromRef)] (src/app.rs:253). FromRef (from axum) auto-generates impl FromRef<AppContext> for <FieldType> for each field, so a handler can extract a single field directly — e.g. State<DatabaseConnection> or State<Arc<cache::Cache>> — instead of always taking the whole State<AppContext>.
Fields
| Field | Type | Feature gate | Purpose |
|---|---|---|---|
environment | Environment | none | Which profile the app booted under (Production / Development / Test / Any(String)); drives config-file selection. |
db | DatabaseConnection (Sea-ORM 2.0) | #[cfg(feature = "with-db")] | The pooled Sea-ORM database connection used by every entity query and by db::converge migrations. Absent entirely from the struct when with-db is off. |
queue_provider | Option<Arc<bgworker::Queue>> | none | The background-job queue (Redis / Postgres / SQLite / in-process), if the app was booted with one wired up. None for queue-less apps. |
config | Config | none | The fully loaded, deserialized config/<environment>.yaml (+ .local.yaml overlay). |
mailer | Option<EmailSender> | none | The configured email-sending backend (SMTP or stub), if the app enabled one. |
storage | Arc<Storage> | none | The file/object storage abstraction (local disk or a cloud backend selected by the storage_* feature flags). |
cache | Arc<cache::Cache> | none | The cache handle (in-memory, Redis, or null backend per cache_* flags / CacheConfig). |
shared_store | Arc<SharedStore> | none | A TypeId-keyed, concurrent DI container (backed by DashMap) for stashing arbitrary app-defined services — see below. |
db is the only field that is compiled out (not just None-able) when its feature (with-db) is disabled — every other field is unconditionally present, with Option/empty-default standing in for “not configured.”
SharedStore — the generic DI slot
shared_store: Arc<SharedStore> (src/app.rs:33-245, 272) is a small heterogeneous store for services that don’t have a dedicated AppContext field. Its API:
insert<T: 'static + Send + Sync>(&self, val: T)(:62)remove<T>(&self) -> Option<T>(:103)get_ref<T>(&self) -> Option<RefGuard<'_, T>>(:151) — borrowed access via aDeref<Target = T>guardget<T: Clone + 'static + Send + Sync>(&self) -> Option<T>(:195) — cloning accesscontains<T>(&self) -> bool(:221)
To read a stashed value from inside a handler, use the extractor of the same name: controller::extractor::shared_store::SharedStore<T>(pub T), which implements FromRequestParts<AppContext> and returns Error::InternalServerError if T was never inserted (src/controller/extractor/shared_store.rs:6-29).
Naming note: app::SharedStore (the container held on AppContext) and the extractor controller::extractor::shared_store::SharedStore<T> (the axum extractor) are two distinct types that share a name. Both are reachable through the prelude — see below — so disambiguate by context: the field type is the store, the tuple-struct-with-generic is the extractor.
loco_rs::prelude
use loco_rs::prelude::*; (src/prelude.rs) is the standard single import for app code (controllers, models, workers, tasks). It re-exports, unconditionally unless noted:
Async / axum plumbing
async_trait::async_traitaxum::debug_handleraxum::extract::{Form, Multipart, Path, Query, State}axum::response::{IntoResponse, Response}axum::routing::{delete, get, head, options, patch, post, put, trace}axum_extra::extract::cookie
Third-party helpers
chrono::NaiveDateTime as DateTimeinclude_dir::{include_dir, Dir}serde_json::json as data— sugar so controller/view code can writedata!({"item": ..})instead ofjson!validator::Validate
Core Loco types
app::{AppContext, Initializer}bgworker::{BackgroundWorker, Queue}errors::ErrorandResult(the crate’sResult<T, Error>alias)mailer(the module itself) andmailer::Mailertask::{self, Task, TaskInfo}validation::{self, Validatable, ValidatorTrait}
Controller layer
controller::{bad_request, not_found, unauthorized}— error-response constructor fnscontroller::format— the response-builder module (format::json,format::render(), …)controller::middleware::format::{Format, RespondTo}— content-negotiation extractor/enumcontroller::middleware::remote_ip::RemoteIP— computed-client-IP extractorcontroller::extractor::shared_store::SharedStore— the DI extractor (see above)controller::extractor::validate::{JsonValidate, JsonValidateWithMessage}— validating-body extractorscontroller::views::{engines::TeraView, ViewEngine, ViewRenderer}controller::{Json, Routes}
Feature-gated
#[cfg(feature = "auth")] controller::extractor::auth(the wholeauthextractor module:JWT,JWTWithUser,ApiToken,UserClaims, token-extraction helpers)#[cfg(feature = "with-db")]:- Sea-ORM traits and types:
ActiveModelBehavior, ActiveModelTrait, ActiveValue, ColumnTrait, ConnectionTrait, DatabaseConnection, DbErr, EntityTrait, IntoActiveModel, ModelTrait, QueryFilter, Set, TransactionTrait - Sea-ORM scalar re-exports:
sea_orm::prelude::{Date, DateTimeUtc, DateTimeWithTimeZone, Decimal, Uuid} model::{query, Authenticable, ModelError, ModelResult}— plus a nestedpub mod model { pub use crate::model::query; }, soqueryis reachable both asloco_rs::prelude::queryand asloco_rs::prelude::model::query
- Sea-ORM traits and types:
#[cfg(feature = "testing")] crate::testing::prelude::*— pulled in only for apps/tests built with thetestingfeature
Source: src/prelude.rs (verified against HEAD at authoring time).