Danh sách bài viết

Bài 11: Axum Là Gì? Sinh Ra Từ Đâu

Bài 11 của series Rust RESTful API — bài đầu Group 2 Axum Overview giới thiệu framework chính được dùng xuyên suốt 316 bài: axum do team tokio-rs ra mắt tháng 7/2021 dưới tay David Pedersen, version 0.8 đã lock workspace.dependencies ở B10, vận hành trên 4 triết lý cốt lõi — tower-based (reuse middleware ecosystem qua trait tower::Service), type-safe extractor (đưa data từ request vào handler qua type signature), KHÔNG macro bắt buộc (route handler là Rust function thường, khác rocket/actix-web), async-first (yêu cầu tokio runtime, mỗi request một task work-stealing); bài cũng so sánh axum với actix-web (actor model truyền thống) và rocket (macro-heavy stateful) trên 10 chiều, điểm tên các production case dùng axum 2026 (Cloudflare Workers Rust, AWS Labs, Shuttle PaaS, Tonic gRPC, Discord Read States), và giải thích 4 lý do Shop API chọn axum cho Group 1-31 — triết lý match (type-safe → DTO validation early), ecosystem alignment với utoipa + tower-http + axum-test, learning curve team-friendly dưới 1 tuần, và roadmap tokio-rs mature đảm bảo version bump ổn định.

12/06/2026
10 phút đọc
1 lượt xem
1

Mục Tiêu Bài Học

Sau bài học, bạn sẽ:

  • Biết axum sinh ra từ team tokio-rs (2021), version mới nhất 0.8 đã lock workspace.dependencies ở B10.
  • Hiểu 4 triết lý cốt lõi của axum: tower-based, type-safe extractor, không macro bắt buộc, async-first.
  • So sánh axum vs actix-web vs rocket — ưu nhược điểm và sweet spot mỗi framework.
  • Biết ai đang dùng axum production 2026: Cloudflare Workers Rust, AWS Labs, Shuttle, Tonic gRPC, Discord Read States.
  • Hiểu vì sao Shop API chọn axum cho Group 1-31 (~300 bài kế tiếp).
  • Sẵn sàng cho B12 Hello World axum thực hành — viết route đầu tiên trong crate shop-api đã init ở B10.
2

axum — Sinh Ra Từ tokio-rs Năm 2021

axumweb framework (khung viết HTTP server) Rust do team tokio-rs phát triển và maintain. Repository: github.com/tokio-rs/axum. Release đầu tiên version 0.1 vào tháng 7/2021 qua blog post chính thức trên tokio.rs. Tác giả khởi xướng là David Pedersen (GitHub handle davidpdrsn), một core contributor của tokio.

Trước axum, hệ sinh thái tokio-rs đã có warp — framework dùng filter combinator pattern. warp mạnh về tính declarative nhưng có vấn đề lớn: thông báo lỗi compile khó đọc do filter nested chồng nhau sinh ra type signature dài vài chục dòng. Một lỗi đơn giản như extract sai thứ tự cũng cho ra error message hàng trăm ký tự. axum ra đời để thay thế warp dần dần với mục tiêu: giữ type-safety nhưng error message phải human-readable.

