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 serde::Deserialize; #[derive(Debug, Deserialize)] #[serde(untagged)]
Parsing flexible JSON shapes with serde
deserialization
enums
json
Intermediate
6 steps
python
from fastapi import FastAPI, WebSocket, WebSocketDisconnect app = FastAPI()
Building a WebSocket chat with FastAPI
websockets
broadcast
connection-management
Intermediate
9 steps
rust
use std::cmp::{Ordering, Reverse}; use std::collections::BinaryHeap; use std::fs::File; use std::io::{self, BufRead, BufReader, BufWriter, Lines, Write};
K-way merge of sorted logs in Rust
binary-heap
k-way-merge
streaming-io
Intermediate
8 steps
rust
use axum::{ extract::{Path, State}, response::sse::{Event, KeepAlive, Sse}, };
Streaming import progress with SSE in Axum
server-sent-events
streams
watch-channel
Advanced
7 steps
rust
use std::f64::consts::PI; #[derive(Debug, Clone, Copy)] pub struct LatLng {
Building geographic bounding boxes in Rust
geospatial
value-types
option
Intermediate
7 steps
javascript
import { useState, useEffect, useCallback, useRef } from 'react'; const cache = new Map(); const inflight = new Map();
Building a stale-while-revalidate hook in React
caching
request-deduplication
custom-hooks
Advanced
10 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.