docs-site/content/docs/reference/app-context.md
+++ title = "AppContext & prelude" description = "The AppContext struct field-by-field, and everything use loco_rs::prelude::* brings into scope." date = 2021-05-01T18:10:00+00:00 updated = 2021-05-01T18:10:00+00:00 draft = false weight = 6 sort_by = "weight" template = "docs/page.html"
[extra] lead = "" toc = true top = false +++
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.
AppContextDefined 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>,
}
#[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>.
| 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 slotshared_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 a Deref<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::preludeuse 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::cookieThird-party helpers
chrono::NaiveDateTime as DateTimeinclude_dir::{include_dir, Dir}serde_json::json as data — sugar so controller/view code can write data!({"item": ..}) instead of json!validator::ValidateCore Loco types
app::{AppContext, Initializer}bgworker::{BackgroundWorker, Queue}errors::Error and Result (the crate's Result<T, Error> alias)mailer (the module itself) and mailer::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 whole auth extractor module: JWT, JWTWithUser, ApiToken, UserClaims, token-extraction helpers)#[cfg(feature = "with-db")]:
ActiveModelBehavior, ActiveModelTrait, ActiveValue, ColumnTrait, ConnectionTrait, DatabaseConnection, DbErr, EntityTrait, IntoActiveModel, ModelTrait, QueryFilter, Set, TransactionTraitsea_orm::prelude::{Date, DateTimeUtc, DateTimeWithTimeZone, Decimal, Uuid}model::{query, Authenticable, ModelError, ModelResult} — plus a nested pub mod model { pub use crate::model::query; }, so query is reachable both as loco_rs::prelude::query and as loco_rs::prelude::model::query#[cfg(feature = "testing")] crate::testing::prelude::* — pulled in only for apps/tests built with the testing featureSource: src/prelude.rs (verified against HEAD at authoring time).