Mục lục
- Mục Tiêu Bài Học
- OpenAPI vs Swagger — Đừng Confused
- OpenAPI 3.1 Cấu Trúc Chính
- Swagger UI / ReDoc / Scalar — Ba UI Tool
- utoipa Crate — Auto-Generate OpenAPI Từ axum
- Code-First vs API-First Workflow
- Endpoint Expose: /api-doc/openapi.json + /swagger-ui
- Tích Hợp Với Security: Bearer JWT
- Tổng Kết
- Bài Tập Củng Cố
- Bài Tiếp Theo
Mục Tiêu Bài Học
Sau bài học, bạn sẽ:
- Phân biệt rõ OpenAPI (spec định dạng) và Swagger (tooling kế thừa) — hai term hay bị dùng lẫn lộn.
- Nắm cấu trúc OpenAPI 3.1 với các root key chính:
info,servers,paths,components(schemas + securitySchemes),security,tags. - Biết ba UI tool render spec phổ biến: Swagger UI (cổ điển, có "Try it out"), ReDoc (3-pane clean cho document hóa), Scalar (modern, API client built-in).
- Hiểu crate
utoipacho axum với derive macroToSchema, attribute#[utoipa::path], struct#[derive(OpenApi)]auto-generate spec từ code. - Hiểu trade-off code-first (code = source of truth) vs API-first (design-by-contract).
- Áp dụng vào Shop API: expose
/api-doc/openapi.json+/swagger-uiở dev/staging, tắt production; tích hợpBearerAuthsecurity scheme qua traitModify.
OpenAPI vs Swagger — Đừng Confused
Hai term OpenAPI và Swagger thường bị dùng thay thế nhau, nhưng kỹ thuật là hai khái niệm tách bạch. Phân biệt rõ ngay từ đầu giúp bạn đọc tài liệu, blog, job description không bị nhầm.
Swagger ra đời năm 2010 do Tony Tam tại Reverb Technologies, là toolkit gốc để thiết kế và document REST API. Ba sản phẩm chính của Swagger thời đó: Swagger Specification (định dạng JSON/YAML mô tả API), Swagger UI (web app render spec), Swagger Codegen (sinh client/server code từ spec). SmartBear mua lại Swagger năm 2015.
Tháng 1 năm 2016, SmartBear donate Swagger Specification cho Linux Foundation, đổi tên thành OpenAPI Specification (OAS) và thành lập OpenAPI Initiative với thành viên sáng lập Google, IBM, Microsoft, PayPal, ... Spec chính thức rebrand: Swagger 2.0 trở thành nền tảng cho OpenAPI 3.0 (2017), tiếp đến OpenAPI 3.1 (2021) là phiên bản hiện hành tại thời điểm bài viết.
Từ 2016 trở đi, hai term phân nhánh rõ ràng:
- OpenAPI = spec định dạng, là chuẩn mô tả REST API (root keys, schema, security, ...). OpenAPI 3.1 được fully align với JSON Schema Draft 2020-12 — đây là điểm quan trọng vì crate Rust
schemarssinh JSON Schema 2020-12 ra có thể nhúng thẳng vào spec OpenAPI 3.1. - Swagger = tên brand của tooling do SmartBear maintain. Sản phẩm còn dùng phổ biến: Swagger UI (render spec), Swagger Editor (IDE web edit YAML), Swagger Codegen / OpenAPI Generator (codegen client SDK 50+ ngôn ngữ).
Tóm gọn: OpenAPI là spec, Swagger UI là một trong nhiều tool render spec đó. Khi đồng nghiệp nói "viết Swagger" thường ý là viết spec OpenAPI; "deploy Swagger" ý là deploy Swagger UI. Series này dùng đúng tên: OpenAPI cho spec, Swagger UI cho UI tool.
OpenAPI 3.1 Cấu Trúc Chính
Một file OpenAPI 3.1 là document JSON hoặc YAML có root keys cố định:
openapi— version spec, vd"3.1.0".info— metadata API:title,version,description,contact,license.servers— danh sách base URL theo môi trường (dev, staging, prod).paths— định nghĩa endpoint; mỗi path có HTTP method (get,post, ...) vớioperationId,parameters,requestBody,responses,tags,security.components— section reusable:schemas(DTO definitions tham chiếu qua$ref),securitySchemes(auth scheme),parameters,responses,examples.security— apply security scheme ở scope global cho cả API.tags— gom nhóm endpoint trong UI (vd "Auth", "Catalog", "Cart").
Đoạn YAML mẫu cho endpoint GET /api/v1/products/:slug của Shop API:
openapi: 3.1.0
info:
title: Shop API
version: 0.1.0
description: E-commerce REST API
contact:
name: Shop API Team
email: [email protected]
license:
name: MIT
servers:
- url: http://localhost:3000
description: Development
- url: https://staging.api.shop.example
description: Staging
paths:
/api/v1/products/{slug}:
get:
tags: [Catalog]
operationId: getProductBySlug
summary: Get product detail by slug
parameters:
- name: slug
in: path
required: true
schema:
type: string
responses:
'200':
description: Product found
content:
application/json:
schema:
$ref: '#/components/schemas/Product'
'404':
description: Product not found
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
components:
schemas:
Product:
type: object
required: [id, slug, name, price]
properties:
id:
type: integer
format: int64
slug:
type: string
name:
type: string
price:
type: string
description: Decimal as string
ErrorResponse:
type: object
required: [error, code, request_id]
properties:
error: { type: string }
code: { type: string }
request_id: { type: string }
securitySchemes:
BearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
Reference reuse qua $ref: '#/components/schemas/Product' tránh lặp; Product và ErrorResponse được dùng lại ở nhiều path khác nhau. Đây là pattern chuẩn — không viết schema inline cho mỗi path response.
Swagger UI / ReDoc / Scalar — Ba UI Tool
Spec OpenAPI là JSON/YAML không có giá trị trực tiếp với end-user (developer consume API). Phải render qua một UI tool. Ba lựa chọn phổ biến nhất tại thời điểm 2026:
Swagger UI — UI gốc do SmartBear maintain, mature nhất, được dùng rộng nhất. Render đầy đủ schema, response example, có nút "Try it out" trên mỗi endpoint cho phép developer gọi API thật ngay trong browser (điền tham số, click Execute, xem response). Phân phối qua npm package swagger-ui-dist hoặc CDN. Trải nghiệm cổ điển nhưng quen thuộc với mọi backend dev.
ReDoc — UI focus vào document hóa hơn là interactivity. Layout 3-pane (sidebar navigation, nội dung chính, code samples), kiểu document tham khảo hơn playground. KHÔNG có "Try it out" mặc định (có plugin Try it nhưng không tích hợp sẵn). Phù hợp public API document trên website marketing dạng docs.api.example nơi clean look quan trọng hơn tính tương tác. Stripe và GitHub dùng phong cách tương tự ReDoc.
Scalar — UI modern (xuất hiện 2023, phổ biến từ 2024), fast load, dark mode mặc định, có API client built-in (mạnh hơn "Try it out" của Swagger UI, có thể save request collection như Postman lite). Crate Rust hỗ trợ qua utoipa-scalar. Thích hợp project mới muốn UX hiện đại không bị "cổ" như Swagger UI.
Cả ba đều consume cùng file openapi.json. Cấu hình chỉ là trỏ UI tới URL spec: spec.url = "/api-doc/openapi.json". Có thể mount nhiều UI cùng lúc trên cùng project — vd /swagger-ui cho dev test, /redoc cho document public.
Shop API chọn Swagger UI mặc định cho dev/staging vì có "Try it out" giúp dev test endpoint nhanh không cần mở Postman. Nếu sau này cần document public cho third-party developer, sẽ thêm ReDoc song song; production runtime tắt cả hai (chi tiết section 7).
utoipa Crate — Auto-Generate OpenAPI Từ axum
utoipa là crate Rust de facto cho code-first OpenAPI generation, dùng derive macro để sinh spec từ struct DTO và axum handler. Ecosystem có vài crate phối hợp:
utoipa(core) — derive macroToSchema,OpenApi, attribute#[utoipa::path].utoipa-swagger-ui— embed Swagger UI assets vào binary và serve qua axum router.utoipa-axum— tích hợp typed router với utoipa (collect path tự động qua macroroutes!).utoipa-scalar/utoipa-redoc— alternative UI cho ai muốn Scalar hoặc ReDoc.
Phiên bản lock cho Shop API: utoipa 5, utoipa-swagger-ui 8, utoipa-axum 0.1. Cargo.toml preview:
# Cargo.toml — shop-api crate (preview cho B12-G6)
[dependencies]
axum = "0.8"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
tokio = { version = "1", features = ["full"] }
# OpenAPI documentation
utoipa = { version = "5", features = ["axum_extras", "chrono", "decimal"] }
utoipa-swagger-ui = { version = "8", features = ["axum"] }
utoipa-axum = "0.1"
Pattern code-first qua utoipa có ba thành phần. Thứ nhất, derive ToSchema cho mọi DTO để utoipa sinh schema entry vào components.schemas:
// File: crates/shop-api/src/handlers/products.rs
use serde::{Deserialize, Serialize};
use utoipa::ToSchema;
#[derive(Debug, Serialize, Deserialize, ToSchema)]
pub struct Product {
/// Product ID (BIGSERIAL)
pub id: i64,
/// URL-friendly slug
pub slug: String,
pub name: String,
/// Price as decimal string (vd "1499.00")
pub price: String,
}
#[derive(Debug, Serialize, ToSchema)]
pub struct ErrorResponse {
pub error: String,
pub code: String,
pub request_id: String,
}
Thứ hai, attribute #[utoipa::path] trên handler axum mô tả method, path, parameters, responses:
use axum::{extract::Path, Json};
use axum::http::StatusCode;
#[utoipa::path(
get,
path = "/api/v1/products/{slug}",
tag = "Catalog",
params(
("slug" = String, Path, description = "Product slug")
),
responses(
(status = 200, description = "Product found", body = Product),
(status = 404, description = "Product not found", body = ErrorResponse)
)
)]
pub async fn get_product_by_slug(
Path(slug): Path,
) -> Result, (StatusCode, Json)> {
// Implementation chi tiết ở G7 CRUD
todo!("read from PostgreSQL via ProductRepo")
}
Thứ ba, struct ApiDoc với #[derive(OpenApi)] gom tất cả path và schema lại thành một spec duy nhất:
// File: crates/shop-api/src/openapi.rs
use utoipa::OpenApi;
use crate::handlers::products::{
get_product_by_slug, Product, ErrorResponse,
};
#[derive(OpenApi)]
#[openapi(
info(
title = "Shop API",
version = "0.1.0",
description = "E-commerce REST API"
),
servers(
(url = "http://localhost:3000", description = "Development"),
(url = "https://staging.api.shop.example", description = "Staging")
),
paths(get_product_by_slug),
components(schemas(Product, ErrorResponse)),
tags(
(name = "Catalog", description = "Product catalog endpoints")
)
)]
pub struct ApiDoc;
Gọi ApiDoc::openapi() trả về object utoipa::openapi::OpenApi, serialize ra JSON/YAML qua serde tự động. Mỗi khi thêm handler mới hoặc DTO mới, chỉ cần append vào paths(...) và components(schemas(...)) — spec đồng bộ với code tại compile time.
Code-First vs API-First Workflow
Hai workflow chính trong industry để duy trì sync giữa spec và code:
Code-first (phổ biến trong Rust dev và startup):
- Viết Rust handler và DTO struct trước.
- Derive macro (utoipa
ToSchema,#[utoipa::path],#[derive(OpenApi)]) sinh OpenAPI spec từ code. - Pros: code là single source of truth, không bao giờ drift; refactor handler tự động propagate sang spec; CI dễ check (spec là output của
cargo build); learning curve thấp cho Rust dev. - Cons: spec phụ thuộc implementation, không có giai đoạn "design contract" trước khi code; khó cho frontend/mobile làm song song nếu Rust dev chưa code xong; backend dev có thể accidentally đổi schema breaking client mà không nhận ra.
API-first (phổ biến trong enterprise và team đa nền tảng):
- Viết OpenAPI YAML/JSON trước trong giai đoạn design (vd dùng Swagger Editor, Stoplight Studio, Apicurio).
- Codegen Rust server skeleton từ spec (tool:
oapi-codegen,openapi-generator-cli), backend dev fill in business logic. - Codegen client SDK cho frontend/mobile cùng từ spec, làm song song.
- Pros: contract clear ngay từ đầu, stakeholder review/approve trước khi code; frontend/mobile không bị block chờ backend; spec là nguồn quyền lực, breaking change phải qua review formal.
- Cons: code có thể drift khỏi spec nếu dev sửa Rust mà quên update YAML; cần CI gate check sync (
oapi-codegen diffhoặc test integration); maintenance cost cao hơn cho solo dev / small team.
Shop API lock code-first qua utoipa (đã quyết định ở B6 JSON Format Policy). Lý do phù hợp series:
- Dạy theo step-by-step incremental, viết code trước rồi document sau là natural flow.
- Mỗi bài thêm handler mới, derive macro tự động cập nhật spec — không phải maintain hai source.
- Solo dev / small team không cần ceremony design-by-contract của API-first.
- Đến khi cần API-first (vd Shop có frontend team làm song song), có thể export spec từ code-first ra YAML và dùng làm contract baseline — đảo workflow không bị mất công.
Nếu bạn vào project enterprise cần API-first, tool oapi-codegen (Rust port của openapi-codegen) sinh axum handler skeleton từ YAML; hoặc openapi-generator-cli hỗ trợ Rust template với rust-axum generator.
Endpoint Expose: /api-doc/openapi.json + /swagger-ui
Shop API expose hai endpoint cho documentation:
GET /api-doc/openapi.json— trả spec JSON đầy đủ. Tool externals (Postman import, codegen, monitoring) consume URL này.GET /swagger-ui— UI Swagger render spec, dev/QA dùng test endpoint qua "Try it out".
Cấu hình axum trong main.rs:
// File: crates/shop-api/src/main.rs
use axum::{Router, routing::get, Json};
use utoipa::OpenApi;
use utoipa_swagger_ui::SwaggerUi;
use crate::openapi::ApiDoc;
use crate::handlers::products::get_product_by_slug;
#[tokio::main]
async fn main() -> Result<(), Box> {
let config = AppConfig::from_env()?;
let api_router = Router::new()
.route("/products/{slug}", get(get_product_by_slug));
let mut app = Router::new()
.nest("/api/v1", api_router);
// Mount documentation chỉ ở dev/staging
if config.env != "production" {
app = app
.route(
"/api-doc/openapi.json",
get(|| async { Json(ApiDoc::openapi()) }),
)
.merge(
SwaggerUi::new("/swagger-ui")
.url("/api-doc/openapi.json", ApiDoc::openapi()),
);
}
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await?;
axum::serve(listener, app).await?;
Ok(())
}
Logic if config.env != "production" đảm bảo runtime production hoàn toàn không có route documentation — request GET /swagger-ui trả 404 thay vì render UI. Lý do tắt production:
- Tránh leak schema chi tiết ra public — đối thủ và attacker có thể đọc endpoint nội bộ, parameter, response shape để map surface attack.
- Giảm attack surface — Swagger UI có lịch sử CVE liên quan XSS, prototype pollution; mỗi endpoint extra là một bề mặt tiềm năng.
- Giảm binary size nhỏ — không ship Swagger UI assets cho production runtime (utoipa-swagger-ui embed ~2MB JS/CSS).
Internal team vẫn cần spec để consume API ở production (vd partner integration). Solution: artifact CI build sinh file openapi.json ra GitHub Release hoặc internal artifact registry; team frontend/mobile/partner pull file đó về dùng làm contract. Không expose qua runtime endpoint.
// File: crates/shop-api/src/bin/export_openapi.rs
// Binary phụ chạy ở CI để export spec
use shop_api::openapi::ApiDoc;
use utoipa::OpenApi;
fn main() -> Result<(), Box> {
let spec = ApiDoc::openapi();
let json = serde_json::to_string_pretty(&spec)?;
std::fs::write("openapi.json", json)?;
println!("OpenAPI spec exported to openapi.json");
Ok(())
}
GitHub Actions chạy cargo run --bin export_openapi mỗi PR, upload file làm artifact, tag release attach file → internal team có spec mới nhất không cần production endpoint.
Tích Hợp Với Security: Bearer JWT
OpenAPI 3.1 hỗ trợ nhiều security scheme: HTTP basic, HTTP bearer, API key (header/query/cookie), OAuth2 (4 flow), OpenID Connect. Shop API lock BearerAuth qua JWT (đã quyết định ở B4 header convention). Schema scheme:
{
"components": {
"securitySchemes": {
"BearerAuth": {
"type": "http",
"scheme": "bearer",
"bearerFormat": "JWT"
}
}
}
}
Utoipa support thêm security scheme qua trait Modify — implement trait này cho struct riêng (vd SecurityAddon), gọi trong attribute #[openapi(modifiers(&SecurityAddon))]:
// File: crates/shop-api/src/openapi.rs (mở rộng)
use utoipa::{
openapi::security::{HttpAuthScheme, HttpBuilder, SecurityScheme},
Modify, OpenApi,
};
pub struct SecurityAddon;
impl Modify for SecurityAddon {
fn modify(&self, openapi: &mut utoipa::openapi::OpenApi) {
let components = openapi
.components
.as_mut()
.expect("components register first");
components.add_security_scheme(
"BearerAuth",
SecurityScheme::Http(
HttpBuilder::new()
.scheme(HttpAuthScheme::Bearer)
.bearer_format("JWT")
.build(),
),
);
}
}
#[derive(OpenApi)]
#[openapi(
modifiers(&SecurityAddon),
info(title = "Shop API", version = "0.1.0"),
paths(get_product_by_slug, get_me),
components(schemas(Product, ErrorResponse, User)),
)]
pub struct ApiDoc;
Apply security per handler bằng attribute security(...) trong #[utoipa::path]. Endpoint cần auth liệt kê scheme; endpoint public bỏ trống:
#[utoipa::path(
get,
path = "/api/v1/me",
tag = "User",
security(("BearerAuth" = [])), // require Bearer JWT
responses(
(status = 200, description = "Current user", body = User),
(status = 401, description = "Unauthenticated", body = ErrorResponse)
)
)]
pub async fn get_me(/* CurrentUser extractor */) -> Json {
todo!()
}
#[utoipa::path(
get,
path = "/api/v1/products/{slug}",
tag = "Catalog",
// KHÔNG có security — endpoint public
responses(/* ... */)
)]
pub async fn get_product_by_slug(/* ... */) {
todo!()
}
Trên Swagger UI, sau khi spec có securitySchemes.BearerAuth, hiện nút Authorize ở góc phải. Click → paste JWT token → click Authorize. Mọi "Try it out" trên endpoint có security(("BearerAuth" = [])) sẽ tự động đính kèm Authorization: Bearer <token>; endpoint public không có security thì không cần token. Đây là workflow chuẩn dev test endpoint cần auth.
Chi tiết JWT signing, claim, refresh token sẽ phân tích sâu ở Group 12 (B111-B120). Tại B8 bài này chỉ lock spec scheme cho documentation: BearerAuth { type: http, scheme: bearer, bearerFormat: JWT } — naming này dùng xuyên suốt series, mọi handler cần auth reference cùng key "BearerAuth".
Tổng Kết
- OpenAPI 3.1 = spec định dạng (do OpenAPI Initiative maintain từ 2016); Swagger = tên brand tooling (Swagger UI, Swagger Editor, Swagger Codegen do SmartBear maintain). Không dùng lẫn hai term.
- Cấu trúc OpenAPI: root keys
info,servers,paths,components.schemas(DTO reusable qua$ref),components.securitySchemes,security,tags. OpenAPI 3.1 fully align với JSON Schema 2020-12. - Ba UI tool render spec: Swagger UI (cổ điển, "Try it out"), ReDoc (3-pane clean cho document hóa), Scalar (modern 2024+, API client built-in). Cùng consume một file
openapi.json. - utoipa crate: code-first OpenAPI cho axum qua derive macro. Pattern ba thành phần —
#[derive(ToSchema)]cho DTO,#[utoipa::path(...)]trên handler,#[derive(OpenApi)]cho structApiDocgom path + schema. - Code-first vs API-first: code-first dùng utoipa derive (code = source of truth, không drift, learning curve thấp); API-first viết YAML trước rồi codegen (contract clear, frontend/mobile làm song song, cần CI gate sync). Shop API chọn code-first vì solo dev / small team.
- Endpoint expose:
GET /api-doc/openapi.json+GET /swagger-uiở dev/staging; production tắt cả hai để tránh leak schema và giảm attack surface. Internal team consume spec qua artifact CI build (fileopenapi.jsonexport ra GitHub Release). - BearerAuth security scheme: inject qua trait
Modify(structSecurityAddontrongopenapi.rs), apply per handler quasecurity(("BearerAuth" = [])). Swagger UI có nút "Authorize" cho dev paste token test endpoint. - Cargo.toml lock version:
utoipa 5,utoipa-swagger-ui 8(featureaxum),utoipa-axum 0.1— preview cho Group 6 trở đi khi handler thực tế xuất hiện.
Bài Tập Củng Cố
Tự trả lời, đáp án ở cuối:
- OpenAPI và Swagger có cùng nghĩa không? Phân biệt rõ hai term này về mặt lịch sử và phạm vi áp dụng hiện tại. Khi đồng nghiệp nói "viết Swagger" hoặc "deploy Swagger" thì thường ý là gì?
- Crate
utoipatheo workflow code-first hay API-first? Shop API chọn workflow nào và lý do? Liệt kê ưu/nhược điểm code-first so với API-first cho team có frontend làm song song. - Trong spec OpenAPI, section
components.schemasdùng để làm gì? Cho ví dụ một schemaProductđược dùng lại ở nhiều path endpoint. Tại sao nên dùng$refthay vì viết schema inline cho từng response? - Tại sao Shop API tắt Swagger UI ở production environment? Nêu ba lý do cụ thể. Internal team vẫn cần spec để consume API ở production — giải pháp là gì để không expose endpoint runtime mà vẫn có spec cho team?
- Cấu trúc
securitySchemescho JWT Bearer trong OpenAPI viết như thế nào (3 field bắt buộc)? Trong utoipa, làm thế nào để inject security scheme vào spec? Apply scheme đó cho từng handler ra sao? Endpoint public không cần auth có khai báosecurity(...)không?
Đáp án
- KHÔNG cùng nghĩa. OpenAPI là spec định dạng (chuẩn mô tả REST API qua JSON/YAML với root keys cố định), do OpenAPI Initiative dưới Linux Foundation maintain từ 2016 (rebrand từ Swagger Specification 2.0 do SmartBear donate). Phiên bản hiện hành OpenAPI 3.1 (2021). Swagger là tên brand tooling do SmartBear sở hữu: Swagger UI (render spec), Swagger Editor (IDE web edit YAML), Swagger Codegen (sinh client SDK). Khi đồng nghiệp nói "viết Swagger" thường ý là viết spec OpenAPI (YAML/JSON); "deploy Swagger" thường ý là deploy Swagger UI để render spec. Series và Shop API dùng đúng tên: OpenAPI cho spec, Swagger UI cho UI tool — tránh nhầm lẫn khi nói chuyện về architecture.
- Utoipa theo code-first: viết Rust handler + DTO trước, derive macro (
ToSchema,#[utoipa::path],#[derive(OpenApi)]) sinh OpenAPI spec từ code tại compile time. Shop API chọn code-first vì: (a) solo dev / small team không cần ceremony design-by-contract; (b) series dạy incremental, viết code trước rồi document sau là natural flow; (c) code = single source of truth, không bao giờ drift giữa spec và implementation. Ưu điểm code-first: không drift, refactor handler tự propagate sang spec, learning curve thấp cho Rust dev, CI dễ check. Nhược điểm so với API-first: không có giai đoạn "design contract" trước khi code → khó cho frontend/mobile làm song song nếu chưa có spec sớm; backend dev có thể accidentally đổi schema breaking client mà không nhận ra (cần CI test ngược lại). Nếu team có frontend/mobile làm song song và cần contract trước, API-first phù hợp hơn — viết YAML, codegen Rust skeleton quaoapi-codegen/openapi-generator-cli. - Section
components.schemaslà kho định nghĩa reusable DTO, mỗi entry là một schema có tên (vdProduct,User,ErrorResponse). Path endpoint reference qua$ref: '#/components/schemas/Product'thay vì viết schema inline. Ví dụ: schemaProductdùng lại ởGET /api/v1/products/{slug}(response 200),GET /api/v1/products(response 200 với arrayProduct),POST /api/v1/admin/products(response 201). Lợi ích$ref: (a) DRY — schema thay đổi chỉ sửa một chỗ ởcomponents.schemas.Product, mọi path tự cập nhật; (b) UI tool render gom nhóm — Swagger UI hiển thị section "Schemas" tách riêng cho dev browse cấu trúc DTO; (c) Codegen client SDK sinh struct/class Product một lần dùng ở nhiều endpoint method; (d) File size nhỏ hơn với spec lớn (~60 endpoint Shop API, mỗi DTO inline 3-5 chỗ → tăng nhân ba file size). - Shop API tắt Swagger UI ở production vì ba lý do: (1) Tránh leak schema chi tiết — Swagger UI expose toàn bộ endpoint, parameter, response shape, error code; đối thủ và attacker đọc spec map ra surface attack (vd thấy
POST /api/v1/admin/productsbiết có admin endpoint, thấyerror code: INVALID_TOKENbiết hệ thống có JWT). (2) Giảm attack surface — Swagger UI có lịch sử CVE liên quan XSS, prototype pollution, click-jacking; mỗi endpoint extra là bề mặt tiềm năng cho exploit. (3) Giảm binary size — không ship Swagger UI assets (~2MB JS/CSS embedded quautoipa-swagger-ui) cho production runtime. Solution cho internal team vẫn có spec mà không expose endpoint: artifact CI build. Tạo binary phụ (vdsrc/bin/export_openapi.rs) gọiApiDoc::openapi()serialize ra fileopenapi.json. GitHub Actions chạy binary này mỗi PR, upload file làm CI artifact, tag release attach file. Frontend/mobile/partner pull fileopenapi.jsontừ GitHub Release để codegen client SDK hoặc import Postman — không cần production endpoint. - Cấu trúc
securitySchemescho JWT Bearer có ba field bắt buộc:type: http,scheme: bearer,bearerFormat: JWT. YAML đầy đủ:components.securitySchemes.BearerAuth = { type: http, scheme: bearer, bearerFormat: JWT }. Trong utoipa, inject security scheme qua traitModify: implementimpl Modify for SecurityAddon { fn modify(&self, openapi: &mut OpenApi) { ... } }, trong methodmodifygọiopenapi.components.as_mut().unwrap().add_security_scheme("BearerAuth", SecurityScheme::Http(HttpBuilder::new().scheme(HttpAuthScheme::Bearer).bearer_format("JWT").build())), sau đó đăng ký vàoApiDocqua attribute#[openapi(modifiers(&SecurityAddon), ...)]. Apply scheme cho từng handler qua attributesecurity(("BearerAuth" = []))trong#[utoipa::path]. Mảng rỗng[]nghĩa không yêu cầu scope cụ thể (nếu dùng OAuth2 thì điền scope vào trong mảng vd("OAuth2" = ["read:products", "write:products"])). Endpoint public không cần auth KHÔNG khai báosecurity(...)— utoipa hiểu là endpoint không yêu cầu security scheme. Trên Swagger UI, endpoint cósecurity(...)hiện icon ổ khóa và "Try it out" auto-attachAuthorization: Bearer <token>sau khi user click "Authorize" paste token; endpoint không có security không cần token để gọi.
Bài Tiếp Theo
Bài 9: Tool Test API: Postman, curl, HTTPie — giới thiệu các tool CLI/GUI để test API nhanh: curl (universal, verbose mode, header/body/auth flag đầy đủ), HTTPie (syntax thân thiện cho dev, color output, plugin), Postman (collection, environment variable, scripting), Bruno (open source alternative giữ file .bru trong git). So sánh nhanh, khi nào dùng tool nào trong workflow phát triển Shop API.
