-
Notifications
You must be signed in to change notification settings - Fork 0
Project Structure
John R. D'Orazio edited this page Feb 18, 2026
·
3 revisions
This page describes the organization of the OntoKit API codebase.
ontokit-api/
├── ontokit/ # Application source code
│ ├── __init__.py
│ ├── main.py # FastAPI application entry point
│ ├── runner.py # CLI entry point (`ontokit` command)
│ ├── version.py # Version management (Weblate-style)
│ ├── worker.py # ARQ background job queue
│ ├── api/ # API routes
│ │ └── routes/ # REST routers
│ │ ├── auth.py # Authentication endpoints
│ │ ├── projects.py # Project management
│ │ ├── ontologies.py # Ontology CRUD
│ │ ├── classes.py # OWL class operations
│ │ ├── properties.py # OWL property operations
│ │ ├── pull_requests.py # Pull request workflow
│ │ ├── lint.py # Ontology linting
│ │ └── search.py # Search and SPARQL
│ ├── core/ # Core functionality
│ │ ├── __init__.py
│ │ ├── config.py # Settings (Pydantic)
│ │ ├── database.py # Database connection
│ │ └── auth.py # JWT validation
│ ├── models/ # SQLAlchemy models
│ │ ├── __init__.py # Model exports
│ │ └── project.py # Project, ProjectMember
│ ├── schemas/ # Pydantic schemas
│ │ ├── auth.py # Auth request/response
│ │ ├── project.py # Project schemas
│ │ ├── ontology.py # Ontology schemas
│ │ ├── owl_class.py # OWL class schemas
│ │ ├── owl_property.py # OWL property schemas
│ │ └── search.py # Search schemas
│ ├── services/ # Business logic
│ │ ├── ontology.py # Ontology operations (RDFLib)
│ │ ├── project_service.py # Project operations
│ │ ├── linter.py # Ontology validation rules
│ │ ├── pull_request_service.py # PR workflow with semantic diff
│ │ ├── github_service.py # GitHub App integration
│ │ └── search.py # Search service
│ ├── git/ # Git integration
│ │ └── bare_repository.py # pygit2 bare repos for concurrent access
│ └── collab/ # Collaboration (WebSocket)
│ ├── presence.py # User presence
│ └── protocol.py # Message protocol
├── alembic/ # Database migrations
│ ├── env.py # Migration environment
│ ├── script.py.mako # Migration template
│ └── versions/ # Migration files
├── tests/ # Test suite
│ ├── conftest.py # Pytest fixtures
│ └── test_*.py # Test files
├── scripts/ # Utility scripts
│ └── setup-zitadel.sh # Zitadel OIDC configuration
├── docs/ # Documentation
├── .env # Environment variables (not in git)
├── .env.example # Example environment file
├── alembic.ini # Alembic configuration
├── compose.yaml # Docker Compose (full stack)
├── compose.prod.yaml # Docker Compose (infrastructure only)
├── Dockerfile # Container build instructions
├── pyproject.toml # Project configuration
└── README.md
The FastAPI application entry point:
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI(
title="OntoKit API",
description="Collaborative OWL Ontology Curation Platform",
)
# CORS middleware
app.add_middleware(CORSMiddleware, ...)
# Include API routers (URL prefix is /api/v1/, not a directory concern)
app.include_router(api_router, prefix="/api/v1")Configuration management using Pydantic Settings:
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
app_env: str = "development"
database_url: PostgresDsn
# ... other settings
model_config = SettingsConfigDict(env_file=".env")
settings = get_settings() # Cached singletonAsync SQLAlchemy database setup:
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
class Base(DeclarativeBase):
pass
engine = create_async_engine(str(settings.database_url))
async_session_maker = async_sessionmaker(engine)
async def get_db() -> AsyncGenerator[AsyncSession, None]:
async with async_session_maker() as session:
yield sessionJWT token validation and user extraction:
class CurrentUser(BaseModel):
id: str
email: str | None
name: str | None
roles: list[str] = []
# Dependency injection types
RequiredUser = Annotated[CurrentUser, Depends(get_current_user)]
OptionalUser = Annotated[CurrentUser | None, Depends(get_current_user_optional)]API routers are registered in main.py with the /api/v1/ URL prefix:
# In main.py
app.include_router(projects.router, prefix="/api/v1/projects", tags=["Projects"])
app.include_router(ontologies.router, prefix="/api/v1/ontologies", tags=["Ontologies"])
# ... more routers┌──────────────────────────────────────┐
│ API Layer (Routers) │ ← HTTP handling, validation
├──────────────────────────────────────┤
│ Service Layer │ ← Business logic
├──────────────────────────────────────┤
│ Data Layer (Models) │ ← Database operations
└──────────────────────────────────────┘
- Router receives HTTP request
- Pydantic schema validates request body
- Dependencies inject services, user context
- Service executes business logic
- Model interacts with database
- Response schema formats the response
# Router (ontokit/api/routes/projects.py)
@router.post("", response_model=ProjectResponse)
async def create_project(
project: ProjectCreate, # Validated by Pydantic
service: ProjectService = Depends(), # Injected service
user: RequiredUser, # Authenticated user
) -> ProjectResponse:
return await service.create(project, user)
# Service (ontokit/services/project_service.py)
async def create(self, project: ProjectCreate, owner: CurrentUser):
db_project = Project(
name=project.name,
owner_id=owner.id,
...
)
self.db.add(db_project)
await self.db.commit()
return self._to_response(db_project, owner)FastAPI's Depends() is used throughout:
# Database session
def get_db():
return async_session_maker()
# Service with database
def get_project_service(db: AsyncSession = Depends(get_db)):
return ProjectService(db)
# In router
@router.get("/")
async def list(service: ProjectService = Depends(get_project_service)):
...| Type | Convention | Example |
|---|---|---|
| Files | snake_case.py |
project_service.py |
| Classes | PascalCase |
ProjectService |
| Functions | snake_case |
create_project |
| Variables | snake_case |
user_id |
| Constants | UPPER_SNAKE_CASE |
MAX_LIMIT |
| API paths |
kebab-case (usually) or snake_case
|
/api/v1/projects |
For each entity, we typically have:
| Schema | Purpose | Example |
|---|---|---|
{Entity}Base |
Shared fields | ProjectBase |
{Entity}Create |
Input for creation | ProjectCreate |
{Entity}Update |
Input for updates (all optional) | ProjectUpdate |
{Entity}Response |
API response | ProjectResponse |
{Entity}ListResponse |
Paginated list | ProjectListResponse |
# ontokit/models/comment.py
class Comment(Base):
__tablename__ = "comments"
...
# ontokit/models/__init__.py
from ontokit.models.comment import Commentalembic revision --autogenerate -m "Add comments"
alembic upgrade head# ontokit/schemas/comment.py
class CommentCreate(BaseModel): ...
class CommentResponse(BaseModel): ...# ontokit/services/comment_service.py
class CommentService:
def __init__(self, db: AsyncSession):
self.db = db
async def create(self, ...): ...# ontokit/api/routes/comments.py
router = APIRouter()
@router.post("", response_model=CommentResponse)
async def create_comment(...): ...# ontokit/main.py
app.include_router(comments.router, prefix="/api/v1/comments", tags=["Comments"])- Development - Development workflow
- Database - Database operations
- API Reference - API documentation