Position trong ecosystem Rust web framework: axum là alternative cho hai framework lớn cùng thời — actix-web (ra đời 2017, ban đầu dùng actor model riêng) và rocket (ra đời 2016, nổi bật với macro #[get]). axum không cạnh tranh ở khía cạnh "nhanh hơn ai" mà ở khía cạnh "tích hợp tốt nhất với ecosystem tower + tokio".

Số liệu tăng trưởng từ crates.io: lúc release 2021 chỉ ~10,000 download/tháng, đến 2024-2025 đã đạt ~5 triệu download/tháng, lọt top 50 Rust crate theo download. Số reverse dependency (crate khác phụ thuộc axum) cũng vào top 30. Version 0.8 release năm 2024 với edition 2024 ready, là version đã lock trong workspace.dependencies ở B10.

3

Triết Lý 1: Tower-Based — Không Tự Build HTTP Stack

axum không tự implement HTTP/1.1 hay HTTP/2 từ con số không. Toàn bộ phần thấp của HTTP do hyper đảm nhiệm (hyper 1.x cũng thuộc tokio-rs). Phía middleware abstraction, axum xây trên trait tower::Service — interface chuẩn cho mọi async service Rust: nhận Request trả Response.

Cấu trúc lớp từ dưới lên (TCP đến handler):

┌─────────────────────────────────────────┐
│  Application handlers (axum::handler)   │  ← code bạn viết
├─────────────────────────────────────────┤
│  axum Router + extractor + IntoResponse │  ← thin layer
├─────────────────────────────────────────┤
│  tower middleware (Layer + Service)     │  ← timeout, retry, cors, trace
├─────────────────────────────────────────┤
│  hyper HTTP engine (HTTP/1.1 + HTTP/2)  │  ← parse, header, body
├─────────────────────────────────────────┤
│  tokio runtime (TCP, async I/O)         │  ← work-stealing scheduler
└─────────────────────────────────────────┘

Hệ quả thực tế của triết lý này:

  • Composable middleware — mọi crate implement tower::Layer đều dùng được trực tiếp với axum không cần adapter. Crate tower-http đã đóng gói sẵn TimeoutLayer, CorsLayer, TraceLayer, CompressionLayer, RateLimitLayer — Shop API sẽ dùng nguyên ở Group 15-17 (CORS B153, compression B48, rate-limit G17).
  • Tái dùng cross-protocoltonic (gRPC framework cùng tokio-rs) cũng xây trên tower::Service. Cùng một middleware TimeoutLayer chạy được cho cả HTTP REST (axum) và gRPC (tonic) — đây là lý do quan trọng để Shop API chọn axum khi B314 capstone re-implement subset domain qua gRPC.
  • Trade-off — để debug sâu khi middleware behave lạ, bạn phải hiểu trait ServiceLayer (đặc biệt poll-based poll_ready + call). Group 15 sẽ mổ chi tiết kèm ví dụ Shop API.
4

Triết Lý 2: Type-Safe Routing & Extraction

Trong axum, một route handlerfunction async thường của Rust — không phải class, không phải struct, không phải actor. Cách lấy data từ request vào handler là qua extractor — type implement trait FromRequest hoặc FromRequestParts. Mỗi argument của handler chính là một extractor; type của argument quyết định data được lấy từ đâu trong request.

Ví dụ handler axum lấy path parameter + query string + JSON body:

use axum::extract::{Path, Query, Json, State};

// Đây là handler axum. Type signature là interface khai báo
// "tôi cần gì từ request" — compiler verify lúc build.
async fn create_review(
    Path(product_id): Path<i64>,            // /products/:id
    Query(filter): Query<ReviewFilter>,     // ?lang=vi
    State(app): State<AppState>,            // shared state
    Json(payload): Json<CreateReviewDto>,   // request body, PHẢI cuối
) -> AppResult<Json<Review>> {
    // Logic ở đây nhận đầy đủ data đã parse, đã validate.
    todo!()
}

So sánh với handler warp tương đương (filter combinator chain):

// warp — filter combinator nested
let create_review = warp::path!("products" / i64 / "reviews")
    .and(warp::query::<ReviewFilter>())
    .and(with_state(app.clone()))
    .and(warp::body::json())
    .and_then(|id, filter, state, payload: CreateReviewDto| async move {
        // Logic — nhưng type của closure là chuỗi tuple dài
    });

Khác biệt rõ ràng: axum handler đọc giống function thông thường, mọi extractor là argument tách biệt có tên rõ. warp handler là closure nhận tuple do filter combinator sinh ra. Khi handler có 4-5 extractor, signature warp dài 3-4 dòng và error message khi extract sai có thể vài chục dòng. axum giữ signature ngắn, error message tách từng extractor.

Compiler chặn sai signature từ B12: extract Json<T> phải đặt cuối cùng trong argument list vì Json consume body. Đặt giữa sẽ báo lỗi compile rõ ràng "Json must be the last extractor" — không phải lỗi runtime 500. Group 4 (B31-B40) sẽ deep dive trait FromRequest + FromRequestParts và viết custom extractor cho CurrentUser của Shop API.

5

Triết Lý 3: Không Macro Bắt Buộc — Pure Rust

Đây là điểm khác biệt rõ rệt nhất giữa axum và hai framework đối thủ. Trong rocket, route khai báo qua macro #[get("/users")]; trong actix-web, qua macro #[actix_web::get("/users")]. axum không có macro nào tương đương — route khai báo qua method call thông thường trên type Router:

use axum::{Router, routing::{get, post, patch, delete}};

let router = Router::new()
    .route("/api/v1/products", get(list_products).post(create_product))
    .route("/api/v1/products/:id", get(get_product)
                                    .patch(update_product)
                                    .delete(delete_product))
    .route("/api/v1/categories", get(list_categories));

Toàn bộ là regular Rust code — type Router, method route, function get/post/patch/delete trong module axum::routing. Lợi ích cụ thể:

  • Debug dễ — không cần cargo expand xem macro sinh ra gì. Code bạn đọc là code chạy.
  • Error message rõ ràng — compiler báo lỗi trực tiếp vào dòng get(handler_sai), không phải vào một dòng macro tự generate.
  • Refactor an toàn — đổi tên handler function thì rust-analyzer rename tự động qua mọi reference; với macro thì IDE refactor đôi khi miss.
  • Không "magic" — dev mới onboard nhìn router.route("/x", get(handler)) hiểu ngay; macro #[get("/x")] phải tra docs để biết những gì khác (response builder, error handler, async detection) macro tự gắn vào.

Có một ngoại lệ: utoipa macro #[utoipa::path(...)] dùng để annotate handler sinh OpenAPI spec (đã lock từ B8) là macro optional — bạn thêm trên handler axum thì utoipa pickup, không thêm thì handler vẫn chạy bình thường. Macro này KHÔNG can thiệp vào routing core của axum, chỉ đọc thông tin để generate document.

Trade-off: viết route trong axum hơi dài hơn rocket một chút (rocket chỉ cần một dòng macro). Với Shop API ~60 endpoint thì overhead này không đáng kể, đổi lại debug experience tốt hơn nhiều. Quyết định lock từ B10 đã chốt: Shop API chỉ dùng utoipa macro cho OpenAPI, không dùng macro routing nào khác.

6

Triết Lý 4: Async-First Trên tokio

axum yêu cầu tokio runtime — không hỗ trợ executor async khác (smol, async-std). Mọi handler phải là async fn trả type implement IntoResponse. Entry point binary main dùng macro #[tokio::main] để khởi tạo runtime (đã thấy ở B10 trong crates/shop-api/src/main.rs).

// Handler axum tối giản — chỉ vài dòng cho thấy 4 thứ:
// async fn, không có macro, IntoResponse, không can thiệp gì.
async fn root() -> &'static str {
    "shop-api v0.1.0"
}

