Skip to content

Study Guide

This guide provides a recommended reading order for understanding the AxumKit codebase. Follow the sequence below — each step builds on the previous one.

Overview

AxumKit is a Rust monorepo with 7 internal crates. The dependency graph flows like this:

axumkit-config ─────────────────────┐
axumkit-constants ──────────────────┤
axumkit-entity ─────────────────────┤
axumkit-errors ─────────────────────┼──▶ axumkit-server
axumkit-dto ────────────────────────┤       │
                                    │       ▼
                                    └──▶ axumkit-worker

Step 1: Configuration (axumkit-config)

Start here. This crate is the simplest and shows how the entire app is configured.

FileWhat to learn
src/server_config.rsLazyLock pattern for static config, require! macro, env var loading

Key takeaways:

  • All config is loaded once via ServerConfig::get() (returns &'static ServerConfig)
  • Missing required env vars are collected and panic'd at startup — fail-fast pattern
  • Optional vars use .unwrap_or() with sensible defaults
  • Two Redis instances: session (persistent, AOF) and cache (volatile, LRU)
  • Write/Read database separation for PostgreSQL

Step 2: Entities (axumkit-entity)

Database models. Read these to understand the data layer.

FileWhat to learn
src/users.rsUser model with TOTP fields, optional password (OAuth users)
src/posts.rsPost model with storage_key pointing to SeaweedFS
src/user_oauth_connections.rsOAuth provider linking, OAuthProvider enum
src/action_logs.rsAudit log with IpNetwork type, JSONB metadata
src/common.rsShared enums: OAuthProvider, ActionResourceType

Key takeaways:

  • UUIDs everywhere (v7 for time-ordering)
  • password is Option<String> — OAuth-only users have no password
  • totp_backup_codes uses PostgreSQL array type
  • Relations defined via SeaORM's DeriveRelation

Step 3: Error System (axumkit-errors)

The centralized error handling system. This is a critical architectural pattern.

FileWhat to learn
src/errors.rsErrors enum, IntoResponse impl, handler chain pattern
src/protocol.rsError code string constants (e.g., "user:not_found")
src/handlers/*.rsDomain-specific map_response() and log_error() functions

Key takeaways:

  • Every Errors variant auto-converts to an HTTP response
  • Handler chain: each domain handler gets a chance to match the error
  • Error codes follow domain:operation format
  • details field only shows in dev mode (ServerConfig::get().is_dev)
  • ServiceResult<T> is Result<T, Errors> — used everywhere

Step 4: DTOs (axumkit-dto)

Request/response types organized by domain.

DirectoryWhat to learn
src/auth/request/Login, TOTP verify, password reset request shapes
src/auth/response/Login response with TOTP-required flag
src/oauth/request/OAuth code exchange, link/unlink requests
src/posts/request/Post creation, pagination queries
src/search/request/Search query params
src/validator/ValidatedJson, ValidatedQuery — request validation wrappers

Key takeaways:

  • All DTOs derive Serialize/Deserialize + ToSchema (for OpenAPI)
  • Validation via validator crate with #[validate] attributes
  • Custom extractors (ValidatedJson, ValidatedQuery) validate before handler runs

Step 5: Constants (axumkit-constants)

Shared constants used by both server and worker.

FileWhat to learn
src/action_log_actions.rsActionLogAction enum with "resource:operation" format
src/nats_subjects.rsNATS subject for realtime events
src/storage_keys.rsR2/SeaweedFS key prefixes, image size limits

Step 6: Server (axumkit-server)

The main API server. This is the largest crate. Read in this order:

6a. State & Connections

FileWhat to learn
src/state.rsAppState struct — all shared resources
src/connection/*.rsHow each external service is connected
src/main.rsServer startup, middleware stack assembly

6b. Middleware

FileWhat to learn
src/middleware/anonymous_user.rsAnonymous user cookie (UUIDv7, 365-day TTL)
src/middleware/cors.rsCORS from env vars
src/middleware/rate_limit.rsSliding window with Redis Lua script
src/middleware/stability.rsConcurrency limit, buffer, timeout (Tower layers)
src/middleware/trace_layer_config.rsRequest ID propagation

Middleware stack order (outermost to innermost):

Request ID → Trace → CORS → Stability → Cookie → Anonymous User → Routes

6c. Extractors

FileWhat to learn
src/extractors/session.rsRequiredSession / OptionalSession — session from cookie → Redis
src/extractors/turnstile.rsCloudflare Turnstile bot protection

6d. Routes (API Layer)

DirectoryWhat to learn
src/api/routes.rsTop-level router: health + /v0 + Swagger
src/api/v0/routes/auth/routes.rsAll auth endpoints
src/api/v0/routes/posts/routes.rsCRUD endpoints
src/api/v0/routes/user/routes.rsProfile management
src/api/v0/routes/search/routes.rsMeiliSearch query endpoints
src/api/v0/routes/stream/routes.rsSSE event stream

6e. Services (Business Logic)

DirectoryWhat to learn
src/service/auth/session.rsRedis session CRUD, sliding TTL refresh
src/service/auth/login.rsPassword verification, TOTP check, session creation
src/service/oauth/OAuth2 flow: authorize URL → code exchange → find/create user
src/service/posts/Post CRUD with SeaweedFS storage

6f. Repository (Database Layer)

PatternExample
find_by_*Returns Option<Model>
get_by_*Returns Result<Model, Errors> (errors if not found)

6g. Bridge (Server → Worker Communication)

FileWhat to learn
src/bridge/worker_client/email.rsPublish email jobs to NATS
src/bridge/worker_client/index.rsPublish search index jobs
src/bridge/worker_client/storage.rsPublish delete content jobs

Step 7: Worker (axumkit-worker)

Background job processor. Read after understanding the server.

FileWhat to learn
src/main.rsWorker startup, consumer spawning
src/nats/streams.rsJetStream stream definitions
src/nats/consumer.rsGeneric consumer with retry/backoff
src/jobs/email/mod.rsEmail rendering (MRML + MiniJinja) and sending
src/jobs/index/post.rsMeiliSearch post indexing
src/jobs/cron/mod.rsCron scheduler setup
src/jobs/cron/sitemap.rsSitemap generation and R2 upload

Key takeaways:

  • NatsConsumer is a generic consumer: deserialize → handle → ack/nak
  • Exponential backoff: 1s, 2s, 4s, 8s, 16s (5 retries)
  • Concurrency controlled via Semaphore
  • Cron jobs: cleanup (Sat 4AM), sitemap (Sun 3AM), orphan cleanup (Fri 5AM)

After reading through the codebase:

  1. Add a new field to the User entity — touch entity, migration, DTO, service, repository
  2. Add a new API endpoint — follow the existing pattern in api/v0/routes/
  3. Add a new worker job — define stream, publisher (bridge), consumer
  4. Write an e2e test — see crates/e2e/ for existing examples

Released under the MIT License.