A lightweight REST API for managing jokes, built with Rust and the Axum web framework.
- Full CRUD operations (Create, Read, Update, Delete)
- Pagination support for listing jokes
- Random joke endpoint
- Health check endpoint
- SQLite database with automatic migrations
- Input validation
- CORS support
- Graceful shutdown handling
- Comprehensive test coverage
- Runtime: Tokio - Async runtime
- Web Framework: Axum v0.8
- Database: SQLite via SQLx (compile-time checked queries)
- Serialization: Serde
- Validation: validator
- CLI Parsing: Clap
- Logging: tracing + tower-http
- Rust (edition 2024, i.e., Rust 1.85+)
- Cargo
-
Clone the repository:
git clone <repository-url> cd axum_everyone
-
Configure environment variables:
cp .env.example .env
The default
.envfile contains:DATABASE_URL=sqlite:data.db
# Build the project
cargo build
# Run the server (starts on http://localhost:3000)
cargo runcargo run -- --helpAvailable options:
--host- Bind to all interfaces (default: localhost only)--port <PORT>- Port to listen on (default: 3000)
Examples:
# Run on default (localhost:3000)
cargo run
# Run on all interfaces, port 8080
cargo run -- --host --port 8080| Method | Path | Description | Status Codes |
|---|---|---|---|
| GET | / |
Hello World | 200 |
| GET | /health |
Health check | 200 |
| GET | /jokes |
List jokes (paginated) | 200 |
| POST | /jokes |
Create a new joke | 201, 422 |
| DELETE | /jokes |
Delete all jokes | 200, 500 |
| GET | /joke/{id} |
Get joke by ID | 200, 404 |
| PUT | /joke/{id} |
Update joke by ID | 200, 404, 422 |
| DELETE | /joke/{id} |
Delete joke by ID | 200, 404 |
| GET | /joke/random |
Get a random joke | 200, 404 |
Request:
{
"content": "Why did the programmer quit his job? Because he didn't get arrays."
}Response (201 Created):
{
"id": 1,
"content": "Why did the programmer quit his job? Because he didn't get arrays.",
"created_at": "2026-04-10 22:27:14",
"updated_at": "2026-04-10 22:27:14"
}Response:
{
"jokes": [...],
"total": 42,
"page": 1,
"per_page": 20,
"total_pages": 3
}The jokes list endpoint supports query parameters:
page- Page number (default: 1)per_page- Items per page (default: 20, max: 100)
Example: GET /jokes?page=2&per_page=10
{
"error": "Not found"
}{
"error": "Joke content must be between 1 and 1000 characters"
}CREATE TABLE IF NOT EXISTS jokes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
content TEXT NOT NULL CHECK(length(content) > 0 AND length(content) <= 1000),
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);Database migrations are automatically applied on application startup.
PowerShell (Windows):
.\test_api.ps1Bash (Linux/macOS/WSL):
./test_api.shcargo test8 integration tests cover all CRUD operations, validation, pagination, and error cases using isolated in-memory SQLite databases.
cargo clippy -- -D warningssrc/
├── lib.rs # Library crate: app builder, re-exports (testable)
├── main.rs # Binary crate: CLI, server startup, migrations
├── models.rs # Data models and shared types
└── handler/
├── mod.rs # Handler module root, error types, route handlers
├── create.rs # Database insert operations
├── read.rs # Database query operations (with pagination)
├── update.rs # Database update operations
└── delete.rs # Database delete operations
migrations/
└── 001_create_jokes_table.sql # SQLx migration
The project includes a GitHub Actions workflow (.github/workflows/build.yml) that runs on every push and pull request:
- Build - Compiles the project
- Clippy - Runs linting (treats warnings as errors)
- Test - Runs the test suite
- Library + Binary pattern:
lib.rsexportscreate_app()for testable router construction;main.rshandles CLI and server lifecycle - Typed AppState: Uses
AppState { db: pool }for extensibility - Error Handling: Custom
AppErrorenum with HTTP status mapping (404, 422, 500) and structured JSON error responses - Validation: Uses
validatorcrate for declarative field validation - Database Queries: Static SQL strings with SQLx compile-time query checking
- Code Style: Strict Clippy lints enabled (
all,pedantic,nursery) - Documentation: All public
Result-returning functions have# Errorsdoc sections