async fn health() -> (axum::http::StatusCode, &'static str) {
    (axum::http::StatusCode::OK, "ok")
}

Concurrency model: mỗi request HTTP nhận về sẽ được spawn thành một tokio task riêng (khác với thread per request truyền thống). tokio dùng work-stealing scheduler đa lõi (recap từ series rust-cơ-bản Group 31) — task có thể migrate giữa worker thread khi một thread bận. Số task tối đa hệ thống xử lý đồng thời gần như không giới hạn, chỉ bị giới hạn bởi memory và file descriptor.

Hiệu năng axum trên các benchmark như TechEmpower, Tachy thường rất sát actix-web (chênh nhau vài phần trăm tùy loại workload). Cả hai đều lọt top 10 Rust framework theo throughput. Với Shop API là CRUD-heavy + DB-bound, chênh lệch micro-benchmark này không phản ánh được performance thực — bottleneck nằm ở PostgreSQL và Redis, không ở framework.

Lưu ý quan trọng: handler axum bắt buộc thỏa Send + 'static. Mọi giá trị shared cross-task (như AppState) phải implement Clone hoặc bọc Arc. B28 sẽ deep dive State + Arc pattern cho Shop API AppState { pool, redis, config }.

7

So Sánh axum vs actix-web vs rocket

Bảng so sánh 3 framework trên 10 chiều quan trọng:

Aspect          | axum                | actix-web           | rocket
----------------+---------------------+---------------------+----------------------
Founded         | 2021 (tokio-rs)     | 2017 (Nikolay K.)   | 2016 (Sergio Benitez)
Latest version  | 0.8                 | 4.x                 | 0.5
Runtime         | tokio (only)        | actix (tokio-based) | tokio
Routing model   | Function + extractor| Function + macro    | Macro #[get]
Middleware      | tower::Layer        | wrap_fn / Transform | Fairings
HTTP/2          | Yes (via hyper)     | Yes                 | Yes
Async maturity  | Native từ đầu       | Native từ 4.x       | Stable async từ 0.5
Type safety     | Strong (extractor)  | Medium              | Strong (codegen)
Learning curve  | Medium              | Medium-High (actor) | Low (macro magic)
Ecosystem       | tower-http mature   | actix-* tách riêng  | Smaller scope

