Files
echoes-of-the-ash/docs/archive/API_REFACTOR_GUIDE.md
2025-11-07 15:27:13 +01:00

8.6 KiB

🔄 API-First Architecture Refactor

Overview

This refactor moves game logic from the Telegram bot to the FastAPI server, making the API the single source of truth for all game operations.

Benefits

Single Source of Truth - All game logic in one place
Consistency - Web and Telegram bot behave identically
Easier Maintenance - Fix bugs once, applies everywhere
Better Testing - Test game logic via API endpoints
Scalability - Can add more frontends (Discord, mobile app, etc.)
Performance - Direct database access from API

Architecture

┌─────────────────┐          ┌──────────────────┐
│  Telegram Bot   │◄────────►│   FastAPI API    │
│   (Frontend)    │  HTTP    │  (Game Engine)   │
└─────────────────┘          └──────────────────┘
                                      │
                             ┌────────▼────────┐
                             │   PostgreSQL    │
                             │    Database     │
                             └─────────────────┘

┌─────────────────┐
│  React PWA      │◄────────►│   FastAPI API    │
│   (Frontend)    │  HTTP    │  (Game Engine)   │
└─────────────────┘          └──────────────────┘

Implementation Status

Completed

  1. API Client (bot/api_client.py)

    • Async HTTP client using httpx
    • Methods for all game operations
    • Error handling and retry logic
  2. Internal API (api/internal.py)

    • Protected endpoints with internal API key
    • Player management (get, create, update)
    • Movement logic
    • Location queries
    • Inventory operations
    • Combat system
  3. Environment Configuration

    • API_INTERNAL_KEY - Secret key for bot-to-API auth
    • API_BASE_URL - API endpoint for bot to call
  4. Dependencies

    • Added httpx==0.25.2 to requirements.txt

🔄 To Be Migrated

The following bot files need to be updated to use the API client instead of direct database access:

  1. bot/handlers.py - Telegram command handlers

    • Use api_client.get_player() instead of database.get_player()
    • Use api_client.move_player() instead of direct location updates
    • Use api_client.start_combat() for combat initiation
  2. bot/logic.py - Game logic functions

    • Movement should call API
    • Item usage should call API
    • Status effects should be managed by API
  3. bot/combat.py - Combat system

    • Can keep combat logic here OR move to API
    • Recommendation: Move to API for consistency

Internal API Endpoints

All internal endpoints require the X-Internal-Key header for authentication.

Player Management

Method Endpoint Description
GET /api/internal/player/telegram/{telegram_id} Get player by Telegram ID
POST /api/internal/player Create new player
PATCH /api/internal/player/telegram/{telegram_id} Update player data

Location & Movement

Method Endpoint Description
GET /api/internal/location/{location_id} Get location details
POST /api/internal/player/telegram/{telegram_id}/move Move player

Inventory

Method Endpoint Description
GET /api/internal/player/telegram/{telegram_id}/inventory Get inventory
POST /api/internal/player/telegram/{telegram_id}/use_item Use item
POST /api/internal/player/telegram/{telegram_id}/equip Equip/unequip item

Combat

Method Endpoint Description
POST /api/internal/combat/start Start combat
GET /api/internal/combat/telegram/{telegram_id} Get combat state
POST /api/internal/combat/telegram/{telegram_id}/action Combat action

Security

Internal API Key

The internal API uses a shared secret key (API_INTERNAL_KEY) to authenticate bot requests:

  • Not exposed to users - Only bot and API know it
  • Different from JWT tokens - User auth uses JWT
  • Should be changed in production - Use strong random key

Network Security

  • Bot and API communicate via Docker internal network
  • No public exposure of internal endpoints
  • Traefik only exposes public API and PWA

Migration Guide

Step 1: Deploy Updated Services

# Rebuild both bot and API with new code
docker compose up -d --build echoes_of_the_ashes_bot echoes_of_the_ashes_api

Step 2: Test Internal API

# Test from bot container
docker exec echoes_of_the_ashes_bot python -c "
import asyncio
from bot.api_client import api_client

async def test():
    player = await api_client.get_player(10101691)
    print(f'Player: {player}')

asyncio.run(test())
"

Step 3: Migrate Bot Handlers

Update bot/handlers.py to use API client:

Before:

from bot.database import get_player, update_player

async def move_command(update, context):
    player = await get_player(telegram_id=user_id)
    # ... movement logic ...
    await update_player(telegram_id=user_id, updates={...})

After:

from bot.api_client import api_client

async def move_command(update, context):
    result = await api_client.move_player(user_id, direction)
    if result.get('success'):
        # Handle success
    else:
        # Handle error

Step 4: Remove Direct Database Access

Once all handlers are migrated, bot should only use:

  • api_client.* for game operations
  • database.* only for legacy compatibility (if needed)

Testing

Manual Testing

  1. Test Player Creation
curl -X POST http://localhost:8000/api/internal/player \
  -H "X-Internal-Key: bot-internal-key-9f8e7d6c5b4a3210fedcba9876543210" \
  -H "Content-Type: application/json" \
  -d '{"telegram_id": 12345, "name": "TestPlayer"}'
  1. Test Movement
curl -X POST http://localhost:8000/api/internal/player/telegram/12345/move \
  -H "X-Internal-Key: bot-internal-key-9f8e7d6c5b4a3210fedcba9876543210" \
  -H "Content-Type: application/json" \
  -d '{"direction": "north"}'
  1. Test Location Query
curl -X GET http://localhost:8000/api/internal/location/start_point \
  -H "X-Internal-Key: bot-internal-key-9f8e7d6c5b4a3210fedcba9876543210"

Integration Testing

  1. Send /start to Telegram bot - should still work
  2. Try moving via bot - should use API
  3. Try moving via PWA - should use same API
  4. Verify both show same state

Rollback Plan

If issues occur, rollback is simple:

# Revert to previous bot image
docker compose down echoes_of_the_ashes_bot
git checkout HEAD~1 bot/
docker compose up -d --build echoes_of_the_ashes_bot

Bot will continue using direct database access until refactor is complete.

Performance Considerations

Latency

  • Before: Direct database query (~10-50ms)
  • After: HTTP request + database query (~20-100ms)
  • Impact: Negligible for human interaction

Caching

Consider caching in API for:

  • Location data (rarely changes)
  • Item definitions (static)
  • NPC templates (static)

Connection Pooling

  • httpx client reuses connections
  • Database connection pool in API
  • No need for bot to manage DB connections

Monitoring

Add logging to track API calls:

# In api_client.py
import logging
logger = logging.getLogger(__name__)

async def get_player(self, telegram_id: int):
    logger.info(f"API call: get_player({telegram_id})")
    # ... rest of method ...

Future Enhancements

  1. Rate Limiting - Prevent API abuse
  2. Request Metrics - Track endpoint usage
  3. Error Recovery - Automatic retry with backoff
  4. API Versioning - /api/v1/internal/...
  5. GraphQL - Consider for complex queries

Status: IN PROGRESS

  • Create API client
  • Create internal endpoints
  • Add authentication
  • Update environment config
  • Fix location endpoint bug
  • Migrate bot handlers
  • Update bot logic
  • Remove direct database access from bot
  • Integration testing
  • Documentation

Next Steps:

  1. Deploy current changes (API fixes are ready)
  2. Test internal API endpoints
  3. Begin migrating bot handlers one by one
  4. Full integration testing
  5. Remove old database calls from bot

This refactor sets the foundation for a scalable, maintainable architecture! 🚀