# API Server Development Skill

**Purpose**: Provide guidance for creating and reviewing production-grade REST API servers that follow Project Agnostic Practice (PAP) principles.

**When to Use**:
- Creating a new REST API server
- Reviewing existing API servers for compliance
- Implementing configuration systems
- Adding CORS/TLS/static files support

## The Config-Driven API Server Pattern

This skill is based on the **syntaxis-api** implementation, which establishes the standard pattern for all API servers in this project.

### What Makes This Pattern Special

✅ **Portable**: No hardcoded paths - works anywhere
✅ **Explicit**: All configuration required, no defaults
✅ **Safe**: Clear error messages, validation
✅ **Flexible**: Config-driven CORS, logging, static files, TLS
✅ **Graceful**: Missing optional features don't break server

## Quick Start for New API Servers

### 1. Configuration Module

Create `src/config.rs`:

```rust
use serde::{Deserialize, Serialize};
use thiserror::Error;

#[derive(Error, Debug)]
pub enum ConfigError {
    #[error("IO error: {0}")]
    Io(#[from] std::io::Error),
    #[error("TOML parsing error: {0}")]
    TomlError(#[from] toml::de::Error),
    #[error("Configuration error: {0}")]
    Custom(String),
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ServerConfig {
    pub host: String,
    pub port: u16,
    pub database_path: String,
    pub public_files_path: String,
    pub cors_enabled: bool,
    pub log_level: String,
    #[serde(default)]
    pub tls: Option<TlsConfig>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TlsConfig {
    pub enabled: bool,
    pub cert_path: Option<String>,
    pub key_path: Option<String>,
}

impl ServerConfig {
    pub fn load<P: AsRef<std::path::Path>>(config_path: P) -> Result<Self, ConfigError> {
        // Extract [server] section explicitly
        // Fail if any required field missing
        // Return clear error messages
    }

    pub fn socket_addr(&self) -> Result<SocketAddr, ConfigError> { ... }
    pub fn database_path_buf(&self) -> PathBuf { ... }
    pub fn public_files_path_buf(&self) -> PathBuf { ... }
    pub fn validate_tls(&self) -> Result<(), ConfigError> { ... }
    pub fn log_config(&self) { ... }
}
```

### 2. CLI Argument Parsing

Add to `Cargo.toml`:
```toml
clap = { version = "4.4", features = ["derive"] }
```

In `src/main.rs`:
```rust
use clap::Parser;

#[derive(Parser)]
#[command(name = "tool-server")]
struct Args {
    /// Path to configuration file (TOML format)
    #[arg(short, long)]
    config: PathBuf,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let args = Args::parse();
    let cfg = ServerConfig::load(&args.config)?;
    cfg.validate_tls()?;
    // ... rest of initialization
}
```

### 3. Tracing from Config

```rust
// Initialize tracing with configured log level
let env_filter = tracing_subscriber::EnvFilter::try_from_default_env()
    .or_else(|_| tracing_subscriber::EnvFilter::try_new(&cfg.log_level))?;

tracing_subscriber::fmt()
    .with_env_filter(env_filter)
    .init();

cfg.log_config();
```

### 4. Conditional CORS

```rust
if cfg.cors_enabled {
    router = router.layer(CorsLayer::permissive());
    tracing::debug!("CORS enabled");
}
```

### 5. Config-Driven Static Files

```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 /public and /docs 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));
}
```

### 6. Template Configuration File

Create `<tool>-api-config.template.toml`:

```toml
# <Tool> API Server Configuration Template
#
# Usage: <tool>-server --config /path/to/config.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.tls]
enabled = false
# cert_path = "/path/to/cert.pem"
# key_path = "/path/to/key.pem"
```

### 7. Installation Script

Create `scripts/install-<tool>-api.sh`:

```bash
#!/bin/bash
set -e

CONFIG_DIR="$HOME/.config/<tool>-api"
BINARY="${PROJECT_ROOT}/target/release/<tool>-server"

mkdir -p "$CONFIG_DIR"
cp <tool>-api-config.template.toml "$CONFIG_DIR/<tool>-api-config.toml"

cat > "$CONFIG_DIR/run-<tool>-server.sh" << 'EOF'
#!/bin/bash
exec BINARY_PATH --config CONFIG_FILE_PATH "$@"
EOF

sed -i '' "s|BINARY_PATH|$BINARY|g" "$CONFIG_DIR/run-<tool>-server.sh"
sed -i '' "s|CONFIG_FILE_PATH|$CONFIG_DIR/<tool>-api-config.toml|g" "$CONFIG_DIR/run-<tool>-server.sh"
chmod +x "$CONFIG_DIR/run-<tool>-server.sh"

echo "✅ Installation complete!"
echo "Run: $CONFIG_DIR/run-<tool>-server.sh"
```

## Review Checklist

When reviewing an existing API server:

### Configuration System
- [ ] Has `--config` CLI argument (clap)
- [ ] Loads TOML from file
- [ ] Extracts `[server]` table explicitly
- [ ] No hardcoded paths
- [ ] No env var overrides
- [ ] All fields required (no defaults)
- [ ] Clear error messages

### Configuration Fields
- [ ] `host` (string)
- [ ] `port` (u16)
- [ ] `database_path` (string)
- [ ] `public_files_path` (string)
- [ ] `cors_enabled` (bool)
- [ ] `log_level` (string)
- [ ] `[server.tls]` (optional)

### Features
- [ ] Log level applied to tracing
- [ ] CORS conditional based on config
- [ ] Static files conditional (with existence check)
- [ ] TLS validation if enabled
- [ ] Configuration logged at startup

### Files
- [ ] `<tool>-api-config.template.toml` in project root
- [ ] `scripts/install-<tool>-api.sh`
- [ ] `README.md` with configuration examples
- [ ] Tests for config loading

## Reference Implementation

**Location**: `crates/syntaxis-api/` in syntaxis

Review this crate for a complete, production-ready example:
- Configuration: `src/config.rs`
- Server setup: `src/main.rs`
- Template: `syntaxis-api-config.template.toml`
- Installation: `scripts/install-syntaxis-api.sh`

## Full Documentation

See **API_SERVER_GUIDELINE.md** in project root for:
- Complete principles
- Detailed implementation checklist
- Error handling standards
- All required patterns

## Key Takeaways

1. **Configuration First**: `--config` is not optional
2. **Be Explicit**: No defaults, no magic
3. **Be Kind**: Clear error messages with file paths
4. **Be Flexible**: Conditional features (CORS, TLS, files)
5. **Be Portable**: No hardcoded paths anywhere
6. **Be Documented**: Template + comments + installation guide
