Graphden Tutorial
Step-by-step introduction to graphden: text lessons, most of them paired with a guided in-editor tour (the ▶ column below).
Audience: someone who can program but has never seen graphden before. Each lesson assumes the previous ones and nothing else from the project docs.
How to read this
- Read lessons in order on first pass.
- Each lesson has a Goal (what you'll be able to do at the end), Concepts (vocabulary introduced), and Try it (something concrete to type into the running editor).
- If a concept needs deeper detail than the lesson gives, the lesson links into ARCHITECTURE.md, PACKAGES.md, or wherever it lives.
Lessons
Read them top to bottom — the table is in teaching order, in the same
five chapters the in-editor picker uses, and the numbering is
sequential in that order: the number is the file name, the deep link
(?tutorial=16), and the tour's :id. Inserting a lesson mid-sequence
renumbers everything after it — that is a mechanical, repo-wide
search-and-replace (ids appear only in file names, links, tour :ids and
prose references), so keep the numbers honest rather than appending out
of order.
Basics
| # | Lesson | Status |
|---|---|---|
| 01 | Anatomy of a fn-def | ✅ written · ▶ interactive |
| 02 | Reading a card — rows, unfolding, closed cards, descriptions | ✅ written · ▶ interactive |
| 03 | Parents and inheritance — single parent, then multiple | ✅ written · ▶ interactive |
| 04 | Slots and bindings — what they are at the data level | ✅ written · ▶ interactive |
| 05 | Free arguments and how they propagate | ✅ written · ▶ interactive |
| 06 | Lists — seed, append, close | ✅ written · ▶ interactive |
| 07 | Optional, required and sealed — what a slot decides beside its value | ✅ written · ▶ interactive |
| 08 | Types — atomic, refinement, record, union, variant, list | ✅ written · ▶ interactive |
Composing
| # | Lesson | Status |
|---|---|---|
| 09 | Higher-order functions and :fn-typed slots |
✅ written · ▶ interactive |
| 10 | Composing pages from components | ✅ written · ▶ interactive |
| 11 | The :custom-script escape hatch |
✅ written · ▶ interactive |
| 12 | State — cells, swap, and a graph-native cache | ✅ written · ▶ interactive |
| 13 | Recursion: loops without cycles | ✅ written · ▶ interactive |
| 14 | Live fragments: htmx from the graph | ✅ interactive + written |
Running it
| # | Lesson | Status |
|---|---|---|
| 15 | Executing a fn — free-arg form, history, cancel | ✅ written · ▶ interactive |
| 16 | Effects and the :secret type-marker |
✅ written · ▶ interactive |
| 17 | Tests — the tests namespace |
✅ written · ▶ interactive |
| 18 | Debugging: traces, the call tree, and catching a request | ✅ written · ▶ interactive |
| 19 | When something breaks, and when it just repeats: the problem filters | ✅ written · ▶ interactive |
The editor
| # | Lesson | Status |
|---|---|---|
| 20 | Finding your way: kind filters and the Inspector | ✅ written · ▶ interactive |
| 21 | Working without the mouse — keyboard & accessibility | ✅ written · ▶ interactive |
| 22 | Filters and views — look at the part of the graph you mean | ✅ written · ▶ interactive |
| 23 | Branches — fork, edit, diff, merge | ✅ written · ▶ interactive |
| 24 | Review — propose, approve, protected merge | ✅ written · ▶ interactive |
| 25 | Editing the editor: asset overrides | ✅ written · ▶ interactive |
| 26 | Version history: what changed, and going back | ✅ written · ▶ interactive |
Your organization
▶ interactive — the lesson also exists as a guided in-editor tour:
open the editor with ?tutorial=NN (the landing demo link does this for
Lesson 01), or pick “Interactive tutorial” in the account-chip menu —
in an organization workspace the lesson runs on its own tutorial-NN-*
branch, and ending it offers branch deletion = full rollback. The tour's
step scripts live in the graph (app.tour/_tour-lessons) and are
drift-guarded by tools/browser-test/edit-tutorial-tour*.test.js —
keep the written lesson's “Try it” section and the tour steps in
sync when either changes.
The catalogue remembers what you have read (in your browser, not in the
graph) and marks it ✓ done. That mark is yours: the ↺ beside a
finished lesson takes it back off, and Clear progress in the
catalogue's footer clears the whole history at once, behind a
confirmation. Lessons have editions (:version in the tour script,
bumped when a lesson's flow changes): a lesson you finished that has
since changed is chipped updated until you take it again, a lesson
that was not in the catalogue the last time you opened it is chipped
new, and the account-chip menu counts both on its “Interactive
tutorial” row. The catalogue's header counts what you have done, what
this session can run, and how many lessons there are. Finishing a lesson offers what to read next right there —
the lesson that follows, plus the first one you have not read when that
is a different lesson — and starting it from the dialog cleans the
finished lesson up first (deleting its branch, or the rows it created).
If a tab closes or reloads before cleanup finishes, its lesson and creation ledger remain in this browser. Open Interactive tutorial in the account menu to continue the saved step or choose End lesson & clean up. Returning on another branch leaves the lesson paused; recovery switches back to its original context before cleanup. Reloading a cleanup dialog restores that decision, including a failed deletion that can be retried. Choosing another lesson first asks whether to delete or keep the pending lesson's work. Keep branch and Keep & close end the tracked session and retain the data; later lessons do not claim a retained branch for rollback. Sessions saved by an older editor use their recorded items for cleanup; their legacy branch name alone cannot authorize a full branch rollback. Cleanup pauses when the original account or organization cannot be verified, or the original branch is unavailable. Cancel retains the saved session; Keep & close ends tracking without deleting graph data. Inspect any remaining branch from the branch menu.
Every tour is the SHOWN half of its lesson; the written lesson is the
whole of it — the model behind the steps, the branches of the topic the
tour does not walk (cardinality and per-branch services in 35, closure
capture in 09, …), the same thing as fn-defs, and a "what we glossed
over" list. The tour says so at the hand-over: its last step links to
the written lesson and says in one line what the text adds, and the
catalogue repeats that line under every lesson you have marked ✓ done
(the ↗ on any row opens the text). The two lessons without a tour are
listed in the catalogue too, as text only rows that open the text —
so the catalogue is the whole tutorial, not just the part the editor can
walk. The link target is https://graphden.dev/tutorial/ plus the
lesson's file name; a deployment serving its own copy of the lessons (a
translation, an intranet mirror) sets GRAPHDEN_TUTORIAL_BASE to it.
Two lessons have no tour: 31 works with package files on disk and startup sync; 34 requires a second instance and external deployment tools. 14 practices live fragment updates through an isolated HTML handler preview; 39 practices queue retries and recovery when persistent services are available. 41 practices author and consumer on two owned branches; 42 practices account token creation, scopes and revocation, while external registry and MCP client setup remain in their written lessons. Both text-only lessons sit in the chapter their subject belongs to, which is why the ▶ column is worth reading.
The organization tours declare what they need (:requires): a capability such
as manage-users or publish-packages, or a named deployment condition. Lessons
35 and 38 require an authenticated temporary HTTP host; ordinary cloud accounts
can publish finite handlers without a dedicated executor or service quota.
Lesson 14 also needs an account browser session, an isolated apps domain and
manage-apps access: it creates its own app host and removes that exact route
before its lesson graph and branch. Lesson 30 needs those same prerequisites;
it creates an Apps route, serves typed HTML through the branch preview, and
explains ordinary publication after a reviewed merge into main.
Persistent services still require the existing self-hosted/dedicated capability.
The cross-org tour needs organizations, and asset overrides need a single-tenant
instance. A missing condition leaves the lesson visible but disabled with a
reason on its row.
Lesson 25 edits personal UI graphs on both cloud and self-hosted installations
(:requires "personal-ui-graphs"). A protected ordinary resolver finds the
current profile's editor graph or shared defaults. Personalize creates inherited
role children; the lesson binds the menu view's free theme input while keeping
shared helpers and unrelated components. The full bundle remains an advanced
compatibility tool. Creating and editing graphs still requires ordinary permissions.
The separate Source files panel edits shared frontend assets and remains
self-hosted/platform-only because those writes affect every user of the instance.
New lessons are added as features ship. If a lesson would document a feature that doesn't yet exist or behaves differently from how it's described, it stays ⏳ planned until the gap closes.
Earlier end-to-end worked example
Once you've worked through the Basics and Composing chapters (lessons 01–14) plus Services (lesson 35), Building an API-poller shows an earlier composition: a scheduled service that calls an external HTTP API with a vault-backed bearer token and writes each result into your own Postgres table — built entirely from fn-defs, no Clojure. It needs alignment with the current secret-flow contract before it is a working end-to-end recipe; read its status note first.
How to contribute a lesson
A lesson is a short focused walkthrough of one concept. Keep:
- ~60-150 lines of markdown total
- One concrete worked example you can paste into the running editor
- Concepts introduced explicitly named (so future lessons can refer)
- No prerequisites beyond the previous lessons in this index
Avoid:
- Re-explaining what earlier lessons already covered (link to them)
- Duplicating reference material that lives in
docs/*.md(link) - "Why we did it this way" rationale — that belongs in PHILOSOPHY.md and gets linked, not pasted