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.
62 lines
2.5 KiB
Markdown
62 lines
2.5 KiB
Markdown
# 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.
|