Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,12 @@ logs
*.log

# Generated binaries
main
main

# Configuration files (exclude sensitive configs, include examples)
config.yaml
config.yml
configs/config.yaml
configs/config.yml
!config.example.yaml
!config.example.yml
39 changes: 39 additions & 0 deletions config.example.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Configuration file for go-api
# This file follows a strict schema with validation
# Environment variables will override any values set here

server:
host: localhost
port: "8000"

database:
url: postgres://user:password@localhost/dbname?sslmode=disable

rate_limiter:
requests_per_time_frame: 100
time_frame: 60s
enabled: true

push_notification:
vapid_public_key: "your-vapid-public-key"
vapid_private_key: "your-vapid-private-key"

auth:
# api_key: "your-api-key" # Optional
jwt_secret: "your-secret-key"
jwt_issuer: "your-app"
jwt_audience: "your-app-users"
token_expiration: 15m
refresh_expiration: 168h # 7 days

storage:
enabled: false
bucket_name: "my-bucket"
account_id: "account-id"
access_key_id: "access-key"
secret_access_key: "secret-key"
# public_domain: "https://cdn.example.com" # Optional
use_public_url: true

redis:
url: "redis://localhost:6379"
176 changes: 176 additions & 0 deletions docs/CONFIGURATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
# Configuration Management

This document describes the configuration management system for the go-api project, which supports both YAML files and environment variables with strict validation.

## Overview

The configuration system follows best practices by:
- Supporting structured YAML configuration files with strict schema validation
- Maintaining backward compatibility with environment variables
- Providing environment variable override capability
- Implementing comprehensive validation using Go's validator package
- Offering flexible configuration file locations

## Configuration Sources Priority

1. **Environment Variables** (highest priority) - override any YAML values
2. **YAML Configuration File** - structured configuration with validation
3. **Default Values** (lowest priority) - fallback values defined in code

## YAML Configuration

### Configuration File Locations

The system automatically searches for configuration files in the following order:

1. `./config.yaml`
2. `./config.yml`
3. `./configs/config.yaml`
4. `./configs/config.yml`
5. `$HOME/.config/go-api/config.yaml`
6. `/etc/go-api/config.yaml`

### YAML Schema

```yaml
server:
host: localhost # required: Server hostname
port: "8000" # required: Server port (string)

database:
url: postgres://user:password@localhost/dbname?sslmode=disable # required: Database connection URL

rate_limiter:
requests_per_time_frame: 100 # min=0: Maximum requests per time frame
time_frame: 60s # min=0: Time frame duration (e.g., 30s, 5m, 1h)
enabled: true # Enable/disable rate limiting

push_notification:
vapid_public_key: "your-vapid-public-key" # required: VAPID public key
vapid_private_key: "your-vapid-private-key" # required: VAPID private key

auth:
api_key: "your-api-key" # optional: API key
jwt_secret: "your-secret-key" # required: JWT signing secret
jwt_issuer: "your-app" # required: JWT issuer
jwt_audience: "your-app-users" # required: JWT audience
token_expiration: 15m # min=1m: Token expiration (e.g., 15m, 1h)
refresh_expiration: 168h # min=1h: Refresh token expiration (e.g., 24h, 168h)

storage:
enabled: false # Enable/disable storage features
bucket_name: "my-bucket" # required_if enabled: S3-compatible bucket name
account_id: "account-id" # required_if enabled: Storage account ID
access_key_id: "access-key" # required_if enabled: Storage access key
secret_access_key: "secret-key" # required_if enabled: Storage secret key
public_domain: "https://cdn.example.com" # optional: Public domain for file URLs
use_public_url: true # Use public URLs for file access

redis:
url: "redis://localhost:6379" # required: Redis connection URL
```

### Time Duration Format

Time durations can be specified using Go's standard duration format:
- `30s` - 30 seconds
- `5m` - 5 minutes
- `1h` - 1 hour
- `24h` - 24 hours
- `168h` - 168 hours (7 days)

