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

Open Notebook implements a classic three-tier architecture that cleanly separates the React frontend, FastAPI backend, and SurrealDB database into distinct layers communicating over HTTP and SurrealQL protocols.

Open Notebook (lfnovo/open-notebook) is structured as a modular, async-first system designed for local-first AI notebook management. The three-tier architecture separates concerns between presentation, business logic, and data persistence while maintaining high performance through asynchronous operations and native vector search capabilities.

Architecture Overview

The system divides responsibilities across three distinct runtime layers:

  • Frontend (Port 3000): React/Next.js application handling all UI interactions
  • API (Port 5055): FastAPI service orchestrating LangGraph workflows and AI provider management
  • Database (Port 8000): SurrealDB instance storing graph relationships and vector embeddings

Data flows unidirectionally from the browser through REST endpoints to the database, with the API layer mediating all AI operations through the Esperanto library abstraction.

Tier 1: Frontend Layer (React and Next.js)

The presentation tier runs as a Next.js application on port 3000, providing the notebook interface, source upload widgets, chat interfaces, search functionality, and podcast generation controls.

State Management and Data Fetching

The frontend uses Zustand for global state management and TanStack Query for server state synchronization. All UI actions invoke the backend through typed REST API calls rather than direct database connections, enforcing strict separation of concerns.

UI Components and Styling

Interface components leverage shadcn/ui primitives styled with Tailwind CSS, providing a consistent design system across notebook editing, source management, and chat interfaces. The component architecture in frontend/src/pages/index.tsx demonstrates this pattern by combining Zustand stores with TanStack Query hooks to fetch notebook data.

Tier 2: API Layer (FastAPI and LangGraph)

The application tier on port 5055 implements the core business logic through FastAPI, handling authentication, workflow orchestration, and provider-agnostic AI operations.

REST Endpoints and Business Logic

The API exposes structured endpoints for notebooks, sources, notes, and chat sessions. The main application entry point in api/main.py initializes CORS policies and registers routers including api/routers/notebook.py, which implements CRUD operations for notebook entities.

LangGraph Workflow Orchestration

Long-running AI processes execute as LangGraph state machines defined in open_notebook/graphs/*. These workflows handle:

  • Source ingestion and text extraction pipelines
  • RAG chat completion chains
  • Podcast generation and audio rendering jobs

The graph architecture enables checkpointing and recovery of multi-step AI operations.

AI Provider Management

The ModelManager class (via the Esperanto library) in open_notebook/ai/model_manager.py abstracts LLM calls across OpenAI, Anthropic, Ollama, and other providers. This abstraction allows users to configure different models for different tasks without changing application code.

A job queue schedules background tasks like podcast rendering, preventing HTTP timeout issues during long AI generation cycles.

Tier 3: Database Layer (SurrealDB)

The data persistence tier runs SurrealDB on port 8000, storing the graph structure of notebooks, sources, notes, and chat sessions.

SurrealDB's native vector support stores embeddings generated during source ingestion, enabling fast similarity search for RAG queries. When a user uploads a document, the API extracts text, generates embeddings via the configured AI provider, and persists both content and vectors in the database.

Async Connection and Migrations

The connection layer in open_notebook/database/manager.py provides an async wrapper around SurrealDB operations. This module handles connection pooling, SurrealQL query execution, and automatic schema migrations at startup, ensuring the database schema remains synchronized with application requirements.

Data Flow and Communication Protocols

Communication follows distinct protocols between tiers:


[Browser] → HTTP/REST (Port 3000→5055) → FastAPI → SurrealQL (Port 5055→8000) → SurrealDB

The frontend communicates exclusively via REST. The API layer translates these requests into SurrealQL queries using an async driver. This protocol separation prevents direct database exposure while enabling complex graph traversals and vector similarity calculations.

When processing a chat request, the flow executes as: API receives HTTP POST → queries SurrealDB for relevant source embeddings via vector search → passes context to LLM through ModelManager → returns generated response to frontend.

Implementation Examples

Creating a Notebook via REST API

import httpx

BASE = "http://localhost:5055"

async def create_notebook(name: str):
    async with httpx.AsyncClient() as client:
        resp = await client.post(
            f"{BASE}/notebooks",
            json={"title": name}
        )
        resp.raise_for_status()
        return resp.json()

Querying SurrealDB Directly

from surrealdb import Surreal

db = Surreal("ws://localhost:8000")
await db.signin({"user": "root", "pass": "root"})
await db.use("open_notebook", "my_namespace")

async def get_notes(notebook_id: str):
    query = f"SELECT * FROM note WHERE notebook = '{notebook_id}'"
    result = await db.query(query)
    return result[0]["result"]

Executing LangGraph Workflows

from open_notebook.graphs.podcast import podcast_graph

async def generate_podcast(source_id: str):
    state = {"source_id": source_id}
    result = await podcast_graph.ainvoke(state)
    return result["podcast_url"]

Key Source Files and Modules

Understanding the architecture requires familiarity with these specific implementation files:

Summary

  • Open Notebook separates concerns into Frontend (React/Next.js), API (FastAPI), and Database (SurrealDB) tiers running on ports 3000, 5055, and 8000 respectively
  • The frontend uses Zustand and TanStack Query to communicate via REST, never touching the database directly
  • LangGraph workflows in open_notebook/graphs/* orchestrate complex AI operations through state machines
  • SurrealDB provides graph relationships and native vector storage for semantic search capabilities
  • The ModelManager abstraction in open_notebook/ai/model_manager.py enables provider-agnostic LLM operations
  • All database interactions occur through the async wrapper in open_notebook/database/manager.py, which handles migrations automatically

Frequently Asked Questions

How does the frontend communicate with the database?

The frontend never communicates directly with the database. Instead, it makes HTTP REST calls to the FastAPI backend on port 5055, which then translates requests into SurrealQL queries. This strict separation prevents exposing database credentials to the browser and allows the API layer to implement authentication and business logic.

What AI providers does Open Notebook support?

According to the source code in open_notebook/ai/model_manager.py, the system supports multiple providers including OpenAI, Anthropic, and Ollama through the Esperanto abstraction library. Users configure their preferred providers and models via environment variables defined in CONFIGURATION.md, allowing different models for different tasks without code changes.

How does the vector search implementation work?

SurrealDB's built-in vector storage holds embeddings generated when sources are ingested. During a chat session, the API performs a vector similarity search in SurrealDB to retrieve relevant source content, then passes this context to the configured LLM through the ModelManager. This RAG (Retrieval-Augmented Generation) pattern enables grounded responses based on uploaded documents.

Where are the LangGraph workflows defined?

LangGraph state machines for ingestion, chat, and podcast generation reside in open_notebook/graphs/*. These Python modules define the node graphs that orchestrate multi-step AI processes, with podcast_graph.ainvoke() demonstrating the async invocation pattern for long-running generation tasks.

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 →