การสร้าง API ด้วย Axum
ทำไมต้องใช้ Axum?
หัวข้อที่มีชื่อว่า “ทำไมต้องใช้ Axum?”ถ้าคุณมาจาก Node.js คุณคงรู้จัก Express หรือ NestJS แล้ว Axum คือคำตอบของ Rust สำหรับพื้นที่นั้น — เป็น web framework ระดับสูงที่ให้คุณเขียน async fn route handlers, วิเคราะห์ข้อมูล request ด้วย type-safe extractors และแชร์ state ข้ามหลาย routes
แต่ต่างจาก Express ตรงที่ Axum ไม่ได้รวมทุกอย่างมาในตัว ออกแบบมาให้บางแล้วโยนหน้าที่ middleware ให้ Tower ที่เป็น middleware library ระดับ production ที่ compose ได้ยืดหยุ่นมาก ผลที่ได้คือ:
- Type safety ตั้งแต่ compile-time ตลอดทั้ง request/response cycle
- Zero-cost abstractions — ไม่มี garbage collector ไม่มีการ allocation ที่ซ่อนอยู่
- Async แบบ first-class สร้างบน Tokio ที่เป็น async runtime มาตรฐานของ Rust
เปรียบเทียบ Axum กับ Express และ NestJS
หัวข้อที่มีชื่อว่า “เปรียบเทียบ Axum กับ Express และ NestJS”| ฟีเจอร์ | Express | NestJS | Axum |
|---|---|---|---|
| ภาษา | TypeScript / JS | TypeScript | Rust |
| Routing | app.get(...) | @Get(...) decorator | Router::new().route(...) |
| Middleware | app.use(...) | @Middleware / guards | Tower layers |
| วิเคราะห์ Request | req.body, req.params | @Body(), @Param() | Extractors (Json, Path, Query) |
| State | app.locals | Dependency injection | State(...) extractor |
| จัดการ Error | next(err) middleware | Exception filters | Result<T, E> + IntoResponse |
| Type safety | บางส่วน (runtime) | บางส่วน (runtime) | เต็มรูปแบบ (compile-time) |
// Expressimport express from 'express';const app = express();
app.get('/', (req, res) => { res.json({ message: 'Hello, World!' });});
app.listen(3000, () => console.log('Listening on :3000'));use axum::{routing::get, Json, Router};use serde_json::{json, Value};
async fn hello() -> Json<Value> { Json(json!({ "message": "Hello, World!" }))}
#[tokio::main]async fn main() { let app = Router::new().route("/", get(hello)); let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); println!("Listening on :3000"); axum::serve(listener, app).await.unwrap();}รันบนเครื่องของคุณ — Axum ผูก port และทำงานตลอดเวลา จึงไม่สามารถรันใน browser sandbox ได้ ใช้
cargo runหลังจากตั้งค่าโปรเจกต์ในบทถัดไป
สิ่งที่คุณจะสร้าง
หัวข้อที่มีชื่อว่า “สิ่งที่คุณจะสร้าง”ตลอดโมดูลนี้คุณจะสร้าง Users API ขนาดเล็กที่ประกอบด้วย:
GET /users— ดึงรายการ users (pagination ด้วยQuery)POST /users— สร้าง user ใหม่ (Jsonbody)GET /users/{id}— ดึง user เดียว (Path)DELETE /users/{id}— ลบ user- Shared in-memory state (
Arc<Mutex<Vec<User>>>) - JSON errors แบบมีโครงสร้าง
- Tower CORS layer
- SQLx database integration (เชิงแนวคิด)
- Handler tests ด้วย
tower::ServiceExt::oneshot
แต่ละแนวคิดเชื่อมโยงโดยตรงกับสิ่งที่คุณรู้จากอยู่แล้วจาก Express หรือ NestJS
มาจาก Axum 0.7?
หัวข้อที่มีชื่อว่า “มาจาก Axum 0.7?”โมดูลนี้ใช้ Axum 0.8 ถ้าคุณกำลังย้าย codebase จาก 0.7 หรืออ่าน tutorial เก่า ๆ อยู่ นี่คือจุดที่จะทำให้ code พัง:
| 0.7 | 0.8 |
|---|---|
.route("/users/:id", ...) | .route("/users/{id}", ...) — และ /*rest กลายเป็น /{*rest} |
ต้องมี #[async_trait] บน FromRequestParts | ไม่ต้องแล้ว trait ใช้ async fn แบบ native |
Option<Path<T>> กลืน error ทุกกรณี | ตอนนี้ reject request ในกรณีส่วนใหญ่ — ถ้าต้องการพฤติกรรมเดิมให้ implement OptionalFromRequestParts |
extractor Host อยู่ใน axum | ย้ายไป crate axum-extra |
Message::Text(String) | Message::Text(Utf8Bytes) และ Message::Binary(Bytes) |
re-export axum::body::{Empty, Full} | ใช้ axum::body::Body |
handler และ service ต้องเป็น Sync ด้วย ส่วนที่เหลือของ API — extractor, State, Tower layer — ไม่เปลี่ยน การย้ายจึงเกือบทั้งหมดเป็นแค่การ find-and-replace ที่ route string