Skip to main content

Architecture

This document provides an overview of the FF-API-External architecture, including its core components, design patterns, and how the various parts of the system interact.

Table of Contents

System Overview

FF-API-External is a Flask-based API service that integrates with numerous third-party services. Its architecture follows a modular design with clear separation of concerns between routing, business logic, and data access.

The high-level architecture can be visualized as follows:

Client Request

[Nginx/Apache] → Reverse Proxy/Load Balancer

[WSGI Server] → Gunicorn/uWSGI

[Flask Application]

┌─────────────────┬─────────────────┬─────────────────┐
│ API Routes │ Service Models │ Database Utils │
└─────────────────┴─────────────────┴─────────────────┘
↓ ↓ ↓
┌─────────────────┬─────────────────┬─────────────────┐
│ Third-Party │ MySQL │ MongoDB │
│ Services │ Database │ Database │
└─────────────────┴─────────────────┴─────────────────┘

Core Components

Flask Application

The Flask application is the central component that initializes the web server and registers all the API routes. It's defined in app.py and configured in server.py:

# app.py
from flask import Flask
from flask_cors import CORS

app = Flask(__name__)
CORS(app)
# server.py
import inspect
import os
import sys
import datetime
from app import app

# Util imports
from utils.json_response import JsonResponse

currentdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
parentdir = os.path.dirname(currentdir)
sys.path.insert(0, parentdir)

# Response object
resp = JsonResponse()

# API route imports
from view.api.ai.openai.chatgpt import chatgpt_route
from view.api.ai.anthropic.claude import claude_route
# ... other route imports

# Inject Registered Blueprints
app.register_blueprint(chatgpt_route)
app.register_blueprint(claude_route)
# ... other blueprint registrations

API Routes

API routes are organized as Flask Blueprints, with each blueprint corresponding to a specific third-party service or functional area. They're defined in the view/api/ directory:

# Example from view/api/ai/anthropic/claude.py
from flask import Blueprint, request
from flask_cors import cross_origin
from models.ai.anthropic.claude import ClaudeAI
from utils.json_response import JsonResponse

claude_route = Blueprint('claude_route', __name__)
resp = JsonResponse()

@claude_route.route('/api/v1/claude/prompt', methods=['POST'])
@cross_origin()
def api_v1_claude_prompt():
try:
claude = ClaudeAI()
prompt = request.form.get("prompt")
max_tokens = int(request.form.get("max_tokens", 1000))
temperature = float(request.form.get("temperature", 0.7))
system_prompt = request.form.get("system_prompt")

response = claude.generate_response(
prompt=prompt,
max_tokens=max_tokens,
temperature=temperature,
system_prompt=system_prompt
)

return resp.returnResponse(200, response)
except Exception as e:
return resp.returnResponse(400, f"Error: {str(e)}")

Service Models

Service models encapsulate the business logic for interacting with third-party services. They're defined in the models/ directory:

# Example from models/ai/anthropic/claude.py
import anthropic

class ClaudeAI:
def __init__(self):
self.api_key = config('CLAUDE_API_KEY')
self.client = anthropic.Anthropic(api_key=self.api_key)
self.available_models = {
"claude-3-opus-20240229",
"claude-3-sonnet-20240229",
"claude-3-haiku-20240307",
"claude-3-5-sonnet-20240620",
"claude-3-7-sonnet-20250219"
}

def generate_response(self, prompt, max_tokens=1000, temperature=0.7, system_prompt=None):
# Implementation to generate response from Claude AI
# ...

Database Utilities

Database utilities provide standardized methods for interacting with the databases. They're defined in the dbutils/ directory:

# Example from dbutils/mongodb.py
class MongoDB:
def __init__(self, no_http=False):
self.no_http = no_http
try:
self.mclient = MongoClient(config('MONGODB_MAIN_PROD'))
except FileNotFoundError:
currentdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
parentdir = os.path.dirname(currentdir)
self.mclient = MongoClient(config('MONGODB_MAIN_DEV_PREFIX') + parentdir + "/mongodb-1-cert.crt")
self.resp = JsonResponse()

def insert_or_update(self, db, col, filter, update):
"""
Inserts or updates a document in a MongoDB collection based on the filter.
"""
# Implementation...
# Example from dbutils/mysql.py
class MySQL:
def __init__(self):
self.conn = mysql.connection
self.cur = self.conn.cursor()
self.resp = JsonResponse()

def select(self, select):
"""Execute a SELECT query and return the results."""
# Implementation...

Utility Functions

Utility functions provide reusable functionality for common tasks such as JSON response formatting, validation, and HTTP requests. They're defined in the utils/ directory:

# Example from utils/json_response.py
class JsonResponse:
def returnResponse(
self,
code: int = 200,
msg: Any = "",
exec_time: int = 0,
cached: bool = False,
misc: Optional[Any] = None
):
"""Format a standardized JSON response."""
# Implementation...
# Example from utils/validation.py
class Validation:
def is_valid_email(self, email):
"""Validates the given email address using a regular expression."""
regex = r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b'
return bool(re.fullmatch(regex, email))

Design Patterns

FF-API-External employs several design patterns to ensure maintainability, scalability, and separation of concerns:

Factory Pattern

The service models act as factories that create and manage instances of third-party clients:

class GSMArena:
def __init__(self):
self.mdb = MongoDB()
self.gsm_arena_base_url = "https://www.gsmarena.com/"

def get_brand_list(self):
# Implementation...

Decorator Pattern

Flask decorators are used to add functionality to routes:

@serp_google_place_route.route('/api/v1/place/serp/search', methods=['GET'])
@expected_params(['q', 'll'])
@cross_origin()
def api_v1_place_serp_search():
# Implementation...

Custom decorators are used for parameter validation:

def required_params(req_keys):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
# Validation logic...
return func(*args, **kwargs)
return wrapper
return decorator

Repository Pattern

The database utilities implement the repository pattern for data access:

class MongoDB:
def find_many(self, db, col, query=None, sortkey=None, sortorder=None, limit=None, skip=0, projection=None):
# Implementation...

Adapter Pattern

Service models act as adapters for third-party APIs, providing a consistent interface:

class VincarioVINDecoder:
def __init__(self, api_key, secret_key):
self.api_key = api_key
self.secret_key = secret_key
self.base_url = "https://api.vindecoder.eu/3.0"

def decode(self, vin):
# Implementation...

Interaction Flow

The typical flow of a request through the system is as follows:

  1. Client Request: A client sends an HTTP request to an API endpoint
  2. Route Handling: The appropriate Flask route handler processes the request
  3. Parameter Validation: Request parameters are validated
  4. Service Model: The route handler instantiates a service model
  5. Business Logic: The service model executes the business logic
  6. External API or Database: The service model interacts with external APIs or databases
  7. Response Formatting: The response is formatted using the JsonResponse utility
  8. Client Response: The formatted response is returned to the client

Example of this flow:

Client → [GET /api/v1/gsmarena/brands]

Flask Route (gsmarena_route.py)

GSMArena Model (gsmarena.py)

MongoDB Utility (mongodb.py)

MongoDB Database

JsonResponse Utility (json_response.py)

Client ← [JSON Response]

Dependency Management

Dependencies are managed through the requirements.txt file, which specifies the required Python packages and their versions:

ago==0.0.95
aniso8601==9.0.1
annotated-types==0.6.0
anthropic~=0.43.0
anyio==3.6.2
# ... other dependencies

Key dependencies include:

  • Flask: Web framework
  • Flask-CORS: Cross-Origin Resource Sharing
  • Flask-MySQLdb: MySQL integration
  • PyMongo: MongoDB integration
  • Requests: HTTP client
  • Anthropic: Claude AI client
  • OpenAI: ChatGPT client

Error Handling

Error handling is implemented at multiple levels:

Route Level

Try-except blocks in route handlers catch exceptions and return formatted error responses:

@claude_route.route('/api/v1/claude/prompt', methods=['POST'])
@cross_origin()
def api_v1_claude_prompt():
try:
# Implementation...
return resp.returnResponse(200, response)
except Exception as e:
return resp.returnResponse(400, f"Error: {str(e)}")

Service Model Level

Service models include error handling for third-party API calls:

def get_vehicle_makes_by_year(self, year):
try:
url = f"{self.base_url}/car-lists/get/makes/{year}"
response = requests.get(url, headers=self.headers)
response.raise_for_status() # Raise exception for HTTP errors
return response.json()
except requests.exceptions.RequestException as e:
logging.error(f"Error in get_vehicle_makes_by_year: {str(e)}")
return {"error": str(e)}

Database Level

Database utilities include error handling for database operations:

def insert_or_update(self, db, col, filter, update):
try:
# Implementation...
except (PyMongoError, TypeError) as e:
logging.error("MongoDB Error: %s", str(e))
return self.resp.returnResponse(400, f"MDB Error: {str(e)}")

Extensibility

The modular architecture of FF-API-External makes it easily extensible:

Adding a New Service

To add a new third-party service:

  1. Create a new service model in the models/ directory
  2. Create a new route blueprint in the view/api/ directory
  3. Register the blueprint in server.py

Adding a New Endpoint

To add a new endpoint for an existing service:

  1. Add a new route handler to the appropriate blueprint
  2. Implement the required functionality in the service model

Adding a New Database

To add support for a new database:

  1. Create a new database utility in the dbutils/ directory
  2. Implement the standard methods (insert, update, select, etc.)
  3. Update the relevant service models to use the new database utility