Phân tích từng framework:

  • axum — thắng ở ecosystem alignment với tower-http (CORS, trace, compression, rate-limit ready-made) và cross-protocol middleware reuse với tonic gRPC. Type-safety qua extractor giúp catch lỗi sớm ở compile time. Trade-off: route declaration hơi verbose so với rocket.
  • actix-web — performance nhỉnh hơn axum ~5% trong vài benchmark đặc biệt (TechEmpower plaintext) do tối ưu sâu HTTP parser nội bộ. Trước version 4.x dùng actor model riêng học khá khó; từ 4.x align tokio dễ hơn nhưng API vẫn có dấu vết actor. Ecosystem rộng nhưng tự thành cluster, ít share với crate ngoài.
  • rocketdễ học nhất nhờ macro #[get("/users/<id>")] đọc gần như Flask/Django. Async support chỉ stable từ 0.5 (muộn hơn axum 2 năm). Ecosystem nhỏ hơn — production case ít, integration third-party rời rạc. Phù hợp project nhỏ hoặc dạy học.

Không có "framework tốt nhất" tuyệt đối — chọn framework tùy team size, ecosystem cần thiết, và yêu cầu hiệu năng. Phần kế giải thích lý do Shop API chọn axum.

8

Ai Dùng axum Production 2026

Một số production case dùng axum tính đến giữa 2026:

  • Cloudflare Workers Rust — runtime wasm chạy Rust code ở edge. Cloudflare Workers SDK Rust mặc định compatible với axum router để dev viết handler một lần chạy được cả Workers và server tradition.
  • AWS Labs — nhiều sample application chính thức của AWS Rust SDK dùng axum. smithy-rs (generator AWS SDK Rust) dùng tower làm nền — share trait với axum trong middleware.
  • Shuttle — PaaS Rust hàng đầu, template mặc định cho project mới là axum. Onboard với Shuttle thì axum gần như default choice.
  • Tonic — gRPC framework cùng tokio-rs team, integrate trực tiếp với axum qua tower service. Project nào cần REST + gRPC cùng codebase dùng axum + tonic là pattern phổ biến nhất.
  • Discord Read States service — Discord migration từ warp sang axum năm 2023, công bố qua engineering blog Discord. Kết quả: giảm P99 latency ~15%, code complexity giảm rõ do extractor pattern thay thế filter chain warp.
  • Rust Foundation Annual Survey 2024-2025 — khoảng 40% Rust web service production dùng axum, vượt actix-web (~30%) và rocket (~10%). Số liệu approximation theo answer "Which web framework do you use in production".

Adoption rộng tạo flywheel hai chiều: ecosystem crate viết integration cho axum trước (CORS, OpenAPI, auth, metrics) → dev mới chọn axum để tiết kiệm thời gian → ecosystem càng rộng.

9

Vì Sao Shop API Chọn axum?

4 lý do cụ thể Shop API chọn axum làm framework chính cho Group 1-31 (~300 bài):

  1. Triết lý match domain Shop:
    • Type-safe extractor → DTO validation early ở B41 (Json<CreateUserDto> + validator derive), error 422 trả về client với cấu trúc rõ — không phải runtime panic.
    • Tower middleware mature → CORS (B153), rate-limit (G17 Redis token bucket), trace (G15 OpenTelemetry), compression (B48) chỉ cần plug-in từ tower-http.
    • Không macro routing → dev mới đọc router.route("/api/v1/products", get(list_products)) hiểu ngay; cargo expand không cần.
    • tokio default → align trực tiếp với sqlx (DB driver async), redis-rs/fred (Redis async), apalis (background job) — không phải runtime adapter, không phải executor crossing.
  2. Ecosystem alignment với Shop tech stack:
    • utoipa (đã lock B8) — code-first OpenAPI generation hỗ trợ axum first-class qua feature axum_extras.
    • tower-http — đã lock workspace.dependencies với feature trace + cors + compression-gzip ở B10.
    • axum-test (sẽ dùng từ B253) — test framework chính thức của tokio-rs cho axum, gọi router trực tiếp không qua TCP, test integration nhanh.
  3. Team-friendly onboard nhanh — dev biết Rust cơ bản và async tokio (mục tiêu của series rust-cơ-bản đã hoàn thành) có thể productive với axum trong dưới một tuần. Đối chứng: actix-web actor model học cần 2-3 tuần để hiểu sâu enough viết middleware tùy chỉnh.
  4. Future-proof qua tokio-rs maintenancetokio-rs là organization mature, có công ty bảo trợ (AWS, Cloudflare), version bump tốt và predictable (0.5 → 0.6 → 0.7 → 0.8 mỗi năm ~1 major). Rocket ngược lại có giai đoạn stagnate dài (0.4 → 0.5 mất hơn 2 năm), risk maintainer burn-out cao hơn vì project cá nhân.

Quyết định lock cụ thể trong B10: axum = "0.8"workspace.dependencies, không có ý định nâng version trong suốt series trừ khi có lý do bảo mật. Mọi bài sau build incremental trên decision này.

10

Tổng Kết

  • axum do team tokio-rs maintain, release đầu 7/2021 dưới tay David Pedersen, version mới nhất 0.8 (2024+, Rust edition 2024 ready). Mục đích ban đầu thay thế warp do error message của warp khó đọc.
  • 4 triết lý cốt lõi: (1) tower-based reuse middleware ecosystem qua tower::Service; (2) type-safe extractor đưa data từ request vào handler qua type signature, compile-time check; (3) không macro bắt buộc route khai báo regular Rust code, debug dễ; (4) async-first trên tokio runtime, work-stealing scheduler đa lõi.
  • So với actix-web: type-safety nhỉnh hơn nhờ extractor, ecosystem tower share rộng (tonic gRPC reuse middleware), learning curve trung bình. actix-web performance hơn vài phần trăm trong micro-benchmark nhưng không phản ánh real-world DB-bound workload.
  • So với rocket: ecosystem axum lớn hơn nhiều, async native sớm hơn 2 năm, không phụ thuộc macro magic — debug và refactor an toàn hơn. Rocket dễ học nhất nhưng adoption nhỏ.
  • Adoption 2026: Cloudflare Workers Rust, AWS Labs, Shuttle (PaaS default template), Tonic gRPC, Discord Read States (migrate warp → axum 2023, giảm P99 latency 15%). Rust Foundation Survey: ~40% Rust web service production dùng axum.
  • Shop API chọn axum vì 4 lý do: (1) triết lý match (type-safe extractor, tower middleware mature, không macro, tokio align); (2) ecosystem alignment với utoipa + tower-http + axum-test; (3) team-friendly onboard dưới một tuần; (4) tokio-rs roadmap mature, version bump predictable.
  • Version axum 0.8 đã lock workspace.dependencies ở B10 (không đổi xuyên suốt series trừ lý do bảo mật). B12 bắt đầu thực hành hello world axum trên skeleton crate shop-api đã có.
11

Bài Tập Củng Cố

Tự trả lời, đáp án ở cuối:

  1. Tác giả axum là ai và thuộc tổ chức nào? axum ra đời với mục đích thay thế framework nào trong cùng ecosystem? Lý do thay thế là gì?
  2. Liệt kê 4 triết lý cốt lõi của axum. Với mỗi triết lý, nêu một hệ quả thực tế cho dev khi dùng axum.
  3. axum xây trên Service trait của crate nào? Lợi ích cụ thể khi reuse middleware ecosystem qua trait này — đặc biệt khi project cần cả REST và gRPC?
  4. So sánh routing pattern của axum vs rocket. Framework nào dùng macro, framework nào không? Trade-off cụ thể của mỗi cách?
  5. Shop API chọn axum cho Group 1-31 vì 4 lý do gì? Liệt kê ngắn gọn theo thứ tự và giải thích mỗi lý do bằng một câu.
