You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2.5 KiB

quotesdb API — Architecture

Overview

Single Axum router binary (src/bin/api/main.rs) that serves a quotes REST API. Targets both:

  • Native (host): Axum + Tokio + rusqlite — for local dev and integration tests
  • wasm32-unknown-unknown: workers-rs + Cloudflare D1 — for production Cloudflare Workers

Component structure

src/bin/api/
├── main.rs              # Entry point — opens DB, wires router, starts server (native only)
├── db/
│   ├── mod.rs           # QuoteRepository trait + ListResult/DeleteResult/DbError types
│   ├── migrations.rs    # SQL DDL strings (CREATE TABLE IF NOT EXISTS)
│   ├── native.rs        # NativeRepository: tokio-rusqlite implementation
│   ├── connection.rs    # open() helper — opens SQLite with WAL and FK pragma
│   └── d1.rs            # D1Repository: workers-rs stub (wasm32 only)
└── handlers/
    └── mod.rs           # All 7 route handlers + router() factory function

DB layer

The QuoteRepository trait abstracts over two backends:

Target Implementation Storage
Native (not(wasm32)) NativeRepository Local SQLite via tokio-rusqlite
wasm32 D1Repository Cloudflare D1 via workers-rs

On native targets, the trait uses async_trait (Send-capable futures), allowing Arc<dyn QuoteRepository> to be used as Axum state.

On wasm32, the trait uses async_trait(?Send) because D1Database wraps JS values that aren't Send.

Request flow (native)

TCP :3000 → Axum router → handler fn
                              ↓
                    State<Arc<dyn QuoteRepository>>
                              ↓
                    NativeRepository.method()
                              ↓
                    tokio-rusqlite → SQLite file

Auth model

No user accounts. Each quote has an auth_code (4-word passphrase from EFF word list), generated at creation time and returned once in the creation response. Stored plaintext in quotes.auth_code.

For update and delete, the caller supplies the auth code in the X-Auth-Code request header. A mismatch returns 403 Forbidden.

OpenAPI spec

api/openapi.yaml is the source of truth. build.rs converts it to JSON at compile time, writing to $OUT_DIR/openapi.json. The GET /api/ handler serves it via include_str!.

Router ordering

GET /api/quotes/random is registered before GET /api/quotes/:id to prevent "random" being matched as an ID.