Tính năng | Node.js | Rust |
---|---|---|
Quản lý thư viện | npm / yarn | cargo |
Khởi tạo dự án | npm init | cargo new <tên dự án> |
Thêm thư viện | npm install express | cargo add axum |
Chạy ứng dụng | node server.js | cargo run |
Quản lý phụ thuộc | package.json | Cargo.toml |
Công cụ build | Thường cần build tools bên ngoài (webpack, babel...) | Tích hợp sẵn trong cargo |
use axum::{routing::get, Router};
#[tokio::main]async fn main() { let app = Router::new() .route("/", get(|| async { "Hello World" }));
let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") .await .unwrap();
println!("Server running on port 3000"); axum::serve(listener, app).await.unwrap();}
#[tokio::main]
và xử lý lỗi với .unwrap()
, đồng thời đảm bảo compile-time an toàn và hiệu quả.app.get('/users/:id', (req, res) => { const userId = req.params.id; res.json({ id: userId });});
use axum::extract::Path;
async fn get_user(Path(user_id): Path<String>) -> String { format!("User ID: {}", user_id)}
// Đăng ký route trong main():// .route("/users/:id", get(get_user))
app.use(express.json());
app.post('/users', (req, res) => { const { name, email } = req.body; const user = { id: 1, name, email }; res.json(user);});
use axum::extract::Json;use serde::{Deserialize, Serialize};
#[derive(Deserialize)]struct CreateUserRequest { name: String, email: String,}
#[derive(Serialize)]struct User { id: u32, name: String, email: String,}
async fn create_user(Json(request): Json<CreateUserRequest>) -> Json<User> { let user = User { id: 1, name: request.name, email: request.email, }; Json(user)}
app.get('/divide/:a/:b', (req, res) => { const a = parseInt(req.params.a); const b = parseInt(req.params.b); if (b === 0) { return res.status(400).json({ error: 'Division by zero' }); } res.json({ result: a / b });});
use axum::http::StatusCode;use axum::response::{IntoResponse, Response};
async fn divide(Path((a, b)): Path<(i32, i32)>) -> Response { if b == 0 { return (StatusCode::BAD_REQUEST, "Division by zero").into_response(); } Json(json!({ "result": a / b })).into_response()}
rust-api/├── Cargo.toml├── src/│ ├── main.rs│ ├── handlers/│ │ ├── mod.rs│ │ ├── users.rs│ │ └── auth.rs│ ├── models/│ │ ├── mod.rs│ │ └── user.rs│ └── config.rs
mod.rs
dùng làm entry point cho mỗi module.Mục Đích | Rust | Node.js |
---|---|---|
Framework Web | axum | express |
Async Runtime | tokio | built-in |
JSON Serialize | serde | JSON.parse/Stringify |
HTTP Client | reqwest | axios |
ORM Database | sqlx | prisma |
Biến Môi Trường | dotenv | dotenv |
use axum::{ extract::{Path, Json, State}, http::StatusCode, response::{IntoResponse, Response}, routing::{get, post}, Router,};use serde::{Deserialize, Serialize};use std::collections::HashMap;use std::sync::{Arc, Mutex};
#[derive(Serialize, Deserialize, Clone)]struct User { id: u32, name: String, email: String,}
type UserStore = Arc<Mutex<HashMap<u32, User>>>;
#[tokio::main]async fn main() { let store = Arc::new(Mutex::new(HashMap::new()));
let app = Router::new() .route("/users", get(list_users).post(create_user)) .route("/users/:id", get(get_user)) .with_state(store);
let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") .await .unwrap();
println!("Server running on http://127.0.0.1:3000"); axum::serve(listener, app).await.unwrap();}
async fn list_users(State(store): State<UserStore>) -> Json<Vec<User>> { let users = store.lock().unwrap(); let user_list: Vec<User> = users.values().cloned().collect(); Json(user_list)}
async fn get_user(Path(id): Path<u32>, State(store): State<UserStore>) -> Response { let users = store.lock().unwrap(); match users.get(&id) { Some(user) => Json(user.clone()).into_response(), None => StatusCode::NOT_FOUND.into_response(), }}
async fn create_user(State(store): State<UserStore>, Json(mut user): Json<User>) -> Response { let mut users = store.lock().unwrap(); let id = users.len() as u32 + 1; user.id = id; users.insert(id, user.clone()); (StatusCode::CREATED, Json(user)).into_response()}
# Tạo dự án mớicargo new my-apicd my-api
# Thêm thư việncargo add axum tokio serde --features tokio/full,serde/derive
# Kiểm tra biên dịch (không chạy)cargo check
# Chạy và tự rebuild khi có thay đổicargo watch -x run
# Build bản release tối ưucargo build --release
#[cfg(test)]mod tests { use super::*; use axum::body::Body; use axum::http::{Method, Request, StatusCode}; use tower::ServiceExt; // cho oneshot #[tokio::test] async fn test_create_user() { let store = Arc::new(Mutex::new(HashMap::new())); let app = Router::new() .route("/users", post(create_user)) .with_state(store);
let request = Request::builder() .method(Method::POST) .uri("/users") .header("content-type", "application/json") .body(Body::from(r#"{"id":0,"name":"Test","email":"[email protected]"}"#)) .unwrap();
let response = app.oneshot(request).await.unwrap();
assert_eq!(response.status(), StatusCode::CREATED); }}
FROM rust:1.70 AS builderWORKDIR /appCOPY . .RUN cargo build --release
FROM debian:bookworm-slimRUN apt-get update && apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/*COPY --from=builder /app/target/release/my-api /usr/local/bin/my-apiEXPOSE 3000CMD ["my-api"]