Open-Notebook Three-Tier Architecture: Frontend, API, and Database Communication Explained

TLDR: Open-notebook implements a strict three-tier architecture using React/Next.js for the frontend, FastAPI for the API layer, and SurrealDB for the database, with communication flowing through HTTP/REST, SurrealQL queries, and the Esperanto AI abstraction library.

Open-notebook is organized as a production-ready, open-source notebook application with clear separation of concerns across three distinct layers. Understanding the open-notebook three-tier architecture is essential for contributors customizing the codebase or deploying scalable instances. The architectural diagram in the repository's root CLAUDE.md visualizes this stack, showing how each tier handles specific responsibilities while communicating through well-defined interfaces.

Three-Tier Architecture Overview

The system divides responsibilities across the classic presentation, application, and data tiers, enabling independent scaling and technology swaps.

Frontend Layer (React/Next.js/TypeScript)

The presentation layer is built with React and Next.js, handling UI components for notebooks, sources, notes, chat sessions, podcasts, and search. State management relies on Zustand, while TanStack Query manages server state and data fetching. UI components come from shadcn/ui, providing a consistent design system across the application.

API Layer (FastAPI/Python 3.11+)

The middle tier exposes REST endpoints for all domain objects and orchestrates business logic. It runs LangGraph workflows for AI operations and queues asynchronous jobs for long-running tasks. The API selects AI providers dynamically through the Esperanto library, abstracting differences between OpenAI, Anthropic, and other models.

Database Layer (SurrealDB)

The data tier uses SurrealDB, a graph database that stores notebooks, sources, notes, chat sessions, credentials, and vector embeddings for semantic search. Unlike traditional relational databases, SurrealDB handles graph relationships natively, allowing efficient traversal of connections between notebooks and their sources.

Inter-Layer Communication Flows

Each layer communicates only with its adjacent tier, maintaining loose coupling and clear contracts.

Frontend to API via HTTP/REST

The frontend communicates with the API through a thin wrapper located in frontend/src/lib/api/client.ts, which configures an Axios instance pointing at the API base URL (default http://localhost:5055). Domain-specific modules like frontend/src/lib/api/notebooks.ts encapsulate endpoint logic, performing typed HTTP requests that the FastAPI layer handles.

The API returns Pydantic-validated JSON responses defined in api/models.py, which the frontend parses into TypeScript interfaces defined in frontend/src/lib/types/api.ts.

API to Database via SurrealQL

The API layer interacts with SurrealDB through a repository abstraction in open_notebook/database/repository.py. This helper manages connection pooling, authentication, and conversion of RecordID objects to strings. Routers build SurrealQL queries—such as SELECT *, count(<-reference.in) AS source_count FROM notebook—and execute them via the repo_query function, ensuring a clean async interface that handles graph traversals and aggregations.

API to AI Providers via Esperanto

LangGraph workflows (e.g., open_notebook/graphs/ask.py) request models through the ModelManager, which routes to appropriate providers using the unified Esperanto interface. This abstraction allows the system to switch between OpenAI, Anthropic, and other providers without changing workflow code.

Async Job Processing via Surreal-Commands

For long-running operations like podcast generation, the API dispatches jobs via the Surreal-Commands system implemented in api/podcast_service.py. The API returns a command ID immediately, and clients poll /commands/{command_id} to track execution progress, preventing HTTP timeouts while maintaining request traceability.

Code Examples: End-to-End Request Flow

The following examples trace a notebook listing request through all three tiers.

Frontend API Wrapper

// frontend/src/lib/api/notebooks.ts
import apiClient from './client'

export const notebooksApi = {
  list: async (params?: { archived?: boolean; order_by?: string }) => {
    const response = await apiClient.get<NotebookResponse[]>('/notebooks', { params })
    return response.data
  },
}

FastAPI Endpoint


# api/routers/notebooks.py

@router.get("/notebooks", response_model=List[NotebookResponse])
async def get_notebooks(
    archived: Optional[bool] = Query(None),
    order_by: str = Query("updated desc"),
):
    # Validate order_by …

    query = f"""
        SELECT *,
        count(<-reference.in) as source_count,
        count(<-artifact.in) as note_count
        FROM notebook
        ORDER BY {validated_order_by}
    """
    result = await repo_query(query)
    # Optional filtering by archived …

    return [
        NotebookResponse(
            id=str(nb.get("id", "")),
            name=nb.get("name", ""),
            # …

            source_count=nb.get("source_count", 0),
            note_count=nb.get("note_count", 0),
        )
        for nb in result
    ]

Database Repository Execution


# open_notebook/database/repository.py

async def repo_query(query_str: str, vars: Optional[Dict[str, Any]] = None) -> List[Dict[str, Any]]:
    async with db_connection() as connection:
        result = parse_record_ids(await connection.query(query_str, vars))
        if isinstance(result, str):
            raise RuntimeError(result)
        return result

Summary

  • Open-notebook organizes code into three distinct tiers: React/Next.js frontend, FastAPI backend, and SurrealDB database.
  • Communication flows are strictly defined: HTTP/REST between frontend and API, SurrealQL between API and database, and Esperanto for AI provider abstraction.
  • Repository pattern in open_notebook/database/repository.py abstracts database complexity, handling connection management and RecordID conversion.
  • Async job processing uses Surreal-Commands to prevent blocking API requests during long-running tasks like podcast generation.
  • Type safety flows through the stack via Pydantic models (Python) and TypeScript interfaces (frontend).

Frequently Asked Questions

What technologies comprise each tier of open-notebook?

The frontend uses React/Next.js with TypeScript, Zustand for state, and TanStack Query for data fetching. The API layer runs FastAPI on Python 3.11+, handling REST endpoints, LangGraph workflows, and AI provider selection through the Esperanto library. The database tier uses SurrealDB, a graph database storing structured data and vector embeddings.

How does the frontend communicate with the API?

The frontend calls the API through an Axios client configured in frontend/src/lib/api/client.ts, targeting http://localhost:5055 by default. Domain-specific modules like frontend/src/lib/api/notebooks.ts encapsulate REST operations, sending HTTP requests that FastAPI routers process and returning typed JSON responses validated by Pydantic schemas.

How does the API layer query the SurrealDB database?

The API uses a repository abstraction in open_notebook/database/repository.py to execute SurrealQL queries. Routers construct graph-aware queries (e.g., counting inbound relationships with count(<-reference.in)) and pass them to repo_query, which handles async connections, authentication, and RecordID string conversion, returning plain Python dictionaries.

How does open-notebook handle switching between AI providers?

The API uses the Esperanto library to abstract provider-specific implementations. LangGraph workflows request models through a unified ModelManager interface, which routes to OpenAI, Anthropic, or other supported providers without requiring changes to the workflow logic in files like open_notebook/graphs/ask.py.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →