This is a baker template for quickly scaffolding a Rust backend powered by axum.
- What does this template do?
- Getting Started
- Baker Questions
- Defining Entities for CRUD Operations
- Schema Usage in Migrations
- Primary Key / Foreign Key Helper Macros
- Docker Multi-Architecture Support
- Why Are Some Structs Red in the IDE?
- Features
- License
- Simple project: If you just want a basic project structure, you can generate a minimal backend using axum.
- Full CRUD application: If you define your entities in the baker prompts, this template will generate a complete CRUD backend with sea-orm models, controllers, and migrations.
Follow the installation instructions for baker.
baker https://github.com/dinosath/axum-template path-to-dirThis will prompt you for configuration options and generate a new project in the specified directory.
Below are the configurable questions exposed by baker.yaml:
project_name: Name of your application (used for crate name and other identifiers)project_author: Author metadataproject_version: Initial versionauthentication: Select auth mechanism (oidcornone)database: Currently onlypostgresdb_schema: PostgreSQL schema where all generated tables will be created. Defaults topublic.id_type: Type used for primary keys & foreign keys. Choices:integer(default):SERIALPK in SQL, Rust typei32big_integer:BIGSERIALPK, Rust typei64uuid:UUID DEFAULT gen_random_uuid()PK, Rust typeUuid
features: Optional extra features (e.g.open-telemetry)protocol:restorgrpccrudcrate: Whether to use crudcrate-generated controllers (only asked when protocol isrest)entities: JSON schema describing your domain entities
The entities configuration accepts a JSON object defining your domain model. For each entity, Baker will generate:
- Database models (using sea-orm)
- SQL migrations
- CRUD controllers (if
protocol = restandcrudcrate = false)
Each entity is defined using JSON Schema format with the following structure:
{
"EntityName": {
"type": "object",
"title": "EntityName",
"properties": {
"field_name": {
"type": "string|integer|number|boolean",
"format": "optional format specifier",
"description": "Field description for documentation"
}
},
"required": ["field_name"]
}
}The template supports the following property types and formats:
- Basic string:
{"type": "string"} - UUID:
{"type": "string", "format": "uuid"}→ RustUuid, SQLUUID - DateTime:
{"type": "string", "format": "date-time"}→ RustDateTime, SQLTIMESTAMPTZ - Date:
{"type": "string", "format": "date"}→ RustNaiveDate, SQLDATE - Time:
{"type": "string", "format": "time"}→ RustTimeTime, SQLTIME - Email:
{"type": "string", "format": "email"}→ RustString(validated) - URL:
{"type": "string", "format": "url"}→ RustString(validated)
- Integer:
{"type": "integer"}→ Maps toi8,i16,i32,i64based on min/max constraints- With
minimumandmaximumconstraints for appropriate sizing - Example:
{"type": "integer", "minimum": 0, "maximum": 255}→i8
- With
- Number:
{"type": "number"}→ Maps tof32orf64based on constraints- Example:
{"type": "number"}→f64
- Example:
- Boolean:
{"type": "boolean"}→ Rustbool, SQLBOOLEAN
Define an enum by providing choices:
{
"status": {
"enum": ["active", "inactive", "pending"],
"description": "User status"
}
}Entities can define relationships using the x-relationship extension and $ref to reference other entities:
{
"user_id": {
"x-relationship": "many-to-one",
"$ref": "User.json",
"description": "The user who created this"
}
}{
"posts": {
"x-relationship": "one-to-many",
"$ref": "Post.json",
"description": "All posts by this user"
}
}{
"tags": {
"type": "array",
"items": {
"x-relationship": "many-to-many",
"$ref": "Tag.json"
},
"description": "Tags associated with this post"
}
}Baker will automatically generate junction tables for many-to-many relationships.
Here's a complete example defining a blog system with Users, Posts, and Tags:
{
"User": {
"type": "object",
"title": "User",
"properties": {
"username": {
"type": "string",
"description": "Unique username"
},
"email": {
"type": "string",
"format": "email",
"description": "User email address"
},
"first_name": {
"type": "string"
},
"last_name": {
"type": "string"
},
"is_active": {
"type": "boolean",
"description": "Whether the user account is active"
},
"created_at": {
"type": "string",
"format": "date-time"
}
},
"required": ["username", "email"]
},
"Post": {
"type": "object",
"title": "Post",
"properties": {
"title": {
"type": "string",
"description": "Post title"
},
"content": {
"type": "string",
"description": "Post content"
},
"published": {
"type": "boolean",
"description": "Whether the post is published"
},
"author_id": {
"x-relationship": "many-to-one",
"$ref": "User.json",
"description": "The author of this post"
},
"tags": {
"type": "array",
"items": {
"x-relationship": "many-to-many",
"$ref": "Tag.json"
},
"description": "Tags for categorizing the post"
},
"created_at": {
"type": "string",
"format": "date-time"
}
},
"required": ["title", "content", "author_id"]
},
"Tag": {
"type": "object",
"title": "Tag",
"properties": {
"name": {
"type": "string",
"description": "Tag name"
},
"slug": {
"type": "string",
"description": "URL-friendly tag identifier"
}
},
"required": ["name", "slug"]
}
}This will generate:
- Database tables:
users,posts,tags, andpost_tag(junction table) - Rust models:
User,Post,Tagwith proper sea-orm annotations - CRUD endpoints (if REST):
POST /api/users,GET /api/users,GET /api/users/{id},PUT /api/users/{id},DELETE /api/users/{id}POST /api/posts,GET /api/posts,GET /api/posts/{id},PUT /api/posts/{id},DELETE /api/posts/{id}POST /api/tags,GET /api/tags,GET /api/tags/{id},PUT /api/tags/{id},DELETE /api/tags/{id}
- Migrations: SQL scripts with proper foreign key constraints
The migration template (migrations/00001_init_db.sql.baker.j2) uses db_schema and id_type:
- Creates schema only if not
public. - Chooses PK column form based on
id_type. - Foreign keys inherit the chosen PK base type.
Example with db_schema = app and id_type = uuid:
CREATE EXTENSION IF NOT EXISTS pgcrypto;
CREATE SCHEMA IF NOT EXISTS app;
CREATE TABLE IF NOT EXISTS app.users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
created_at TIMESTAMPTZ DEFAULT NOW(),
last_updated TIMESTAMPTZ DEFAULT NOW()
-- other columns
);Example with id_type = big_integer:
id BIGSERIAL PRIMARY KEYExample with id_type = integer:
id SERIAL PRIMARY KEYMany‑to‑many join tables and many‑to‑one foreign keys adopt the same underlying type (UUID, BIGINT, or INTEGER).
Macros added in macros.jinja:
rust_id_type(id_type)→Uuid | i64 | i32pk_sql_type(id_type)→UUID | BIGINT | INTEGERpk_column_definition(id_type)→ Full SQL PK declaration.
(If you extend model/controller templates, reuse these macros for consistency.)
The template includes a Dockerfile optimized for multi-architecture builds using cargo-chef:
This template uses hardened base images to ensure maximum security in production environments:
| Aspect | Traditional Images | Docker Hardened Images |
|---|---|---|
| CVEs | Often 100+ vulnerabilities | Zero known CVEs |
| Attack Surface | Shell, package managers, utilities | No shell, minimal binaries |
| Image Size | 50-500+ MB | < 5 MB |
| Compliance | Manual hardening required | SLSA Level 2, SBOM included |
⚠️ Security Best Practice: Always use hardened, distroless images in production. The docker hardened images static image contains only the absolute minimum required to run a statically-linked binary—no shell, no package manager, no attack surface.
The final production image is less than 5 MB thanks to:
- Static linking with musl: No runtime dependencies required
- Distroless base image: Only contains the binary, CA certificates, and timezone data
- LTO (Link-Time Optimization): Maximum binary size reduction
- Binary stripping: Debug symbols removed
- UPX compression (optional): Can further reduce to ~2 MB
- 🔒 Hardened security: Zero CVE base images with minimal attack surface
- 📦 Tiny footprint: Final image under 5 MB—fast pulls, low storage costs
- 🌍 Multi-architecture support: Build for
linux/amd64andlinux/arm64 - ⚡ Fast builds:
cargo-chefcaches dependencies separately from source code - 🔄 Reproducible builds:
--lockedflag ensures consistent dependency versions - 🗜️ Maximum compression: LTO + stripping for the smallest possible binary
# Build for your current platform
docker build -t my-app:latest .
# Build for multiple platforms using buildx
docker buildx build --platform linux/amd64,linux/arm64 -t my-app:latest --push .- Chef stage: Installs
cargo-cheffor dependency caching - Planner stage: Analyzes dependencies to create a recipe
- Builder stage: Builds dependencies (cached), then builds your application
- Runtime stage: Docker hardened images with just the binary
- Rust backend powered by axum
- Database support for postgres via sea-orm
- 🔒 Hardened Docker images with zero CVEs
- 📦 Ultra-small images (< 5 MB) using distroless runtime
- Docker multi-architecture support (amd64, arm64) with cargo-chef
- Compressed, optimized binaries using LTO and stripping
- OpenAPI documentation with interactive UI (utoipa + Scalar)
- Flexible code generation via baker
- Generate either a minimal project or a full CRUD app based on your entity definitions
When returning paginated resources, include the Content-Range header to indicate the range of items in the response and the total count.
| Header | Format | Description |
|---|---|---|
Content-Range |
{resource} {start}-{end}/{total} |
Current range and total count |
Examples:
Content-Range: items 0-24/100for the first 25 items out of 100 total.Content-Range: users 50-99/250for items 51–100 out of 250 total.
MIT