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

> Explore Open Notebook's three-tier architecture featuring React frontend, FastAPI API, and SurrealDB. Understand the separation of layers and communication protocols.

- Repository: [Luis Novo/open-notebook](https://github.com/lfnovo/open-notebook)
- Tags: architecture
- Published: 2026-06-23

---

**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`](https://github.com/lfnovo/open-notebook/blob/main/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`](https://github.com/lfnovo/open-notebook/blob/main/api/main.py) initializes CORS policies and registers routers including [`api/routers/notebook.py`](https://github.com/lfnovo/open-notebook/blob/main/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`](https://github.com/lfnovo/open-notebook/blob/main/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.

### Vector Storage and Semantic Search

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`](https://github.com/lfnovo/open-notebook/blob/main/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

```python
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

```python
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

```python
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:

- **[`frontend/src/pages/index.tsx`](https://github.com/lfnovo/open-notebook/blob/main/frontend/src/pages/index.tsx)**: Main UI entry point demonstrating Zustand and TanStack Query integration
- **[`api/main.py`](https://github.com/lfnovo/open-notebook/blob/main/api/main.py)**: FastAPI application initialization and router registration
- **[`api/routers/notebook.py`](https://github.com/lfnovo/open-notebook/blob/main/api/routers/notebook.py)**: REST endpoint implementations for notebook CRUD operations
- **`open_notebook/graphs/*`**: LangGraph workflow definitions for ingestion, chat, and podcast generation
- **[`open_notebook/database/manager.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/database/manager.py)**: Async SurrealDB connection wrapper and migration handler
- **[`open_notebook/ai/model_manager.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/ai/model_manager.py)**: Esperanto-based AI provider selection and credential management
- **[`CONFIGURATION.md`](https://github.com/lfnovo/open-notebook/blob/main/CONFIGURATION.md)**: Environment variable reference for ports, database URLs, and provider API keys

## 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`](https://github.com/lfnovo/open-notebook/blob/main/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`](https://github.com/lfnovo/open-notebook/blob/main/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`](https://github.com/lfnovo/open-notebook/blob/main/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`](https://github.com/lfnovo/open-notebook/blob/main/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.