Merge _configs/ into config/ for single configuration directory. Update all path references. Changes: - Move _configs/* to config/ - Update .gitignore for new patterns - No code references to _configs/ found Impact: -1 root directory (layout_conventions.md compliance)
14 KiB
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 <PATH>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:
<tool>-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:
[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_levelfrom 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_enabledboolean in config - MUST: Conditionally apply
CorsLayerbased on config - MUST: Use
CorsLayer::permissive()when enabled - MUST: Log CORS status during startup
Code Pattern:
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
/publicand/docsfrom same path - MUST NOT: Hardcode
"public"or"docs"directory names
Code Pattern:
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_pathfields - SHOULD: Implement TLS configuration validation
- MUST: Log TLS status during startup
- SHOULD: Return clear error if TLS enabled but paths missing
Validation Pattern:
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 returnPathBuf
Code Pattern:
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-<tool>-api.sh - MUST: Ask user for config directory (default:
~/.config/<tool>-api) - MUST: Copy template config to user location
- MUST: Generate
run-<tool>-server.shwrapper script - MUST: Provide clear installation instructions
- MUST: Check that binary exists before proceeding
Generated Wrapper Script:
#!/bin/bash
exec /path/to/binary --config /path/to/config.toml "$@"
Implementation Checklist
Configuration Module (config.rs)
ServerConfigstruct with all required fieldsTlsConfigstruct (optional fields)ConfigErrorenum with descriptive messagesload()method that reads TOML file- Explicit
[server]table extraction socket_addr()method*_path_buf()methods for path resolutionvalidate_tls()methodlog_config()method showing all settings- Unit tests for configuration loading
Main Server (main.rs)
- CLI argument parsing with clap (
--configflag) - 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.mdwith configuration guide<tool>-config.template.tomlwith comments- Examples for dev/prod configurations
- Troubleshooting section
Scripts
scripts/install-<tool>-api.shinstallation script- Script handles
--config-dirand--binaryarguments - 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}.tomlrelative 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:
[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
FeaturesConfigstruct 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
# <Tool> API Server Configuration Template
#
# This is a template configuration file for the <tool>-server binary.
# Copy this file to your desired location and update the values below.
#
# Usage:
# <tool>-server --config /path/to/<tool>-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
--configCLI 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 addressport- Integer for port numberdatabase_path- String for database filepublic_files_path- String for static files (NEW!)cors_enabled- Boolean for CORSlog_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:
<tool>-api-config.template.toml - Installation script:
scripts/install-<tool>-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