This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
AxumKit is a production-ready Rust web API template built on Axum, SeaORM, PostgreSQL, Redis, and OAuth2. It implements JWT-based authentication with session management, OAuth2 integrations (Google, GitHub), and Cloudflare R2 storage.
cargo run # Start the development server (localhost:8000)
cargo build # Build the project
cargo test # Run all testscd 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 migrations
cargo run -- refresh # Rollback all, then reapply all migrations
cargo run -- status # Check migration status
cargo run -- generate <NAME> # Generate new migration file- Swagger UI: http://localhost:8000/docs (debug builds only)
- OpenAPI JSON: http://localhost:8000/swagger.json (debug builds only)
The project follows a layered architecture with clear separation of concerns:
API Layer (src/api/)
↓
Service Layer (src/service/)
↓
Repository Layer (src/repository/)
↓
Entity Layer (src/entity/)
AppState (src/state.rs): Application-wide shared state containing:
conn: PostgreSQL database connection (SeaORM)redis_client: Redis connection manager for sessions/cachinghttp_client: reqwest HTTP client for OAuth2 and external APIs
Configuration (src/config/db_config.rs):
- Centralized config using
LazyLockfor environment variables - Access via
DbConfig::get()- returns static reference - Loaded from
.envfile on first access
Error Handling (src/errors/):
- Centralized error system with
Errorsenum - Domain-specific error handlers (user, oauth, session, password, etc.)
- Errors automatically convert to HTTP responses via
IntoResponse - Development mode shows detailed error info; production mode hides it
- Standard result types:
ServiceResult<T>andApiResult<T>
Authentication Flow:
- Session-based auth using Redis (not JWT tokens in production)
- Session data stored as
session:{session_id}in Redis with TTL session_authmiddleware extracts session from cookies and validates via RedisSessionContext(containinguser_idandsession_id) injected into request extensions- Handlers extract
SessionContextfrom request to get authenticated user info - OAuth2 providers (Google, GitHub) create sessions after successful authentication
Middleware (src/middleware/):
session_auth: Validates session cookie and loadsSessionContextanonymous_user_middleware: Handles unauthenticated requestscors_layer: CORS configuration from environment
DTOs (src/dto/):
- Organized by domain:
auth/,oauth/,user/ - Each domain has
request/,response/, andinternal/subdirectories - Internal DTOs (e.g.,
Session,SessionContext) not exposed in API
Services (src/service/):
- Business logic layer
SessionService: Create/get/delete/refresh Redis sessionsauth/: Login, logout, session managementoauth/: OAuth2 flow handling for Google and GitHubvalidator/: Request validation (form and JSON)
Repositories (src/repository/):
- Database query layer
- Pattern:
find_by_*returnsOption<Model>,get_by_*returnsResult<Model, Errors> - Organized by domain (user, oauth)
Routes are versioned and organized by domain:
/v0/health/* - Health check endpoints
/v0/auth/* - Authentication endpoints (login, logout, OAuth)
/docs - Swagger UI (debug builds only)
/swagger.json - OpenAPI spec (debug builds only)
OpenAPI documentation is auto-generated using utoipa:
- Define schemas with
#[derive(ToSchema)] - Document endpoints with
#[utoipa::path(...)] - Register in
src/api/v0/routes/openapi.rs
Required environment variables (see .env.example):
ENVIRONMENT: Set to "dev" or "development" for development modeJWT_SECRET: Secret key for JWT signingPOSTGRES_*: PostgreSQL connection settingsREDIS_HOST,REDIS_PORT: Redis connectionHOST,PORT: Server bind addressGOOGLE_CLIENT_ID,GOOGLE_CLIENT_SECRET,GOOGLE_REDIRECT_URI: Google OAuthGITHUB_CLIENT_ID,GITHUB_CLIENT_SECRET,GITHUB_REDIRECT_URI: GitHub OAuthR2_*: Cloudflare R2 storage credentialsCORS_ALLOWED_ORIGINS,CORS_ALLOWED_HEADERS: CORS configuration
- 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}/
- Entities are in
src/entity/and generated from migrations - Use SeaORM's
DeriveEntityModelfor entities - Database operations go through repository layer, not directly in handlers
- Use transactions for multi-step database operations
Sessions are stored in Redis with the key pattern session:{session_id}:
- Create:
SessionService::create_session() - Retrieve:
SessionService::get_session() - Delete:
SessionService::delete_session() - Refresh:
SessionService::refresh_session()
To access authenticated user in handlers:
pub async fn handler(
Extension(session): Extension<SessionContext>,
) -> Result<Response, Errors> {
let user_id = session.user_id; // UUID
// ...
}Return Errors from services and handlers - they auto-convert to HTTP responses:
pub async fn handler() -> Result<Json<Response>, Errors> {
let user = repository::get_by_id(&conn, id)
.await
.map_err(|_| Errors::UserNotFound)?;
Ok(Json(response))
}OAuth flow:
- Generate auth URL with state (stored in Redis)
- User authorizes with provider
- Provider redirects back with code and state
- Verify state, exchange code for token
- Fetch user info from provider
- Find or create user account
- Create session and return session cookie
When writing tests:
- Unit tests should be in the same file as the code they test
- Integration tests go in
tests/directory - Use
cargo testto run all tests - Mock external dependencies (database, Redis, HTTP) in tests