A production-ready template for building high-performance web APIs with Rust. Built with Axum, SeaORM, PostgreSQL, Redis, and OAuth2.
- High-performance Web Server: Built on Axum, a fast and modular web framework
- Type-safe ORM: SeaORM 2.0 for database operations with compile-time guarantees
- PostgreSQL Support: Robust relational database with connection pooling
- Session-Based Authentication: Secure Redis-backed session management with HTTP-only cookies
- OAuth2 Integration: Pre-configured Google and GitHub OAuth2 authentication flows
- Redis Caching: Session storage, rate limiting, and caching layer
- Cloudflare R2 Support: S3-compatible object storage integration
- RESTful API: Well-structured, versioned endpoints (v0) following best practices
- OpenAPI Documentation: Auto-generated API docs with Swagger UI (debug builds only)
- Layered Architecture: Clean separation: API → Service → Repository → Entity
- Centralized Error Handling: Domain-specific error handlers with development/production modes
- Rate Limiting: Per-route, per-IP rate limiting via Redis
- Request Validation: JSON and multipart validation using validator crate
- Security Features: Argon2 password hashing, CORS configuration, secure cookie management
- Async/Await: Fully asynchronous for maximum performance with Tokio runtime
- Rust (latest stable version recommended)
- PostgreSQL (18+)
- Redis (8.0+)
- Cargo (Rust's package manager)
- Docker and Docker Compose (optional, for containerized development)
-
Clone the repository:
git clone https://github.com/shiueo/AxumKit.git cd AxumKit -
Set up environment variables:
cp .env.example .env
Update the
.envfile with your credentials. Required variables:# Environment ENVIRONMENT=dev # Security (generate with: python -c "import secrets; print(secrets.token_urlsafe(64))") JWT_SECRET=your_secret_key_here # PostgreSQL POSTGRES_USER=postgres POSTGRES_PASSWORD=your_password POSTGRES_HOST=localhost POSTGRES_PORT=5432 POSTGRES_NAME=axumkit POSTGRES_MAX_CONNECTION=100 POSTGRES_MIN_CONNECTION=10 # Redis REDIS_HOST=localhost REDIS_PORT=6379 REDIS_TTL=3600 # Server HOST=127.0.0.1 PORT=8000 # Session (hours) AUTH_SESSION_EXPIRE_TIME=24 AUTH_EMAIL_VERIFICATION_TOKEN_EXPIRE_TIME=1 AUTH_PASSWORD_RESET_TOKEN_EXPIRE_TIME=1 # Google OAuth2 (get from https://console.cloud.google.com/) GOOGLE_CLIENT_ID=your_google_client_id GOOGLE_CLIENT_SECRET=your_google_client_secret GOOGLE_REDIRECT_URI=http://localhost:5173/auth/google/callback # GitHub OAuth2 (get from https://github.com/settings/developers) GITHUB_CLIENT_ID=your_github_client_id GITHUB_CLIENT_SECRET=your_github_client_secret GITHUB_REDIRECT_URI=http://localhost:5173/auth/github/callback # Cloudflare R2 (optional, for file storage) R2_PUBLIC_DOMAIN=your_r2_public_domain R2_ACCOUNT_ID=your_r2_account_id R2_BUCKET_NAME=your_bucket_name R2_ACCESS_KEY_ID=your_access_key_id R2_SECRET_ACCESS_KEY=your_secret_access_key # CORS CORS_ALLOWED_ORIGINS=http://localhost:5173 CORS_ALLOWED_HEADERS=Content-Type,Authorization CORS_MAX_AGE=86400
-
Install dependencies and build:
cargo build
-
Run database migrations:
cd migration cargo run cd ..
-
Start the server:
cargo run
-
Access the API documentation (debug builds only):
http://localhost:8000/docs
This project includes Docker configuration for easy development and deployment.
- Docker Engine 20.10.0+
- Docker Compose 2.0.0+
If you just want to build the Docker image without running it:
docker-compose buildThis will build all services defined in the docker-compose.yml file.
-
Build and start the application:
docker-compose up --build
-
For running in detached mode:
docker-compose up -d --build
-
View logs:
docker-compose logs -f
-
Stop the application:
docker-compose down
-
Stop and remove all containers, networks, and volumes:
docker-compose down -v
- app: The main application server (port 8000)
- db: PostgreSQL database (port 5432)
- redis: Redis cache server (port 6379)
- migrate: Runs database migrations on startup
You can configure the application by creating a .env file in the project root. See the Installation section above for all required variables.
- The application will automatically reload when source code changes
- Database migrations run automatically on startup
- PostgreSQL and Redis data persist in Docker volumes
src/
├── api/ # API routes and handlers
│ └── v0/ # API version 0
│ └── routes/ # Route definitions
│ ├── auth/ # Authentication endpoints (login, logout, OAuth)
│ └── health/ # Health check endpoints
├── config/ # Application configuration
│ └── db_config.rs # Environment-based configuration with LazyLock
├── connection/ # External service connections
│ ├── database_conn.rs # PostgreSQL connection pool
│ ├── redis_conn.rs # Redis connection manager
│ ├── http_conn.rs # HTTP client for OAuth
│ └── r2_conn.rs # Cloudflare R2 client
├── dto/ # Data Transfer Objects (organized by domain)
│ ├── auth/ # Authentication DTOs
│ │ ├── request/ # Login request DTOs
│ │ ├── response/ # Login/logout response DTOs
│ │ └── internal/ # Session, SessionContext, AnonymousUser
│ ├── oauth/ # OAuth2 DTOs
│ │ ├── request/ # Google/GitHub login requests
│ │ ├── response/ # OAuth authorization URL response
│ │ └── internal/ # OAuth state, user result
│ └── user/ # User DTOs
│ ├── request/ # User creation request
│ └── response/ # User response DTOs
├── entity/ # SeaORM database entities
│ ├── users.rs # User entity
│ ├── user_oauth_connections.rs # OAuth provider links
│ └── user_refresh_tokens.rs # Refresh tokens (future use)
├── errors/ # Centralized error handling
│ ├── errors.rs # Error enum with IntoResponse
│ ├── protocol.rs # Error protocol
│ └── handlers/ # Domain-specific error handlers
│ ├── user_handler.rs
│ ├── oauth_handler.rs
│ ├── session_handler.rs
│ └── ...
├── extractors/ # Axum request extractors
│ └── session.rs # RequiredSession and OptionalSession
├── middleware/ # Axum middleware
│ ├── auth.rs # Session auth middleware (deprecated)
│ ├── anonymous_user.rs # Anonymous user ID assignment
│ ├── rate_limit.rs # Rate limiting per IP/route
│ └── cors.rs # CORS configuration
├── repository/ # Data access layer
│ ├── user/ # User database operations
│ │ ├── find_by_*.rs # Returns Option<Model>
│ │ └── get_by_*.rs # Returns Result<Model, Errors>
│ └── oauth/ # OAuth connection operations
├── service/ # Business logic layer
│ ├── auth/ # Authentication services
│ │ ├── login.rs # Email/password login
│ │ ├── logout.rs # Session termination
│ │ └── session.rs # Session CRUD operations
│ ├── oauth/ # OAuth2 services
│ │ ├── provider/ # Provider-specific OAuth clients
│ │ │ ├── google/
│ │ │ └── github/
│ │ ├── google_sign_in.rs # Complete Google OAuth flow
│ │ └── github_sign_in.rs # Complete GitHub OAuth flow
│ └── validator/ # Request validation
│ ├── json_validator.rs
│ └── form_validator.rs
├── utils/ # Utility functions
│ ├── crypto.rs # Argon2 password hashing
│ ├── extract_ip_address.rs
│ ├── extract_user_agent.rs
│ ├── logger.rs # Tracing initialization
│ ├── redis_cache.rs # Redis cache operations
│ └── image_processor/ # Image processing utilities
├── main.rs # Application entry point
├── state.rs # AppState (PostgreSQL, Redis, HTTP client)
└── lib.rs # Library root
migration/ # SeaORM migrations
├── src/
│ ├── m20250515_*.rs # Create users table
│ ├── m20250521_*.rs # Add refresh tokens
│ └── m20250531_*.rs # Add user_agent to refresh_tokens
└── Cargo.toml
This project uses utoipa to automatically generate OpenAPI documentation and serve it via Swagger UI.
Note: API documentation is only available in debug builds (when ENVIRONMENT=dev or ENVIRONMENT=development).
- Swagger UI:
http://localhost:8000/docs - OpenAPI JSON:
http://localhost:8000/swagger.json
All endpoints are versioned under /v0:
GET /v0/health_check - Server health status
POST /v0/auth/login - Email/password login
Request: { "email": "[email protected]", "password": "password123" }
Response: 204 No Content (sets session_id cookie)
POST /v0/auth/logout - Logout (requires authentication)
Response: 204 No Content (clears session_id cookie)
GET /v0/auth/google/authorize?redirect_uri=<uri> - Get authorization URL
Response: { "auth_url": "https://accounts.google.com/..." }
POST /v0/auth/google - Complete OAuth flow
Request: { "code": "auth_code", "state": "state_value", "handle": "username" }
Response: 204 No Content (sets session_id cookie)
GET /v0/auth/github/authorize?redirect_uri=<uri> - Get authorization URL
Response: { "auth_url": "https://github.com/login/oauth/..." }
POST /v0/auth/github - Complete OAuth flow
Request: { "code": "auth_code", "state": "state_value", "handle": "username" }
Response: 204 No Content (sets session_id cookie)
The following routes have rate limiting (10 requests per minute per IP):
/v0/auth/google/authorize/v0/auth/google/v0/auth/github/authorize/v0/auth/github/v0/auth/login
- Session-Based: Sessions are stored in Redis with HTTP-only cookies
- Session Cookie:
session_id(HTTP-only, Secure, SameSite) - Session TTL: Configurable via
AUTH_SESSION_EXPIRE_TIME(default: 24 hours) - Protected Routes: Use
RequiredSessionextractor for authentication
Example protected handler:
pub async fn protected_handler(
RequiredSession(session): RequiredSession,
) -> Result<Json<Response>, Errors> {
let user_id = session.user_id; // UUID of authenticated user
// Handler implementation
Ok(Json(response))
}- Client requests authorization URL from
/v0/auth/{provider}/authorize - User is redirected to provider (Google/GitHub) to authorize
- Provider redirects back to client with
codeandstate - Client sends
codeandstateto/v0/auth/{provider} - Server verifies state, exchanges code for access token
- Server fetches user info from provider
- Server creates or links user account
- Server creates session and returns
session_idcookie
This project follows a layered architecture with clear separation of concerns:
HTTP Request
↓
API Layer (src/api/)
↓
Service Layer (src/service/)
↓
Repository Layer (src/repository/)
↓
Entity Layer (src/entity/)
↓
Database (PostgreSQL)
AppState (src/state.rs)
- Shared application state containing:
conn: PostgreSQL connection pool (SeaORM)redis_client: Redis connection managerhttp_client: HTTP client for OAuth and external APIs
Configuration (src/config/db_config.rs)
- Centralized config using
LazyLockfor lazy initialization - Loaded from
.envfile on first access - Access via
DbConfig::get()- returns&'static DbConfig
Error Handling (src/errors/)
- Centralized error system with
Errorsenum - Domain-specific error handlers (user, oauth, session, password, etc.)
- Automatic conversion to HTTP responses via
IntoResponse - Development mode shows detailed errors; production mode hides them
Session Management (src/service/auth/session.rs)
- Redis-backed sessions with pattern:
session:{session_id} - Session data:
user_id,session_id,created_at,expires_at,user_agent,ip_address - Services:
create_session,get_session,refresh_session,delete_session
Middleware (src/middleware/)
anonymous_user_middleware: Assigns anonymous user IDs to all requestsrate_limit: Per-route, per-IP rate limiting via Rediscors_layer: CORS configuration from environment variables
Repository Pattern
find_by_*(): ReturnsOption<Model>(no error if not found)get_by_*(): ReturnsResult<Model, Errors>(errors if not found)
DTO Organization (src/dto/)
- Organized by domain:
auth/,oauth/,user/ - Each domain has:
request/,response/,internal/subdirectories - Internal DTOs (e.g.,
Session,SessionContext) are not exposed in API
Migrations are managed in the migration/ directory:
cd migration
cargo run # Apply all pending migrations
cargo run -- up # Same as above
cargo run -- down # Rollback last migration
cargo run -- fresh # Drop all tables and reapply
cargo run -- refresh # Rollback all, then reapply
cargo run -- status # Check migration status
cargo run -- generate <NAME> # Generate new migration- Create handler in
src/api/v0/routes/{domain}/{handler}.rs - Add
#[utoipa::path(...)]annotation for OpenAPI docs - Register route in
src/api/v0/routes/{domain}/routes.rs - Add schemas to
src/api/v0/routes/{domain}/openapi.rs - Create DTOs in
src/dto/{domain}/request/andresponse/ - Implement service logic in
src/service/{domain}/ - Add repository functions in
src/repository/{domain}/
This project is licensed under the MIT License - see the LICENSE file for details.