## Environment Variables

All YAML configuration values can be overridden using environment variables:

```bash
# Server configuration
API_URL=localhost
PORT=8000

# Database configuration
DATABASE_URL=postgres://user:password@localhost/dbname?sslmode=disable

# JWT configuration
JWT_SECRET=your-secret-key
JWT_ISSUER=your-app
JWT_AUDIENCE=your-app-users
JWT_TOKEN_EXPIRATION=15 # in minutes
JWT_REFRESH_EXPIRATION=10080 # in minutes (7 days)

# Rate limiting
RATE_LIMIT_MAX_REQUESTS=100
RATE_LIMIT_TIMEFRAME=60 # in seconds

# Push notifications
VAPID_PUBLIC_KEY=your-vapid-public-key
VAPID_PRIVATE_KEY=your-vapid-private-key

# Storage configuration
STORAGE_ENABLED=false
STORAGE_BUCKET_NAME=my-bucket
STORAGE_ACCOUNT_ID=account-id
STORAGE_ACCESS_KEY_ID=access-key
STORAGE_SECRET_ACCESS_KEY=secret-key
STORAGE_PUBLIC_DOMAIN=https://cdn.example.com
STORAGE_USE_PUBLIC_URL=true

# Redis configuration
REDIS_URL=redis://localhost:6379
```

## Validation

The configuration system includes comprehensive validation:

### Required Fields
- All `required` fields must have non-empty values
- `required_if` fields are required when the condition is met (e.g., storage fields when storage is enabled)

### Value Constraints
- **Time Durations**: Minimum values enforced (e.g., token expiration ≥ 1 minute)
- **Numeric Values**: Minimum values enforced (e.g., rate limiting ≥ 0)
- **URLs**: Database and Redis URLs must be valid connection strings

### Error Handling
- Validation errors are reported with detailed field information
- Missing required configuration stops application startup
- Invalid YAML syntax is reported with line numbers

## Migration from Environment Variables Only

If you're currently using only environment variables, no changes are required. The system maintains full backward compatibility:

1. **Existing setup**: Continue using environment variables as before
2. **Gradual migration**: Create a YAML file with some values, keep others as environment variables
3. **Full migration**: Move all configuration to YAML, use environment variables only for sensitive values or deployment-specific overrides

## Best Practices

1. **Use YAML for base configuration**: Define your default configuration in YAML files
2. **Use environment variables for secrets**: Override sensitive values like passwords and keys
3. **Use environment variables for deployment differences**: Override values that differ between environments (dev, staging, production)
4. **Version control**: Include `config.example.yaml` in version control, but exclude actual configuration files with secrets
5. **Validation**: Always test your configuration changes to catch validation errors early

## Example Usage

```go
// Load configuration (automatic discovery)
config := config.LoadConfig()

// Load from specific file
config, err := config.LoadConfigFromYAML("./my-config.yaml")
if err != nil {
log.Fatalf("Failed to load config: %v", err)
}
```

## Troubleshooting

### Common Issues

1. **Validation errors**: Check that all required fields are provided and meet constraints
2. **YAML syntax errors**: Validate your YAML file syntax using online tools or `yamllint`
3. **File not found**: Ensure your configuration file is in one of the searched locations
4. **Environment variable types**: Remember that environment variables for durations use different units (minutes/seconds) compared to YAML format
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ require (
github.com/go-openapi/swag v0.19.15 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.26.0 // indirect
github.com/go-playground/validator/v10 v10.27.0 // indirect
github.com/goccy/go-json v0.10.5 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k=
github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
github.com/go-playground/validator/v10 v10.27.0 h1:w8+XrWVMhGkxOaaowyKH35gFydVHOvC0/uWoy2Fzwn4=
github.com/go-playground/validator/v10 v10.27.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
Expand Down
Loading