Skip to main content

Configuration Overview

Factus API uses pydantic-settings for type-safe configuration management. All settings are defined in a central configuration class and loaded from environment variables.
Configuration values are never hardcoded. All sensitive data and environment-specific settings are loaded from .env files or environment variables.

Settings Class

The configuration is managed through a Pydantic Settings class:
app/src/core/config.py
from pydantic_settings import BaseSettings
from pydantic_settings import SettingsConfigDict

class Settings(BaseSettings):
    model_config = SettingsConfigDict(env_file=".env")

    # Application Settings
    PROJECT_NAME: str = "Factus API"
    VERSION: str = "1.0.0"
    API_V1_STR: str = "/api/v1"
    
    # Factus API Configuration (Required)
    FACTUS_BASE_URL: str 
    FACTUS_CLIENT_ID: str 
    FACTUS_CLIENT_SECRET: str 
    
    # JWT Configuration
    SECRET_KEY: str
    ALGORITHM: str = "HS256"
    ACCESS_TOKEN_EXPIRE_MINUTES: int = 60

settings = Settings()
Type Safety: Pydantic validates all settings at startup. If required variables are missing or have invalid types, the application will fail to start with a clear error message.

Environment Variables

Required Variables

These variables must be set for the application to run:
FACTUS_BASE_URL
string
required
Base URL for the Factus API (e.g., https://api.factus.com.co)
FACTUS_CLIENT_ID
string
required
OAuth2 client ID provided by Factus
FACTUS_CLIENT_SECRET
string
required
OAuth2 client secret provided by Factus
SECRET_KEY
string
required
Secret key for signing JWT tokens. Generate with:
python -c "import secrets; print(secrets.token_urlsafe(32))"

Optional Variables

These variables have sensible defaults but can be overridden:
PROJECT_NAME
string
default:"Factus API"
Name displayed in OpenAPI documentation
VERSION
string
default:"1.0.0"
API version displayed in documentation
API_V1_STR
string
default:"/api/v1"
Prefix for all API v1 endpoints
ALGORITHM
string
default:"HS256"
JWT signing algorithm (HS256, HS384, HS512)
ACCESS_TOKEN_EXPIRE_MINUTES
integer
default:60
JWT token expiration time in minutes

Environment File (.env)

Create a .env file in the project root with your configuration:
.env
# Factus API Configuration
FACTUS_BASE_URL=https://api.factus.com.co
FACTUS_CLIENT_ID=your_client_id_here
FACTUS_CLIENT_SECRET=your_client_secret_here

# JWT Configuration
SECRET_KEY=your_secret_key_here_generate_with_python_secrets
ALGORITHM=HS256
ACCESS_TOKEN_EXPIRE_MINUTES=60

# Application Configuration (Optional)
PROJECT_NAME=Factus API
VERSION=1.0.0
API_V1_STR=/api/v1
Security: Never commit .env files to version control. Add .env to your .gitignore file.

Example .env.example

Provide a template for developers:
.env.example
# Factus API Configuration
FACTUS_BASE_URL=https://api.factus.com.co
FACTUS_CLIENT_ID=
FACTUS_CLIENT_SECRET=

# JWT Configuration
# Generate SECRET_KEY with: python -c "import secrets; print(secrets.token_urlsafe(32))"
SECRET_KEY=
ALGORITHM=HS256
ACCESS_TOKEN_EXPIRE_MINUTES=60

# Application Configuration
PROJECT_NAME=Factus API
VERSION=1.0.0
API_V1_STR=/api/v1

Using Settings in Code

Import the settings singleton throughout your application:
# app/src/infrastructure/gateways/factus_auth_gateway.py
from app.src.core.config import settings

class FactusAuthGateway(IAuthGateway):
    def __init__(self):
        self.base_url = settings.FACTUS_BASE_URL
        self.client_id = settings.FACTUS_CLIENT_ID
        self.client_secret = settings.FACTUS_CLIENT_SECRET

How pydantic-settings Works

Pydantic-settings loads configuration in the following order (later sources override earlier ones):
1

Default values

Values defined in the Settings class:
PROJECT_NAME: str = "Factus API"  # Default value
2

.env file

Values from the .env file in the project root:
PROJECT_NAME=My Custom API
3

Environment variables

System environment variables (highest priority):
export PROJECT_NAME="Production API"
Precedence: Environment variables > .env file > Default values

Validation and Type Checking

Pydantic automatically validates settings at startup:
class Settings(BaseSettings):
    # Must be a valid URL
    FACTUS_BASE_URL: str
    
    # Must be an integer
    ACCESS_TOKEN_EXPIRE_MINUTES: int = 60
    
    # Must be one of the specified values
    ALGORITHM: str = "HS256"
If validation fails, you’ll get a clear error:
ValidationError: 1 validation error for Settings
FACTUS_BASE_URL
  field required (type=value_error.missing)

Best Practices

Always use environment variables for sensitive data:
# ✅ Good
SECRET_KEY: str  # Loaded from environment

# ❌ Bad
SECRET_KEY: str = "hardcoded-secret-key-123"
Make development easier by providing sensible defaults:
# ✅ Good - has default
API_V1_STR: str = "/api/v1"

# ✅ Good - no default for sensitive data
SECRET_KEY: str
Leverage Pydantic’s validation:
# Automatic type conversion and validation
ACCESS_TOKEN_EXPIRE_MINUTES: int = 60
DEBUG: bool = False
ALLOWED_HOSTS: list[str] = []
Provide a .env.example file:
# .env.example
FACTUS_BASE_URL=https://api.factus.com.co
FACTUS_CLIENT_ID=your_client_id
FACTUS_CLIENT_SECRET=your_client_secret
SECRET_KEY=generate_with_python_secrets_module
Create environment-specific files:
.env.development
.env.staging
.env.production
Load the appropriate one:
class Settings(BaseSettings):
    model_config = SettingsConfigDict(
        env_file=f".env.{os.getenv('ENV', 'development')}"
    )

Testing Configuration

Override settings in tests using environment variables:
tests/conftest.py
import pytest
import os

@pytest.fixture
def test_settings():
    """Override settings for testing"""
    os.environ["FACTUS_BASE_URL"] = "https://test.factus.com.co"
    os.environ["SECRET_KEY"] = "test-secret-key"
    
    from app.src.core.config import Settings
    return Settings()

@pytest.fixture
def mock_gateway(test_settings):
    """Create gateway with test configuration"""
    from app.src.infrastructure.gateways.factus_auth_gateway import FactusAuthGateway
    return FactusAuthGateway(
        base_url=test_settings.FACTUS_BASE_URL,
        client_id="test_client",
        client_secret="test_secret"
    )

Generating Secret Keys

Generate secure secret keys using Python’s secrets module:
python -c "import secrets; print(secrets.token_urlsafe(32))"
Example output:
Zh3jK8pL9mN2qR5tV7wX0yZ1aB3cD4eF6gH8iJ0kL2mN4oP6qR8sT0uV2wX4y

Environment-Specific Configuration

Development

.env.development
FACTUS_BASE_URL=https://sandbox.factus.com.co
FACTUS_CLIENT_ID=dev_client_id
FACTUS_CLIENT_SECRET=dev_secret
SECRET_KEY=dev_secret_key
ACCESS_TOKEN_EXPIRE_MINUTES=480  # Longer for development

Production

.env.production
FACTUS_BASE_URL=https://api.factus.com.co
FACTUS_CLIENT_ID=${FACTUS_CLIENT_ID}  # From secrets manager
FACTUS_CLIENT_SECRET=${FACTUS_CLIENT_SECRET}
SECRET_KEY=${SECRET_KEY}
ACCESS_TOKEN_EXPIRE_MINUTES=60
In production, use a secrets manager (AWS Secrets Manager, HashiCorp Vault, etc.) instead of .env files.

Architecture Overview

Learn about the overall project structure

Clean Architecture

Understand the ports and adapters pattern

Quickstart Guide

Set up your development environment

Quick Start

Get started with the API

Build docs developers (and LLMs) love