Skip to content
v1.2.4

Backend Module

The backend half of a module is a Python package that exposes a FastAPI sub-application. Ocelescope discovers it through a Python entry point and mounts it under a versioned path.

The module template includes scripts that generate new modules from templates. To create a backend module, run the following from the repository root:

Terminal window
pnpm run add:backend ocel-stats

ocel-stats is the name of the folder the module is created in. To create a backend and a frontend module together, use pnpm run add:module instead.

The script uses Copier to generate the module from the backend module template. It asks for the module’s name, a short description and an author. All other names are derived from the module name:

Module namePython packageModule keyClass
OCEL Statsocelescope-module-ocel-statsocelStatsOCELStats
3D Viewerocelescope-module-3d-viewermodule3dViewerModule3DViewer

The module is created in backend-modules/<folder>:

backend-modules/ocel-stats/
├── src/ocelescope_module_ocel_stats/
│ ├── __init__.py
│ ├── module.py # The Module class
│ └── routes.py # The module's API endpoints
├── .copier-answers.yml # Your answers, used to update the module later
├── pyproject.toml
└── README.md

The script then adds the module to the root pyproject.toml, so it is installed as part of the backend. Install it and start the development servers:

Terminal window
pnpm run sync
pnpm run dev

The backend loads the module on startup and serves it under /modules/<key>/v1. The generated module contains a single example endpoint, which you can open at http://localhost:8000/modules/ocelStats/v1/hello.

The following sections explain the generated files and how to extend them.

module.py defines the module itself: a class that inherits from Module (from ocelescope_backend.app.modules). It declares its identity through meta and builds its FastAPI app in create_app.

src/ocelescope_module_ocel_stats/module.py
from fastapi import FastAPI
from ocelescope_backend.app.modules import Module, ModuleMeta
from packaging.version import Version
from ocelescope_module_ocel_stats.routes import router
class OCELStats(Module):
meta = ModuleMeta(key="ocelStats", version=Version("1.0"))
def create_app(self) -> FastAPI:
app = FastAPI(title="OCEL Stats", version=str(self.meta.version))
app.include_router(router)
return app
  • meta.key is the module’s unique identifier and meta.version its version. The module is mounted at /modules/<key>/v<major>, so the major version is part of the URL and you can run multiple major versions side by side.
  • create_app returns a fully independent FastAPI app. Include your routers on it and return it.

routes.py contains the module’s endpoints on an APIRouter. The generated module comes with a single example endpoint:

src/ocelescope_module_ocel_stats/routes.py
from fastapi import APIRouter
from pydantic import BaseModel
router = APIRouter()
class HelloResponse(BaseModel):
message: str
@router.get("/hello", operation_id="hello", response_model=HelloResponse)
def hello() -> HelloResponse:
return HelloResponse(message="Hello from the OCEL Stats backend module")

Give every endpoint a stable operation_id, because it becomes the name of the generated frontend hook. Declaring the response as a Pydantic model gives the generated frontend client fully typed responses.

Two dependencies from ocelescope_backend.app.dependencies give you access to the current session and its logs:

  • ApiSession injects the active Session, which holds the uploaded OCELs and resources.
  • ApiOcel injects an OCEL directly, resolved from the ocel_id path parameter.
src/ocelescope_module_ocel_stats/routes.py
from ocelescope_backend.app.dependencies import ApiOcel
@router.get("/{ocel_id}/object-types", operation_id="getObjectTypes")
def get_object_types(ocel: ApiOcel) -> list[str]:
return ocel.objects.types

The injected OCEL is the same ocelescope library class used everywhere else, so its managers (objects, events, e2o, o2o, …) are available.

Ocelescope finds modules through the ocelescope_backend.modules entry point group. The generated pyproject.toml already declares the module under its key:

pyproject.toml
[project]
name = "ocelescope-module-ocel-stats"
version = "0.1.0"
readme = "README.md"
description = "An Ocelescope module"
authors = [{ name = "Your Name" }]
requires-python = ">=3.11"
dependencies = [
"ocelescope-backend>=0.10.1",
]
[project.entry-points."ocelescope_backend.modules"]
ocelStats = "ocelescope_module_ocel_stats.module:OCELStats"

When the backend starts, it loads every entry point in that group, checks each is a Module subclass, and mounts it. Duplicate key and major-version pairs are skipped, so install only one package per major version.

Add further Python dependencies of your module from its folder with uv add <package>.

Once the backend is in place, build the Frontend Module that consumes it.