Đáp án
  1. Tác giả khởi xướng axum là David Pedersen (GitHub handle davidpdrsn), một core contributor của tokio. axum thuộc tổ chức tokio-rs trên GitHub — cùng tổ chức với tokio runtime, hyper HTTP engine, tonic gRPC. Mục đích ban đầu thay thế warp (cũng thuộc tokio-rs). Lý do: warp dùng filter combinator pattern, sinh ra type signature lồng nhau dài → khi compile lỗi, error message rất khó đọc, đôi khi hàng trăm ký tự cho một lỗi extract sai. axum giữ type-safety nhưng dùng function + extractor pattern để error message human-readable theo từng extractor riêng biệt.
  2. 4 triết lý + hệ quả: (1) Tower-based — reuse mọi middleware tower-http (CORS, trace, compression, rate-limit) không cần adapter, share middleware với tonic gRPC. (2) Type-safe extractor — handler là async fn nhận extractor làm argument, compiler chặn signature sai lúc build (vd Json không cuối → error message rõ "Json must be the last extractor"), không phải lỗi runtime 500. (3) Không macro bắt buộc — route khai báo qua method call router.route("/x", get(handler)) là regular Rust code, debug không cần cargo expand, error message chỉ thẳng dòng sai, refactor rename qua IDE an toàn. (4) Async-first trên tokio — handler bắt buộc async fn + Send + 'static, mỗi request một task work-stealing scheduler, align trực tiếp với sqlx + redis + apalis cùng dùng tokio.
  3. axum xây trên trait Service của crate tower. Interface: async fn call(Request) -> Result<Response, Error> — abstraction phổ quát cho mọi async service request/response. Lợi ích reuse middleware: (a) mọi crate implement tower::Layer (timeout, retry, rate-limit, circuit breaker, ...) dùng được trực tiếp với axum không cần wrapper; (b) tower-http đã đóng gói sẵn CorsLayer, TraceLayer, CompressionLayer, TimeoutLayer — Shop API plug-in trực tiếp ở G15-G17. Đặc biệt cho REST + gRPC cùng project: tonic gRPC cũng xây trên tower::Service nên cùng một TimeoutLayer, TraceLayer chạy được cho cả axum (REST) và tonic (gRPC). Khi Shop API capstone B314 re-implement subset domain qua gRPC, không phải viết lại middleware — đây là lý do quan trọng để chọn axum thay vì actix-web.
  4. axum không dùng macro cho route — declaration là method call router.route("/users/:id", get(get_user).delete(delete_user)) trên type Router. rocket dùng macro#[get("/users/<id>")] trên function. Trade-off rocket: dòng code ngắn gọn, đọc gần như Flask/Django dễ tiếp cận; nhưng debug khó hơn (phải cargo expand xem macro sinh code gì), error message đôi khi chỉ vào dòng macro thay vì code thực, refactor rename qua IDE rủi ro miss. Trade-off axum: viết route hơi verbose hơn (thêm method .route(...)); đổi lại debug experience tốt, code đọc là code chạy không có "magic", IDE refactor an toàn 100%, error message chỉ thẳng dòng sai. Với Shop API ~60 endpoint, overhead verbose của axum không đáng kể, đổi lại lợi ích debug rất lớn khi maintain dài hạn.
  5. 4 lý do Shop API chọn axum: (1) Triết lý match domain — type-safe extractor cho DTO validation early ở B41, tower middleware mature cho CORS/rate-limit/trace, không macro routing dễ debug, tokio default align sqlx + redis + apalis. (2) Ecosystem alignment với tech stack đã lock — utoipa (B8) hỗ trợ axum first-class qua feature axum_extras, tower-http đã lock workspace.dependencies, axum-test là test framework chính thức tokio-rs. (3) Team-friendly onboard nhanh — dev biết Rust + async cơ bản productive với axum trong dưới một tuần, đối chứng actix-web actor model cần 2-3 tuần. (4) Future-proof qua tokio-rs maintenance — organization mature có công ty bảo trợ (AWS, Cloudflare), version bump predictable mỗi năm ~1 major, không risk stagnate kéo dài như rocket (0.4 → 0.5 mất hơn 2 năm).
12

Bài Tiếp Theo

— thực hành hello world axum trong crate shop-api đã init ở B10: refresh Cargo.toml với feature axum + tokio đã lock workspace.dependencies, build router Router::new().route("/", get(handler)), bind tokio::net::TcpListener, gọi axum::serve, chạy cargo run -p shop-apicurl http://localhost:3000 verify response. Bài đầu tiên thực sự viết code axum trong codebase Shop API.