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:
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:
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:
Access the Graph View in Your Browser
- Open your browser and navigate to: http://localhost:8000/zettelkasten/graph
๐งช 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.
Normalized Wikilink Resolution
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: