A production-ready Task Management REST API built with Rust, Axum, and SQLx, designed for team collaboration and task tracking. This project demonstrates clean architecture, async Rust patterns, and scalable backend design.
- 🏗️ Clean Architecture – Organized into Models, Controllers, Services, Routes, and Middleware
- 🔄 API Versioning –
/api/v1ready - 🔐 JWT Cookie Authentication (Implemented) – Register, login, logout, and protected
meendpoint - 🔒 Google OAuth (Planned/Partial) – User model and login checks are ready for OAuth users
- 🗃️ Database Migrations – SQLx-powered migrations for PostgreSQL
- ⚡ Async/Await – Powered by Tokio runtime for high-performance APIs
- 📝 Full CRUD – Tasks, Projects, Users, Workspaces
- 🩺 Health Check –
/healthendpoint for monitoring - 📦 Consistent Responses – Standardized JSON format across all endpoints
| Technology | Purpose |
|---|---|
| Rust | Programming language |
| Axum | Web framework |
| SQLx | Async SQL toolkit with compile-time query check |
| Tokio | Async runtime |
| PostgreSQL | Relational database |
| Serde | Serialization/deserialization |
| Chrono | Date and time handling |
| dotenvy | Environment variable management |
| JWT | Token-based authentication |
| OAuth2 | Google authentication |
| Tower / Tower-HTTP | Middleware and utilities |
src/
├── controllers/
│ ├── auth_controller.rs # register/login/logout/me handlers
│ └── root_controller.rs # health check
├── dtos/
│ └── auth_dto.rs # request validation + auth response DTO
├── middleware/
│ └── auth.rs # JWT cookie auth extractor for protected routes
├── routes/
│ ├── auth_routes.rs # /api/v1/auth/* route definitions
│ └── mod.rs # route composition + static file fallback
├── utils/
│ ├── response.rs # ApiResponse<T> success wrapper
│ ├── api_error.rs # ApiError enum -> HTTP error responses
│ ├── jwt.rs # token generation and verification
│ ├── hash.rs # Argon2 password hash + verify
│ └── mod.rs # utility exports + validation error formatter
├── db/
│ └── mod.rs # DB pool + automatic SQLx migrations at startup
└── main.rs # app bootstrap, middleware layers, state wiringBase path: /api/v1/auth
-
POST /register- Validates payload (
name,email,password,confirm_password) - Checks duplicate email
- Hashes password with Argon2
- Creates user in PostgreSQL
- Generates JWT and stores it in an HTTP-only cookie named
token - Returns normalized
ApiResponse<AuthUserResponse>
- Validates payload (
-
POST /login- Validates payload
- Finds user by email
- Blocks password login for Google-only users (
google_idpresent) - Verifies password against Argon2 hash
- Generates JWT and updates
tokencookie - Returns normalized
ApiResponse<AuthUserResponse>
-
POST /logout- Clears auth cookie (
token) - Returns normalized
ApiResponse<()>
- Clears auth cookie (
-
GET /me- Protected route using
AuthUserextractor (src/middleware/auth.rs) - Reads and verifies JWT from cookie
- Loads user from DB
- Returns current authenticated user profile in
ApiResponse<AuthUserResponse>
- Protected route using
This project is educational, so the utility layer is intentionally explicit and reusable for future modules (tasks, projects, workspaces).
| File | What it does | Why it matters for future features |
|---|---|---|
src/utils/response.rs |
Defines ApiResponse<T> with success, status_code, message, data |
Gives every endpoint a predictable success payload shape |
src/utils/api_error.rs |
Defines ApiError (BadRequest, Unauthorized, NotFound, Conflict, InternalServerError) and converts it into HTTP responses |
Centralizes error-to-status mapping so controllers stay clean |
src/utils/jwt.rs |
Generates and verifies JWT claims (sub, exp) |
Reusable token logic for any protected resource |
src/utils/hash.rs |
Hashes passwords (Argon2 + random salt) and verifies passwords | Security best practice reusable for account/password features |
src/utils/mod.rs |
Exports utility modules and formats validator errors into readable strings | Keeps validation messages user-friendly and consistent |
- Request enters route (
src/routes/auth_routes.rs) and is handled byauth_controller. - DTO validation runs (
validatorcrate insrc/dtos/auth_dto.rs). - If validation fails, controller maps errors using
format_validation_errors(...)and returnsApiError::BadRequest(...). - If business/database logic fails, controller returns another
ApiErrorvariant. ApiErrorimplementsIntoResponse, so Axum converts it into a standardized HTTP error payload.- If success, controller returns
Json(ApiResponse<T>)fromsrc/utils/response.rs. - For protected routes,
AuthUsermiddleware extractor insrc/middleware/auth.rsreadstokenfrom cookies, verifies JWT usingutils/jwt.rs, fetches the user from DB, and injects authenticatedUserinto the handler.
Success shape:
{
"success": true,
"status_code": 200,
"message": "User logged in successfully",
"data": {
"id": "uuid",
"email": "user@example.com",
"name": "User"
}
}Error shape:
{
"success": false,
"status_code": 400,
"message": "email: Invalid email format, password: Password must be between 6 and 12 characters",
"data": null
}- Cookie name:
token HttpOnly: enabled (protects token from client-side JS access)SameSite:LaxSecure: enabled whenDEV_MODE != development- Signing secret:
JWT_SECRETfrom environment
This makes auth state server-trusted and easy to consume from browser clients.
- install sqlx-cli
cargo install sqlx-cli --no-default-features --features postgres- create a migration
sqlx migrate add init_schema- run migrations
sqlx migrate run
# this step run is the manual way
# `src/db/mod.rs` has the code to run migrations automatically when the app starts+------------+ +-------------+ +----------+
| users | | workspaces | | projects |
+------------+ +-------------+ +----------+
| id |<-------| owner_id | | id |
| email | | name |<-------| workspace_id
| name | | created_at | | name
| google_id | +-------------+ | description
+------------+ +----------+
| |
| |
| |
v v
+-------------+ +----------------+ +----------------+
| tasks |<---| task_assignees |--->| users |
+-------------+ +----------------+ +----------------+
| id | | task_id | | id |
| project_id | | user_id | +----------------+
| title | +----------------+
| description |
| status_id |----> task_status
| priority_id |----> task_priority
| owner_id |
+-------------+
|
v
+-------------+
| comments |
+-------------+
| id |
| task_id |
| user_id |
| content |
+-------------+