Server Architecture
The design and philosophy behind the Waylog Fastify API Server.
The Waylog API is powered by Fastify, chosen for its extreme performance, low overhead, and robust plugin ecosystem. Our architecture strictly separates concerns, ensuring that routing, authentication, and business logic remain isolated and testable.
Plugin-Driven Design
We eschew monolithic router files in favor of a file-system-based approach utilizing @fastify/autoload.
Directory Structure
apps/server/src/
├── plugins/ # Global plugins (CORS, Helmet, Rate Limiting, Better Auth)
├── hooks/ # Reusable Fastify lifecycle hooks (e.g., authenticate)
├── lib/ # Pure utilities (Response envelopes, Zod schemas)
├── routes/ # File-based routing definitions
│ ├── v1/
│ │ ├── public/ # No authentication required
│ │ └── private/ # Guarded by session authentication
│ └── webhooks/ # Guarded by HMAC signatures (e.g., TruckersMP)
└── index.ts # Entry point for plugin registrationSecurity & Access Control
Scope-Level Authentication
Rather than manually protecting individual routes, we apply an onRequest hook (hooks/authenticate.ts) at the directory scope level. Any route placed within routes/v1/private/ is automatically shielded, rejecting unauthenticated requests before they reach the handler.
Tiered Rate Limiting
To protect the system from abuse and handle the high throughput of live telemetry, we implement scoped rate limiting:
- Public Routes: 60 requests per minute per IP.
- Private Routes: 120 requests per minute per User.
- Telemetry Ingestion: 300 requests per minute per User (to accommodate frequent Tauri client polling).
- Webhooks: 500 requests per minute per IP.
Validation & Consistency
All HTTP routes utilize fastify-type-provider-zod. Request parameters, query strings, and bodies are validated against Zod schemas before the route handler is invoked, guaranteeing type safety and eliminating malformed data.
Furthermore, every response adheres to a strict envelope:
// Success
{ "ok": true, "data": { ... } }
// Error
{ "ok": false, "error": { "code": "ERR_CODE", "message": "Description" } }