Skip to content

Project Structure

Understand the Apiary codebase architecture and organization.

Overview

Apiary follows a modular architecture with clear separation of concerns:

  • config/ - Configuration management and validation
  • core/ - Reusable utilities and base functionality
  • models/ - Data models and schemas
  • routers/ - HTTP endpoint handlers
  • services/ - Business logic implementations
  • static/ & templates/ - Web assets
  • tests/ - Test suite

Directory Layout

apiary/
├── app.py                      # FastAPI application factory
├── cli.py                      # Command-line interface
├── __version__.py              # Version information
├── config/                     # Configuration Management
│   ├── __init__.py
│   ├── settings.py             # Settings model (Pydantic)
│   ├── endpoint_config.py      # Endpoint configuration model
│   ├── settings_template.json  # Default settings template
│   ├── endpoints_template.json # Default endpoints template
│   ├── settings.json           # Your settings (gitignored)
│   └── endpoints.json          # Your endpoints (gitignored)
├── core/                       # Core Framework
│   ├── auth/                   # Authentication & Authorization
│   │   ├── authentication.py   # API key verification
│   │   └── authorization.py    # Dependency injection for auth
│   ├── services/               # Service Base Classes
│   │   └── base.py             # BaseService interface
│   ├── api_key_manager.py      # Key loading & hot-reloading
│   ├── api_key_validator.py    # Configuration validation
│   ├── cache.py                # Response caching utilities
│   ├── dependencies.py         # FastAPI dependencies
│   ├── exceptions.py           # Custom exception classes
│   ├── logging_config.py       # Logging configuration
│   ├── metrics.py              # Metrics collection
│   ├── middleware.py           # Request/response middleware
│   ├── rate_limiter.py         # Rate limiting logic
│   ├── request_validation.py   # Request validation
│   └── router_factory.py       # Dynamic router generation
├── models/                     # Data Models
│   ├── requests.py             # Request models (Pydantic)
│   └── responses.py            # Response models (Pydantic)
├── routers/                    # Built-in Routers
│   ├── auth.py                 # /auth endpoints
│   ├── endpoints.py            # /endpoints discovery
│   ├── health.py               # /health checks
│   ├── home.py                 # / landing page
│   └── metrics.py              # /metrics endpoint
├── services/                   # Built-in Services
│   ├── crypto.py               # Crypto helper functions
│   ├── crypto_service.py       # Crypto price service
│   └── hello_service.py        # Hello world demo service
├── routers_custom/             # Custom Routers (gitignored)
│   └── __init__.py             # Your custom routers here
├── services_custom/            # Custom Services (gitignored)
│   └── __init__.py             # Your custom services here
├── static/                     # Static Assets
│   ├── css/                    # Stylesheets
│   │   ├── docs.css
│   │   └── theme.css
│   └── img/                    # Images & icons
├── templates/                  # Jinja2 Templates
│   ├── home/                   # Landing page templates
│   └── shared/                 # Shared layouts
├── tests/                      # Test Suite
│   ├── conftest.py             # Pytest configuration
│   ├── unit/                   # Unit tests
│   └── integration/            # Integration tests
├── docs/                       # Documentation (MkDocs)
│   ├── getting-started/
│   ├── guide/
│   ├── deployment/
│   ├── reference/
│   └── about/
├── pyproject.toml              # Project metadata & dependencies
├── uv.lock                     # Dependency lock file
├── pytest.ini                  # Pytest configuration
├── mkdocs.yml                  # Documentation configuration
└── README.md                   # Project overview

Configuration Files

Files like config/settings.json, config/endpoints.json, services_custom/, and routers_custom/ are gitignored. Run uv run apiary init to create them from templates.


Core Components

Application Entry Point

Main application factory that creates and configures the FastAPI application.

Key responsibilities:

  • Initialize FastAPI app with configuration
  • Load settings and endpoint configurations
  • Register routers (built-in and custom)
  • Configure middleware and exception handlers
  • Set up CORS, rate limiting, and logging

Usage:

from app import api  # Import the FastAPI app
# Run with uvicorn directly
uvicorn app:api --host 0.0.0.0 --port 8000

# Run with CLI
uv run apiary serve

Command-line interface for maintenance and development tasks.

Available commands:

  • serve - Start the development server
  • init - Initialize configuration files
  • validate-config - Validate API keys
  • test - Test configuration and imports
  • backup - Backup configuration
  • clean - Clean generated files

See CLI Reference for full documentation.

Configuration Layer

Pydantic Settings model for type-safe configuration.

Features:

  • Environment variable support
  • JSON file loading
  • Type validation
  • Default values

Settings Reference

Endpoint configuration model with validation.

Features:

  • Pydantic validation
  • Duplicate detection
  • Path validation
  • HTTP method enum

Endpoint Config Reference

Default configuration templates:

  • settings_template.json - Default application settings
  • endpoints_template.json - Example endpoint configurations

These are copied to settings.json and endpoints.json by uv run apiary init.

Core Framework

core/auth/ - Authentication and authorization system

Files:

  • authentication.py - API key verification logic
  • authorization.py - FastAPI dependencies for protecting endpoints

Authentication Reference

core/services/base.py - Abstract base class for all services

Features:

  • HTTP client management
  • Async support
  • Lifecycle management (cleanup)

Base Service Reference

core/api_key_manager.py - API key loading and hot-reloading

Features:

  • Load keys from strings or files
  • Automatic file change detection
  • In-memory caching
  • Thread-safe operations

API Key Manager Reference

core/api_key_validator.py - Configuration validation

Validates:

  • API key file existence
  • Key format and strength
  • Configuration consistency

Validator Reference

Core utilities:

  • cache.py - Response caching headers
  • dependencies.py - Dependency injection
  • exceptions.py - Custom exceptions
  • logging_config.py - Structured logging
  • metrics.py - Application metrics
  • middleware.py - Request/response processing
  • rate_limiter.py - Rate limiting logic
  • request_validation.py - Request validation
  • router_factory.py - Dynamic router creation

Core Modules Reference

Routers & Services

routers/ - HTTP endpoint handlers

Router Path Purpose
home.py / Landing page
health.py /health Health checks
metrics.py /metrics Application metrics
auth.py /auth Authentication info
endpoints.py /endpoints Endpoint discovery

Routers can be enabled/disabled in settings.json:

{
  "enabled_routers": ["health", "metrics", "auth", "endpoints"]
}

services/ - Business logic implementations

Service Purpose Reference
hello_service.py Demo service HelloService
crypto_service.py Crypto prices CryptoService

All services inherit from BaseService:

from core.services.base import BaseService

class MyService(BaseService):
    async def call(self, parameters=None):
        return {"result": "data"}

Update-safe directories for your code:

services_custom/ - Your custom services (gitignored)

  • Add your service files here
  • Register in services_custom/__init__.py

routers_custom/ - Your custom routers (gitignored)

  • Add your router files here
  • Each must export a router variable
  • Enable in settings.jsonenabled_routers

Created by uv run apiary init --custom-dirs

Creating Services Guide

Data Models

Response models using Pydantic.

Base classes:

  • BaseResponse - Includes automatic timestamp

Models Reference

Request models and query parameters.

Models Reference


Request Flow

Understanding how a request flows through Apiary:

graph TB
    A[Client Request] --> B[Middleware]
    B --> C{Rate Limit?}
    C -->|Exceeded| D[429 Too Many Requests]
    C -->|OK| E{Auth Required?}
    E -->|Yes| F{Valid API Key?}
    F -->|No| G[401 Unauthorized]
    F -->|Yes| H[Router Handler]
    E -->|No| H
    H --> I{Dynamic Endpoint?}
    I -->|Yes| J[Router Factory]
    I -->|No| K[Static Router]
    J --> L[Service Call]
    K --> M[Direct Handler]
    L --> N[Response Model]
    M --> N
    N --> O[Middleware]
    O --> P[Client Response]
  1. Middleware - Logs request, adds request ID, applies CORS
  2. Rate Limiting - Checks request rate (if enabled)
  3. Authentication - Validates API key (if required)
  4. Routing - Matches request to handler (static or dynamic)
  5. Service - Executes business logic
  6. Response - Returns validated Pydantic model
  7. Middleware - Adds response headers, logs response

Adding New Components

Adding a Service

  1. Create service file in services_custom/:

    # services_custom/weather_service.py
    from core.services.base import BaseService
    
    class WeatherService(BaseService):
        async def call(self, parameters=None):
            # Your logic here
            return {"temperature": 72}
    
  2. Register in services_custom/__init__.py:

    from .weather_service import WeatherService
    
    __all__ = ["WeatherService"]
    
  3. Add to config/endpoints.json:

    {
      "path": "/api/weather",
      "method": "GET",
      "service": "weather",
      "enabled": true,
      "requires_auth": false
    }
    

Creating Services Guide

Adding a Router

  1. Create router file in routers_custom/:

    # routers_custom/custom.py
    from fastapi import APIRouter
    
    router = APIRouter()
    
    @router.get("/custom")
    async def custom_endpoint():
        return {"message": "Custom router"}
    
  2. Enable in config/settings.json:

    {
      "enabled_routers": ["health", "metrics", "custom"]
    }
    

Adding Endpoints Guide

Adding a Model

  1. Add to models/responses.py or models/requests.py:

    from models.responses import BaseResponse
    
    class WeatherResponse(BaseResponse):
        temperature: float
        humidity: float
        condition: str
    
  2. Use in router:

    @router.get("/weather", response_model=WeatherResponse)
    async def get_weather():
        return WeatherResponse(
            temperature=72.5,
            humidity=45.0,
            condition="sunny"
        )
    

Configuration Files

Tracked in Git

These files are part of the repository:

  • pyproject.toml - Dependencies and project metadata
  • mkdocs.yml - Documentation configuration
  • pytest.ini - Test configuration
  • config/*_template.json - Configuration templates
  • All .py files in config/, core/, models/, routers/, services/

Not Tracked (Gitignored)

These are created locally and not committed:

  • config/settings.json - Your settings
  • config/endpoints.json - Your endpoints
  • config/*_keys.txt - Your API keys
  • services_custom/ - Your custom services
  • routers_custom/ - Your custom routers
  • backups/ - Configuration backups
  • site/ - Built documentation
  • __pycache__/, *.pyc - Python cache files

This allows you to safely git pull updates without conflicts!


Development Workflow

Initial Setup

# Clone and install
git clone <repository>
cd apiary
uv sync

# Initialize configuration
uv run apiary init

# Edit configuration
edit config/settings.json
edit config/endpoints.json

# Validate and test
uv run apiary validate-config
uv run apiary test

# Start server
uv run apiary serve --reload

Adding Features

# Create custom service
mkdir -p services_custom
edit services_custom/my_service.py

# Configure endpoint
edit config/endpoints.json

# Test changes
uv run apiary test

# Start with auto-reload
uv run apiary serve --reload

Updating from Upstream

# Backup your config
uv run apiary backup --include-custom

# Pull updates
git pull origin main
uv sync

# Check for new config options
diff config/settings_template.json config/settings.json

# Test before deploying
uv run apiary test

Next Steps