Why "batteries included"?
Loco’s tagline is “Axum with batteries included,” and it is meant literally: everything under the hood is standard Axum 0.8 and Tower, but almost none of the code you’d normally write to assemble a production web service in Rust — wiring a DB pool into state, picking a logging stack, hand-rolling a queue, choosing a config format — is code you have to write yourself. This page explains the design principle behind that choice, not the mechanics (those live in Architecture and the reference pages).
The prime directive
Section titled “The prime directive”When a Loco app needs a capability, the framework’s bias is:
- Reach for a built-in first. Database access, caching, background jobs, mailing, file storage, view rendering, JWT auth, health checks, a CLI — these already exist, wired into
AppContextand toggled from YAML. - Reach for a generator second.
cargo loco generatescaffolds the idiomatic shape of a model, controller, worker, mailer, task, or full CRUD scaffold. The generated code is not a black box — it’s a starting point you own and edit. - Hand-wire only as a last resort, and when you do, Loco gives you a small number of well-defined seams to do it safely —
Hooks,Initializer,SharedStore,before_routes/after_routes— rather than forcing you to fork the framework or reassemblemain()from scratch.
This ordering is the single idea that explains most of what looks, from a plain-Axum perspective, like “magic”: it isn’t magic, it’s a library of pre-wired decisions with an escape hatch at every layer.
What “hand-wiring” looks like without it
Section titled “What “hand-wiring” looks like without it”A typical Axum service starts every project by re-deciding things that have already been decided a thousand times: which connection-pool settings, which logging crate, how to get the DB handle into every handler, how config and secrets flow in, how a background job survives a restart. None of these decisions are hard, but making them again, per project, is where hours disappear and where inconsistency creeps in between a team’s services.
Loco’s coming-from-axum page walks through this delta concretely (pool setup, AddExtensionLayer state wiring, env_logger vs tracing, main.rs assembly) for a real reference app. The short version: every one of those steps becomes either a YAML key or a generator invocation in Loco, and the underlying Axum Router/State/extractor model is unchanged — so nothing about Axum’s own learning curve is hidden from you.
What Loco integrates
Section titled “What Loco integrates”Batteries, concretely, means the following are already implemented, tested, and reachable from AppContext or a Hooks default, rather than left as an exercise:
- Routing & the request pipeline —
AppRoutes/Routescompile down to a realaxum::Router<AppContext>; a documented, ordered stack of middleware (payload limits, CORS, compression, timeouts, security headers, request IDs, a static file server, a dev-mode fallback page, and more) is available with a config flip rather than atower::Layeryou write by hand. See Architecture and the middleware catalog. - Data & persistence — Sea-ORM 2.0 entities, migrations, and a query/pagination layer are generated from a compact field-type DSL (
cargo loco generate model ...), not written by hand column-by-column. - Background processing — one
BackgroundWorkertrait and oneperform_latercall site work unmodified against three interchangeable queue backends (Redis, Postgres, SQLite); see The background-processing model. - Scheduling & tasks — a cron-like scheduler (English or cron syntax) and ad-hoc CLI-invokable tasks, both driven from the same
Tasks/Hooksregistration points, no separate process supervisor to build. - Caching, storage, mail — a
Cachewith in-memory/Redis/null backends, a multi-driverStorageabstraction (local/memory/S3/Azure/GCS) with single and replicated (mirror/backup) strategies, and aMailerwith SMTP/STARTTLS/implicit-TLS and a stub-for-tests mode — each a field onAppContext, each swappable by config or feature flag rather than by rewriting call sites. - Views — server-rendered Tera templates, JSON views, or a single-binary
embedded_assetsbuild, chosen without touching controller code. See Views and assets. - Auth & security — JWT (HS512 by default, multi-location token extraction) and API-key extractors implementing the same
FromRequestPartspattern as everything else in Axum. - Operability — structured
tracinglogging with sane third-party filtering out of the box,/_ping//_health//_readinessendpoints, acargo loco doctordiagnostic command, and acargo loco routes/middlewareintrospection CLI. - Configuration — one typed
Configstruct, one environment-resolution rule, one file-precedence rule, and Tera’s ownget_envfor secrets — see The configuration model.
None of this requires a plugin marketplace or a runtime registry: it’s all compiled into loco-rs behind Cargo feature flags (see the feature-flags reference), so an app only pays for what it turns on.
The corollary: escape hatches, not walls
Section titled “The corollary: escape hatches, not walls”“Batteries included” only works as a philosophy if it doesn’t become “batteries mandatory.” Every built-in in Loco has a documented seam for replacing or bypassing it:
- Don’t like the default middleware stack? Override
Hooks::middlewaresand return your ownVec<Box<dyn MiddlewareLayer>>. - Want a raw Axum router mounted verbatim?
Hooks::before_routes/after_routeshand you a realaxum::Routerto mutate directly — the Coming from Axum page shows this as the literal drop-in path for existing Axum code. - Need a service that isn’t a first-class
AppContextfield (a third-party API client, a feature-flag SDK)?AppContext.shared_storeis a type-keyed DI container built for exactly that — see AppContext and dependency injection. - Want to own the tracing/logging stack yourself? Return
Ok(true)fromHooks::init_loggerand Loco steps aside. - Want a different view engine than Tera? Implement
ViewRendererand swap it in via anInitializer.
This is the same shape as Rails’ “convention over configuration,” reframed for a language where a global mutable app instance isn’t an option: Loco supplies the convention as a compiled-in default, and the configuration/override points are explicit, typed, and ordered — not implicit and discoverable only by reading source. The rest of this Explanation cluster works through each of those seams — boot lifecycle, DI, config loading, background jobs, views, and the Axum relationship — in more depth.