# 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:

```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-<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.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
- [ ] `<tool>-config.template.toml` with comments
- [ ] Examples for dev/prod configurations
- [ ] Troubleshooting section

### Scripts
- [ ] `scripts/install-<tool>-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
# <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 `--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<T>
- [ ] 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
