rust
64 lines · 9 steps
Offloading work with a channel in Axum
An Axum handler enqueues background jobs onto a Tokio mpsc channel instead of doing slow work inline.
Explained by
highlit
1use std::sync::Arc;
2
3use axum::{
4 extract::State,
5 http::StatusCode,
6 routing::post,
7 Json, Router,
8};
9use serde::Deserialize;
10use tokio::sync::mpsc::{self, Sender};
11use uuid::Uuid;
12
13#[derive(Clone)]
14pub struct AppState {
15 jobs: Sender<Job>,
16}
17
18#[derive(Debug)]
19pub enum Job {
20 SendWelcomeEmail { user_id: Uuid, email: String },
21 ResizeAvatar { user_id: Uuid, url: String },
22}
23
24pub fn build(worker_buffer: usize) -> (Router, mpsc::Receiver<Job>) {
25 let (tx, rx) = mpsc::channel(worker_buffer);
26 let state = Arc::new(AppState { jobs: tx });
27
28 let router = Router::new()
29 .route("/users", post(create_user))
30 .with_state(state);
31
32 (router, rx)
33}
34
35#[derive(Deserialize)]
36struct CreateUser {
37 email: String,
38 avatar_url: Option<String>,
39}
40
41async fn create_user(
42 State(state): State<Arc<AppState>>,
43 Json(payload): Json<CreateUser>,
44) -> Result<StatusCode, StatusCode> {
45 let user_id = Uuid::new_v4();
46
47 state
48 .jobs
49 .send(Job::SendWelcomeEmail {
50 user_id,
51 email: payload.email,
52 })
53 .await
54 .map_err(|_| StatusCode::SERVICE_UNAVAILABLE)?;
55
56 if let Some(url) = payload.avatar_url {
57 state
58 .jobs
59 .try_send(Job::ResizeAvatar { user_id, url })
60 .map_err(|_| StatusCode::SERVICE_UNAVAILABLE)?;
61 }
62
63 Ok(StatusCode::ACCEPTED)
64}
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Sharing a channel sender through Axum state lets handlers hand off slow work and respond immediately.
- 2Choosing send versus try_send decides whether a handler waits for buffer space or fails fast under load.
- 3Returning 202 Accepted signals that work was queued, not that it finished.
Related explainers
rust
use std::num::ParseIntError; fn parse_line(line: &str) -> Result<Vec<i64>, ParseIntError> { line.split(',')
Collecting Results in Rust iterators
iterators
error-handling
result
Intermediate
7 steps
rust
use std::sync::{Arc, Mutex, MutexGuard}; use std::time::Instant; pub struct TimedGuard<'a, T> {
A self-timing mutex guard in Rust
raii
mutex
deref
Advanced
7 steps
rust
use std::error::Error; use std::path::Path; use serde::Deserialize;
Custom CSV deserialization with serde in Rust
serde
csv
deserialization
Intermediate
7 steps
rust
use std::time::Duration; use axum::{extract::State, http::StatusCode, response::IntoResponse, Json}; use serde::Serialize;
A timeout-guarded health check in Axum
health-check
timeout
error-handling
Intermediate
6 steps
typescript
type Event = Record<string, unknown>; interface BatcherOptions { endpoint: string;
Batching analytics events in TypeScript
batching
buffering
async
Intermediate
8 steps
rust
use std::fs::File; use std::io::{self, BufWriter}; use std::path::Path;
Serializing config to JSON in Rust
serialization
serde
file-io
Intermediate
7 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/offloading-work-with-a-channel-in-axum-explained-rust-5ac9/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.