How API Endpoints Are Organized in Securo: A FastAPI Router Architecture Guide
Securo organizes its REST API endpoints using FastAPI's APIRouter pattern, with domain-specific modules in backend/app/api/ each defining their own URL prefix and tags, then centrally registered in backend/app/main.py.
Securo's backend follows a clean, domain-driven design that makes its API structure predictable and maintainable. Built on FastAPI, the codebase splits every logical group of HTTP endpoints into dedicated router modules. This article breaks down exactly how the securo-finance/securo repository structures its API layer, with specific file paths and wiring patterns you can apply to your own FastAPI projects.
Router Module Structure in backend/app/api/
Each business domain gets its own Python file under backend/app/api/. These files create APIRouter instances with distinct prefixes and tags, then export the router for central registration.
Special Case: Routers Without Prefixes
Some authentication modules define routes with explicit path prefixes baked into their endpoint decorators rather than the APIRouter prefix parameter. This pattern appears in:
two_factor.py— routes explicitly start with/api/2fapasskeys.py— routes explicitly start with/api/passkeys
Central Router Registration in main.py
The FastAPI application instance lives in backend/app/main.py. This file imports all router modules and mounts them with app.include_router().
# Simplified structure as implemented in securo-finance/securo
from fastapi import FastAPI
from app.api import (
workspaces,
user_lookup,
transactions,
assets,
budgets,
categories,
# ... all other router modules
)
app = FastAPI(title="Securo API")
app.include_router(workspaces.router)
app.include_router(user_lookup.router)
app.include_router(transactions.router)
app.include_router(assets.router)
app.include_router(budgets.router)
# ... remaining routers
This central registration pattern ensures:
- No circular imports — routers depend on
main.py, not each other - Consistent middleware stack — all routes inherit the same CORS, authentication, and logging layers
- Single source of truth — one file shows the complete API surface
Agent-Specific API Layer
Securo extends its API organization for AI agent functionality through a separate subpackage. Agent-related endpoints reside in backend/app/agents/api/ and follow the same router pattern.
| Domain | Router Prefix | Source File |
|---|---|---|
| Agents (various sub-domains) | /api/agents/* |
[agents.py](https://github.com/securo-finance/securo/blob/main/backend/app/agents/api/agents.py) |
| Knowledge | /api/agents/knowledge |
[knowledge.py](https://github.com/securo-finance/securo/blob/main/backend/app/agents/api/knowledge.py) |
| Conversations | /api/agents/conversations |
[conversations.py](https://github.com/securo-finance/securo/blob/main/backend/app/agents/api/conversations.py) |
This separation keeps agent logic isolated from core financial operations while maintaining architectural consistency.
Design Principles Behind Securo's API Organization
URL Prefixes Mirror Resource Hierarchy
Every prefix nests under /api/ and uses kebab-case plural nouns. This creates intuitive URLs:
/api/workspaces/{id}/accounts
/api/transactions?workspace_id={id}
/api/assets/{id}/prices
Tags Enable OpenAPI Documentation Grouping
Each router module assigns relevant tags to its endpoints. These appear in the auto-generated Swagger UI at /docs, grouping related operations visually.
# Typical router initialization pattern
from fastapi import APIRouter
router = APIRouter(
prefix="/api/assets",
tags=["assets"]
)
Module Size Stays Bounded
No single file accumulates unrelated endpoints. Even closely related domains like transactions and import_transactions split into separate modules based on operation type (CRUD vs. data ingestion).
Practical API Examples
Creating a Workspace
import httpx
async def create_workspace(name: str):
async with httpx.AsyncClient(base_url="http://localhost:8000") as client:
response = await client.post(
"/api/workspaces",
json={"name": name}
)
return response.json()
# → {"id": "ws_abc123", "name": "Personal Finance", "created_at": "2024-01-15T..."}
Listing Assets with Filters
async def list_stocks(workspace_id: str):
async with httpx.AsyncClient(base_url="http://localhost:8000") as client:
response = await client.get(
"/api/assets",
params={"workspace_id": workspace_id, "type": "stock"}
)
return response.json()
# → [{"id": "ast_001", "type": "stock", "symbol": "AAPL", "quantity": 50.0}, ...]
Batch Import Transactions
async def import_csv(workspace_id: str, file_content: bytes):
async with httpx.AsyncClient(base_url="http://localhost:8000") as client:
response = await client.post(
"/api/transactions/import",
params={"workspace_id": workspace_id},
files={"file": ("transactions.csv", file_content, "text/csv")}
)
return response.json()
# → {"import_id": "imp_789", "rows_processed": 142, "errors": []}
Key Files for Understanding Securo API Organization
| File Path | Purpose |
|---|---|
backend/app/main.py |
FastAPI app factory; central router registration |
backend/app/api/__init__.py |
Package exports (if present) |
backend/app/api/{domain}.py |
Individual router modules (30+ files) |
backend/app/agents/api/ |
Agent-specific router subpackage |
backend/app/core/config.py |
Settings including API versioning |
backend/app/dependencies/ |
Shared FastAPI dependencies for auth, DB sessions |
Summary
- Domain-driven modules: Each business concept lives in its own file under
backend/app/api/ - Explicit prefixes: Every
APIRouterdeclares its/api/{resource}prefix at initialization - Central registration:
backend/app/main.pyimports and mounts all routers withinclude_router() - Tags for documentation: Logical grouping in OpenAPI/Swagger through consistent tag usage
- Agent separation: Extended API surface in
backend/app/agents/api/follows identical patterns
Frequently Asked Questions
What pattern does Securo use for FastAPI routing?
Securo uses FastAPI's APIRouter pattern with domain-specific modules. Each file in backend/app/api/ creates a router with a dedicated prefix and tags, then exports it for central registration in main.py. This is the recommended FastAPI pattern for applications with more than a handful of endpoints.
How does Securo handle authentication-related API routes?
Authentication routes split across multiple specialized modules. OIDC flows live in oidc_auth.py with prefix /api/auth/oidc. Two-factor authentication and passkeys each have dedicated modules (two_factor.py, passkeys.py) that define explicit paths rather than router prefixes. This allows flexible grouping of related but distinct security mechanisms.
Where are agent-specific endpoints located?
Agent API endpoints reside in backend/app/agents/api/ rather than the main backend/app/api/ directory. This subpackage contains agents.py, knowledge.py, conversations.py, and related files, all using the same APIRouter pattern with prefixes under /api/agents/. The separation prevents agent logic from polluting core financial domain code.
Can I add new endpoints without modifying main.py?
In Securo's architecture, yes, but with a required registration step. You create a new module in backend/app/api/ with a router, then explicitly import and include it in backend/app/main.py. There is no auto-discovery mechanism — explicit registration ensures intentional API surface changes and prevents accidental exposure of development or test endpoints.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →