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.
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 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:
frontend/src/pages/index.tsx: Main UI entry point demonstrating Zustand and TanStack Query integrationapi/main.py: FastAPI application initialization and router registrationapi/routers/notebook.py: REST endpoint implementations for notebook CRUD operationsopen_notebook/graphs/*: LangGraph workflow definitions for ingestion, chat, and podcast generationopen_notebook/database/manager.py: Async SurrealDB connection wrapper and migration handleropen_notebook/ai/model_manager.py: Esperanto-based AI provider selection and credential managementCONFIGURATION.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.pyenables 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →