Axum vs Loco
We will study realworld-axum-sqlx which is an Axum based app, that attempts to describe a real world project, using API, real database, and real world scenarios as well as real world operability requirements such as configuration and logging.
Picking realworld-axum-sqlx
apart piece by piece we will show that by moving it from Axum to Loco, most of the code is already written for you, you get better best practices, better dev experience, integrated testing, code generation, and build apps faster.
You can use this breakdown to understand how to move your own Axum based app to Loco as well. For any questions, reach out in discussions or join our discord by clicking the green invite button
main
When working with Axum, you have to have your own main
function which sets up every component of your app, gets your routers, adds middleware, sets context, and finally, eventually, goes and sets up a listen
on a socket.
This is a lot of manual, error prone work.
In Loco you:
- Toggle on/off your desired middleware in configuration
- Use
cargo loco start
, no need for amain
file at all - In production, you get a compiled binary named
your_app
which you run
Moving to Loco
- Set up your required middleware in Loco
config/
server:
middlewares:
limit_payload:
enable: true
body_limit: 5mb
# .. more middleware below ..
- Set your serving port in Loco
config/
server:
port: 5150
Verdict
- No code to write, you don't need to hand-code a main function unless you have to
- Best practices off the shelf, you get a main file best practices uniform, shared across all your Loco apps
- Easy to change, if you want to remove/add middleware to test things out, you can just flip a switch in configuration, no rebuild
Env
The realworld axum codebase uses dotenv, which needs explicit loading in main
:
.ok;
dotenv
And a .env
file to be available, maintained and loaded:
DATABASE_URL=postgresql://postgres:{password}@localhost/realworld_axum_sqlx
HMAC_KEY={random-string}
RUST_LOG=realworld_axum_sqlx=debug,tower_http=debug
This is a sample file which you get with the project, which you have to manually copy and edit, which is more often than not very error prone.
Moving to Loco
Loco: use your standard config/[stage].yaml
configuration, and load specific values from environment using get_env
# config/development.yaml
# Web server configuration
server:
# Port on which the server will listen. the server binding is 0.0.0.0:{PORT}
port:
This configuration is strongly typed, contains most-used values like database URL, logger levels and filtering and more. No need to guess or reinvent the wheel.
Verdict
- No coding needed, when moving to Loco you write less code
- Less moving parts, when using Axum only, you have to have configuration in addition to env vars, this is something you get for free with Loco
Database
Using Axum only, you typically have to set up your connection, pool, and set it up to be available for your routes, here's the code which you put in your main.rs
typically:
let db = new
.max_connections
.connect
.await
.context?;
Then you have to hand-wire this connection
.layer
Moving to Loco
In Loco you just set your values for the pool in your config/
folder. We already pick up best effort default values so you don't have to do it, but if you want to, this is how it looks like:
database:
enable_logging: false
connect_timeout: 500
idle_timeout: 500
min_connections: 1
max_connections: 1
Verdict
- No code to write - save yourself the dangers of picking the right values for your db pool, or misconfiguring it
- Change is easy - often you want to try different values under different loads in production, with Axum only, you have to recompile, redeploy. With Loco you can set a config and restart the process.
Logging
All around your app, you'll have to manually code a logging story. Which do you pick? tracing
or slog
? Is it logging or tracing? What is better?
Here's what exists in the real-world-axum project. In serving:
// Enables logging. Use `RUST_LOG=tower_http=debug`
.layer,
And in main
:
// Initialize the logger.
;
init
And ad-hoc logging in various points:
!;
error
Moving to Loco
In Loco, we've already answered these hard questions and provide multi-tier logging and tracing:
- Inside the framework, internally
- Configured in the router
- Low level DB logging and tracing
- All of Loco's components such as tasks, background jobs, etc. all use the same facility
And we picked tracing
so that any and every Rust library can "stream" into your log uniformly.
But we also made sure to create smart filters so you don't get bombarded with libraries you don't know, by default.
You can configure your logger in config/
logger:
enable: true
pretty_backtrace: true
level: debug
format: compact
Verdict
- No code to write - no set up code, no decision to make. We made the best decision for you so you can write more code for your app.
- Build faster - you get traces for only what you want. You get error backtraces which are colorful, contextual, and with zero noise which makes it easier to debug stuff. You can change formats and levels for production.
- Change is easy - often you want to try different values under different loads in production, with Axum only, you have to recompile, redeploy. With Loco you can set a config and restart the process.
Routing
Moving routes from Axum to Loco is actually drop-in. Loco uses the native Axum router.
If you want to have facilities like route listing and information, you can use the native Loco router, which translates to an Axum router, or you can use your own Axum router.
Moving to Loco
If you want 1:1 complete copy-paste experience, just copy your Axum routes, and plug your router in Loco's after_routes()
hook:
async
If you want Loco to understand the metadata information about your routes (which can come in handy later), write your routes()
function in each of your controllers in this way:
// this is what people usually do using Axum only
// this is how it looks like using Loco (notice we use `Routes` and `add`)
Verdict
- A drop-in compatibility - Loco uses Axum and keeps all of its building blocks intact so that you can just use your own existing Axum code with no efforts.
- Route metadata for free - one gap that Axum routers has is the ability to describe the currently configured routes, which can be used for listing or automatic OpenAPI schema generation. Loco has a small metadata layer to suppor this. If you use
Routes
you get it for free, while all of the different signatures remain compatible with Axum router.