Skip to content

How to Build a Zettelkasten Graph View in FastAPI

This guide explains how to compile a local Markdown slip-box (Zettelkasten) into a unified network graph and serve it inside a FastAPI application using D3.js and HTML5 Canvas.


Follow these steps to parse Zettelkasten files, compile them into static data, and serve the interactive graph page.

Configure Your Environment Variable

Ensure your local path to the Zettelkasten notes folder is configured inside your project's .env configuration file:

  • Add the following line to fastapi-app/.env:
ZETTELKASTEN_DIR=/Users/hemroda/DevSecMLOps/zettelkasten

Compile the Graph Data

To prevent your web server from having to scan files on every page request in production, we compile the Zettelkasten graph data locally.

  • Run the build command from the project root:
make zettelkasten-build
Click to view the zettelkasten_graph.py build script execution

Running this command invokes the build script which recursively scans all .md files in your configured directory:

โฏ make zettelkasten-build
cd fastapi-app && uv run python config/zettelkasten_graph.py
๐Ÿ” Scanning Zettelkasten notes at: /Users/hemroda/DevSecMLOps/zettelkasten ...
๐Ÿ’พ Saving graph data (353 nodes, 491 links) to:
   /Users/hemroda/DevSecMLOps/crud-in-the-cloud/fastapi-app/data/zettelkasten/graph-data.json
โœ… Zettelkasten graph data build complete!

Run the Development Servers

Start your Tmux development session or launch the FastAPI server directly:

  • Launch the server:
make dev
# Or run just FastAPI
make fastapi-dev

Access the Graph View in Your Browser


๐Ÿงช Architecture & Implementation Details

Local Decoupled Parsing

The build script uses a standard regex-based YAML parser inside ZettelkastenService to read metadata. It matches and resolves standard Markdown links [link](note.md) as well as double-bracket wikilinks [[Note Title]] by normalizing file stems.

To ensure links like [[Second Note]] correctly resolve to files named second_note.md, the parser normalizes all filename keys in its internal registry:

# Normalizes spaces, underscores, and dashes to a uniform format
normalized_name = file_path.stem.lower().replace(" ", "_").replace("-", "_")
name_to_id[normalized_name] = note_id

Warmup and Auto-Fit Canvas View

On initial load, nodes are pre-settled via synchronous pre-simulation iterations to avoid jarring flight animations. The canvas then automatically calculates the bounding coordinates of all nodes and scale-fits them:

// Warm up simulation synchronously to settle note layout instantly
for (let i = 0; i < 90; ++i) {
  simulation.tick();
}

// Fit and center view within the Canvas boundaries immediately
zoomToFit(60, 0);

Missing Directory Banner

If the application is deployed and the static build JSON file is missing, the web endpoint handles it gracefully by returning an error payload, prompting the frontend to display a warning overlay:

if (data.error) {
  document.getElementById("error-message").textContent = data.error;
  document.getElementById("error-overlay").classList.remove("hidden");
  return;
}

โš™๏ธ Testing the Zettelkasten Pipeline

To verify the parsing logic and endpoint serving, run the test suite:

  • Run the Pytest checks:
cd fastapi-app && uv run pytest tests/