Community feature & backend
ZettelFlow lets users browse, preview, install and reuse shareable building blocks — actions, steps, markdown notes and whole flows. This page covers the frontend community module and the optional FastAPI/MongoDB backend.
1. Two data sources
The community browser has two independent sources, chosen at runtime by whether a token
is set in communitySettings:
| Source | Component | Backing service | Supports |
|---|---|---|---|
| Static (default) | StaticTemplatesGallery |
raw.githubusercontent.com/RafaelGB/Obsidian-ZettelFlow/.../main |
steps, actions, markdown, flows |
| Dynamic (token set) | CommunityTemplatesGallery |
the FastAPI backend at communitySettings.url |
steps, actions |
// CommunityTemplatesModal
if (token && token.length > 0) render(<CommunityTemplatesGallery/>); // backend API
else render(<StaticTemplatesGallery/>); // GitHub raw JSON
The
tokenonly toggles the source; it is not sent as anAuthorizationheader. The backend currently has no auth.
2. Frontend module — src/application/community/
Modals
CommunityTemplatesModal— shell; renders a navbar (with an "Add template" link to a GitHub issue form) and mounts the dynamic or static gallery.CommunityActionModal— preview of a single action (icon/label + rendered markdown description +settingsReader). Install togglessettings.installedTemplates.actions[id].CommunityStepModal— preview of a step (metadata + each action viasettingsReader); Install/Remove (with a confirm dialog) writesinstalledTemplates.steps[id]; "Manage" opensInstalledStepEditorModal.CommunityMarkdownModal— preview of a markdown template; Download writes the note intocommunitySettings.markdownTemplateFolder(Remove deletes it).CommunityFlowModal— the richest: previews a whole flow (fetchesimage.png), "Copy to clipboard" (stores the flow incommunitySettings.clipboardTemplate), "Download flow files" (writes referenced markdown into the vault and rewrites nodefilepaths), and renders the nodes (grouped by type) and edges.ManageInstalledTemplatesModal— manages installed templates; "Add template from clipboard" importsclipboardTemplateintoinstalledTemplates.UsedInstalledStepsModal— a picker (StepTemplatesSelector) used to drop an installed step onto a canvas node.
HTTP client — services/CommunityHttpClientService.ts
Targets only the GitHub-raw static source (uses Obsidian's request()):
COMMUNITY_BASE_URL = "https://raw.githubusercontent.com/RafaelGB/Obsidian-ZettelFlow/refs/heads/main";
| Function | URL | Returns |
|---|---|---|
fetchCommunityTemplates() |
${BASE}/docs/main_template.json |
StaticTemplateOptions[] |
fetchActionTemplate(ref) |
${BASE}${ref} |
CommunityAction |
fetchStepTemplate(ref) |
${BASE}${ref} |
CommunityStepSettings |
fetchFlowTemplate(ref) |
${BASE}${ref}/flow.json |
CommunityFlowData |
fetchMarkdownTemplate(ref) |
${BASE}${ref} |
raw markdown |
The dynamic gallery has its own inline fetchers (in CommunityTemplatesGallery.tsx) hitting
{url}/templates/filter, {url}/steps/{id}, {url}/actions/{id}. It renders cards with debounced
search, All/Step/Action filters, and infinite scroll (IntersectionObserver, page size 15).
How templates are imported
- Steps / Actions — "install" just serializes the object into
settings.installedTemplates.steps|actions[id]. No files are written; installed steps later become canvas nodes (node.unknownData.zettelflowConfig) or are edited viaInstalledStepEditorModal. - Markdown — written as a real note into
markdownTemplateFolder. - Flows — "Copy to clipboard" →
clipboardTemplate; "Download flow files" writes the markdown and rewrites node paths; the clipboard flow is imported viaManageInstalledTemplatesModal.
3. Backend — backend/
A FastAPI + MongoDB service in hexagonal / clean architecture. It is optional: the plugin works fully against the static GitHub source without it.
Layer map
backend/app/
├── main.py # composition root: FastAPI factory + dependency injection
├── domain/models/ # pure Pydantic models (CommunityAction, CommunityStepSettings, template_filter)
├── application/services/ # use-cases: action_service, step_service, template_service
├── infrastructure/
│ ├── db/mongodb.py # MongoDBClient (env-configured) + serialize helpers
│ └── repositories/ # action/step/template repositories (PyMongo)
└── interfaces/api/
├── controllers/ # get_*_router() factories — the ACTUAL routes
└── routers/ # ⚠ present but EMPTY (vestigial)
Dependency direction points inward: main builds one MongoDBClient → injected into three
repositories → injected into three services → injected into three router factories. All three
repositories read/write a single Mongo collection community_templates, discriminated by a
template_type field. TemplateRepository.read_templates builds a case-insensitive $regex
$or over title/description and paginates with a limit+1 look-ahead to compute has_next.
REST API
| Method | Path | Purpose | Response |
|---|---|---|---|
| POST | /steps/create |
create a step (unique title) | created doc (template_type:"step", downloads:0) — 400 if title exists |
| GET | /steps/{step_id} |
read a step | doc — 404/400 |
| DELETE | /steps/{step_id} |
delete a step | {deleted_count} |
| POST | /actions/create |
create an action (unique title) | created doc — 400 if exists |
| GET | /actions/{action_id} |
read an action | doc — 404/400 |
| GET | /templates/filter |
paginated + searchable list | { items:[6 fields], page_info:{skip,limit,has_next} } |
| GET | /templates/item/{item_id}?item_type= |
read by id + type | doc — 400 if bad type |
| DELETE | /templates/delete/{template_id} |
delete any template | {deleted_count} |
Every route declares response_model=Dict, so the domain response models
(template_filter.py) are not enforced at the transport layer. There is no CORS, no
auth, and no health route.
Contract drift (harmless): the frontend's
CommunityTemplatesResponseexpectstotal/has_previous, which the backend never returns — but the frontend only readshas_nextanditems.length.
Deployment — docker-compose.yml
Two services on one bridge network:
mongodb—mongo:6.0, port27017, named volumemongodb_data,mongoshhealthcheck, env from a root.env(MONGO_INITDB_ROOT_USERNAME/PASSWORD,MONGO_INITDB_DATABASE).fastapi— built frombackend/dockerfile(python:3.11-slim,uvicorn main:app --reload), port8000, source bind-mounted (./backend/app:/app) for hot-reload,depends_onmongodb healthy.
The app reads MONGODB_USERNAME/PASSWORD/HOST/PORT/DB (compose supplies these from the Mongo
credentials). requirements.txt pins FastAPI, uvicorn, pydantic v2, pymongo — and the whole
MkDocs docs toolchain. The --reload flag + bind mount make this a development posture; a
production deployment would drop --reload, add CORS/auth, and remove the docs deps.
4. End-to-end: browse → install (dynamic path)
- User sets
communitySettings.url+ atoken; opens Community Templates. - Token present →
CommunityTemplatesGallerycallsGET {url}/templates/filter?skip=0&limit=15. - Backend:
template_controller.filter_templates→TemplateService→TemplateRepositoryruns the Mongo regex+pagination query →{items, page_info}. - Scrolling triggers the
IntersectionObserver→skip += 15; debounced search re-queries. - Clicking a card →
GET {url}/steps/{id}or/actions/{id}→ opens the preview modal. - Install writes the object into
settings.installedTemplatesandsaveSettings(). - The installed step is reusable via
UsedInstalledStepsModal(dropped on a canvas node) orInstalledStepEditorModal(edited).