# API Server Development Guideline **Status**: Production Ready **Version**: 1.0 **Last Updated**: 2025-11-13 **Applies To**: All REST API servers in the syntaxis ecosystem ## Overview This guideline defines the standard pattern for creating and reviewing REST API servers that follow **Project Agnostic Practice (PAP)** principles. All API servers MUST adhere to these rules to ensure consistency, portability, and production-readiness. ## Core Principles ### 1. Configuration-Driven (No Hardcoded Paths) - **MUST**: Use explicit `--config ` CLI argument - **MUST**: Load configuration from TOML file - **MUST NOT**: Use hardcoded paths or project-relative paths - **MUST NOT**: Use environment variable overrides - **MUST NOT**: Provide default values - all settings must be explicit **Why**: Ensures server can run anywhere without modification, follows PAP. ### 2. Strict Configuration Enforcement - **MUST**: Require all configuration fields in TOML - **MUST**: Fail with clear error message if config file missing - **MUST**: Fail with clear error message if required fields missing - **MUST**: Show file path in error messages **Why**: Forces users to understand configuration, prevents silent failures. ### 3. Configuration File Structure - **MUST**: Use `[server]` table in TOML - **MUST**: Extract `[server]` section explicitly for parsing - **MUST**: Provide template file in project root: `-config.template.toml` - **MUST**: Include comments explaining each field **Why**: Clear, discoverable configuration with examples for users. ### 4. Required Configuration Fields Every API server MUST support these core settings: ```toml [server] host = "127.0.0.1" # Bind address (string) port = 3000 # Port number (integer) database_path = "./db.db" # Database file path (string, absolute or relative) public_files_path = "./public" # Static files directory (string, NEW!) cors_enabled = true # CORS support (boolean) log_level = "info" # Logging level (string: trace/debug/info/warn/error) [server.tls] # Optional TLS/HTTPS enabled = false cert_path = "/path/to/cert.pem" # Required if enabled key_path = "/path/to/key.pem" # Required if enabled ``` ### 5. Logging Configuration - **MUST**: Apply `log_level` from config to tracing_subscriber - **MUST**: Support levels: trace, debug, info, warn, error - **MUST**: Log all configuration values during startup (except secrets) - **MUST**: Log TLS status (enabled/disabled/configured) - **MUST**: Log static files directory status **Example Output**: ``` INFO Server configuration: 127.0.0.1:3000, database: /tmp/db.db, public_files: ./public, cors: true, log_level: info, tls: disabled ``` ### 6. CORS Configuration - **MUST**: Include `cors_enabled` boolean in config - **MUST**: Conditionally apply `CorsLayer` based on config - **MUST**: Use `CorsLayer::permissive()` when enabled - **MUST**: Log CORS status during startup **Code Pattern**: ```rust if cfg.cors_enabled { router = router.layer(CorsLayer::permissive()); tracing::debug!("CORS enabled"); } else { tracing::debug!("CORS disabled"); } ``` ### 7. Static Files (Public & Documentation) - **MUST**: Make file path configurable via `public_files_path` - **MUST**: Support both absolute and relative paths - **MUST**: Check if path exists before serving - **MUST**: Log WARNING if path doesn't exist - **MUST**: Skip static file routes if path missing (graceful degradation) - **MUST**: Serve both `/public` and `/docs` from same path - **MUST NOT**: Hardcode `"public"` or `"docs"` directory names **Code Pattern**: ```rust let public_files_path = cfg.public_files_path_buf(); let public_files_exist = public_files_path.exists(); if public_files_exist { tracing::info!("Static files directory: {}", public_files_path.display()); } else { tracing::warn!( "Static files directory does not exist: {}. Skipping endpoints.", public_files_path.display() ); } if public_files_exist { router = router .nest_service("/public", ServeDir::new(public_files_path.clone())) .nest_service("/docs", ServeDir::new(public_files_path)); } ``` ### 8. TLS/HTTPS Support (Optional but Recommended) - **MUST**: Include optional `[server.tls]` section - **MUST**: Support `enabled`, `cert_path`, `key_path` fields - **SHOULD**: Implement TLS configuration validation - **MUST**: Log TLS status during startup - **SHOULD**: Return clear error if TLS enabled but paths missing **Validation Pattern**: ```rust pub fn validate_tls(&self) -> Result<(), ConfigError> { if let Some(tls) = &self.tls { if tls.enabled { if tls.cert_path.is_none() { return Err(ConfigError::Custom( "TLS enabled but cert_path not provided".to_string(), )); } if tls.key_path.is_none() { return Err(ConfigError::Custom( "TLS enabled but key_path not provided".to_string(), )); } } } Ok(()) } ``` ### 9. Path Resolution - **MUST**: Resolve relative paths from current working directory - **MUST**: Support absolute paths without modification - **MUST**: Handle missing parent directories (create if needed for database) - **MUST**: Provide `*_path_buf()` methods that return `PathBuf` **Code Pattern**: ```rust pub fn database_path_buf(&self) -> PathBuf { let path = PathBuf::from(&self.database_path); if path.is_absolute() { path } else { match std::env::current_dir() { Ok(cwd) => cwd.join(&path), Err(_) => path } } } ``` ### 10. Installation Script - **MUST**: Provide bash script: `scripts/install--api.sh` - **MUST**: Ask user for config directory (default: `~/.config/-api`) - **MUST**: Copy template config to user location - **MUST**: Generate `run--server.sh` wrapper script - **MUST**: Provide clear installation instructions - **MUST**: Check that binary exists before proceeding **Generated Wrapper Script**: ```bash #!/bin/bash exec /path/to/binary --config /path/to/config.toml "$@" ``` ## Implementation Checklist ### Configuration Module (`config.rs`) - [ ] `ServerConfig` struct with all required fields - [ ] `TlsConfig` struct (optional fields) - [ ] `ConfigError` enum with descriptive messages - [ ] `load()` method that reads TOML file - [ ] Explicit `[server]` table extraction - [ ] `socket_addr()` method - [ ] `*_path_buf()` methods for path resolution - [ ] `validate_tls()` method - [ ] `log_config()` method showing all settings - [ ] Unit tests for configuration loading ### Main Server (`main.rs`) - [ ] CLI argument parsing with clap (`--config` flag) - [ ] Configuration loading - [ ] Configuration validation - [ ] Tracing initialization from `log_level` - [ ] Database initialization - [ ] Router construction - [ ] Conditional CORS layer application - [ ] Conditional static file route registration - [ ] TLS path existence check - [ ] Server startup logging ### Documentation - [ ] `README.md` with configuration guide - [ ] `-config.template.toml` with comments - [ ] Examples for dev/prod configurations - [ ] Troubleshooting section ### Scripts - [ ] `scripts/install--api.sh` installation script - [ ] Script handles `--config-dir` and `--binary` arguments - [ ] Script creates wrapper script with correct paths - [ ] Clear success/error messages ## Modular Configuration Architecture (Recommended) For servers with multiple optional features, use a **two-tier configuration approach** to prevent config file bloat: ### Tier 1: Main Configuration - Core server settings only (host, port, database, CORS, logging, TLS) - Feature enablement flags (`[server.features]` section) - Minimal and focused ### Tier 2: Feature Configurations - Feature-specific settings in separate files - Located in `configs/features/{feature}.toml` relative to main config - Auto-loaded only if feature is enabled - Reduces main config complexity **Example Structure**: ``` project-root/ ├── server-api-config.toml # Main config with feature flags └── configs/ └── features/ ├── database.toml # Loaded if database feature enabled ├── health.toml ├── metrics.toml ├── rate_limit.toml ├── auth.toml ├── cache.toml └── multi_tenant.toml ``` **Main Config Example**: ```toml [server] host = "127.0.0.1" port = 3000 database_path = "./data/database.db" public_files_path = "./public" cors_enabled = true log_level = "info" [server.features] database.enabled = true # If true, loads configs/features/database.toml health.enabled = true metrics.enabled = false rate_limit.enabled = false auth.enabled = false cache.enabled = false multi_tenant.enabled = false [server.tls] enabled = false # cert_path = "/path/to/cert.pem" # key_path = "/path/to/key.pem" ``` **Implementation Requirements**: - [ ] Add `FeaturesConfig` struct to hold feature flags - [ ] Add `FeatureFlag { enabled: bool }` for each feature - [ ] Implement `validate_feature_configs()` to check enabled features have config files - [ ] Document feature-specific config file format in template files - [ ] Log which features are enabled at startup ## Configuration Template Structure ```toml # API Server Configuration Template # # This is a template configuration file for the -server binary. # Copy this file to your desired location and update the values below. # # Usage: # -server --config /path/to/-api-config.toml [server] # Server host to bind to # Examples: 127.0.0.1 (localhost only), 0.0.0.0 (all interfaces) host = "127.0.0.1" # Server port number port = 3000 # Database file path # Can be absolute (/var/lib/data.db) or relative (./data/data.db) # Parent directory will be created automatically if needed database_path = "./data/database.db" # Path to directory containing static files (public folder and docs) # Can be absolute (/var/www/public) or relative (./public) public_files_path = "./public" # Enable Cross-Origin Resource Sharing (CORS) # Set to true to allow requests from different origins cors_enabled = true # Logging level: trace, debug, info, warn, error log_level = "info" # TLS/HTTPS Configuration (optional) [server.tls] enabled = false # cert_path = "/path/to/cert.pem" # key_path = "/path/to/key.pem" ``` ## Error Handling Standards ### Configuration Loading Errors ``` ❌ Missing config file: Error: Custom("Failed to read config file \"/path/file.toml\": No such file or directory") ❌ Invalid TOML: Error: Custom("Failed to parse TOML config from \"/path/file.toml\": missing field `host`") ❌ Missing [server] section: Error: Custom("Missing [server] section in config file") ❌ Invalid TLS config: Error: Custom("TLS enabled but cert_path is not provided") ``` ### Runtime Warnings ``` ⚠️ Static files directory missing: WARN Static files directory does not exist: /path/to/public. Skipping /public and /docs endpoints. ✓ Successful startup: INFO Server configuration: 127.0.0.1:3000, database: ./db.db, public_files: ./public, cors: true, log_level: info, tls: disabled INFO Server listening on 127.0.0.1:3000 ``` ## Review Checklist for Existing API Servers Use this checklist when reviewing existing API servers for compliance: ### Configuration System - [ ] Uses `--config` CLI argument (clap) - [ ] Loads TOML from file path - [ ] Extracts `[server]` table explicitly - [ ] Has no hardcoded paths - [ ] Has no environment variable overrides - [ ] Has no default values (all fields required) - [ ] Clear error messages with file paths ### Configuration Fields - [ ] `host` - String for bind address - [ ] `port` - Integer for port number - [ ] `database_path` - String for database file - [ ] `public_files_path` - String for static files (NEW!) - [ ] `cors_enabled` - Boolean for CORS - [ ] `log_level` - String for logging level - [ ] `[server.tls]` - Optional TLS section ### Functionality - [ ] Log level applied to tracing_subscriber - [ ] CORS conditionally applied based on config - [ ] Static files conditionally served (with existence check) - [ ] TLS validation if enabled - [ ] Configuration logged during startup ### Documentation - [ ] Template config file in project root - [ ] Installation script provided - [ ] README explains configuration - [ ] Examples for different environments ### Code Quality - [ ] No unsafe code (`#![forbid(unsafe_code)]`) - [ ] No unwrap() - uses Result - [ ] Comprehensive tests - [ ] Proper error handling - [ ] Clear logging ## Template Locations - **Config struct**: `src/config.rs` - **Main server**: `src/main.rs` - **Template file**: `-api-config.template.toml` - **Installation script**: `scripts/install--api.sh` - **Documentation**: `README.md`, `API_SERVER_GUIDELINE.md` ## Working Example **Reference Implementation**: `crates/syntaxis-api/` in syntaxis This crate demonstrates all guidelines: - ✅ Strict configuration in `src/config.rs` - ✅ CLI argument parsing in `src/main.rs` - ✅ CORS conditional application - ✅ Static files with graceful degradation - ✅ TLS support with validation - ✅ Template and installation script - ✅ Comprehensive logging ## Version History ### v1.0 (2025-11-13) - Initial comprehensive guideline - Based on syntaxis-api implementation - Includes all core principles and checklist - Production-ready pattern established ## Future Enhancements Potential improvements for future versions: - [ ] Database migration configuration - [ ] Health check endpoint standardization - [ ] Metrics/monitoring endpoints - [ ] Rate limiting configuration - [ ] Authentication/authorization settings - [ ] Cache configuration options - [ ] Multi-tenant configuration