Your first diagram
We will draw how a photo is uploaded: a browser, an API, object storage, a queue, a worker that makes thumbnails and a database. Each step adds one idea to the same file, and the code blocks mark the lines it adds. The files on this page are real: the diagrams are compiled from them when the site is built.
Create the diagram first:
pnpm docspp new photosThen replace the contents of diagrams/photos/diagram.yaml as you follow along.
1. Name the parts and connect them
Section titled “1. Name the parts and connect them”A node is a box. Give it a kind, which picks a colour and a default icon, and an icon when you want a brand logo. The key (browser, api) is its id. You use ids in edges, groups and scenarios.
An edge is an arrow. a -> b: label is the short form. Use the object form when you need more, such as a kind that changes how the line is drawn.
title: Photo uploads
nodes: browser: { kind: client, icon: react } api: { kind: service, icon: nodejs, title: Upload API } store: { kind: storage, icon: aws-s3, title: Storage } queue: { kind: queue, icon: rabbitmq, title: Events } worker: { kind: worker, icon: python, title: Thumbnailer } db: { kind: database, icon: postgresql, title: Metadata }
edges: - browser -> api: POST /photos - api -> store: { kind: data, label: PUT } - api -> db: { kind: data, label: SQL } - api -> queue: { kind: queue, label: photo.uploaded } - queue -> worker: { kind: queue } - worker -> store: { kind: data, label: PUT } - worker -> db: { kind: data }
views: overview: { direction: DOWN }Nothing here says where a box goes. Hover a box to follow its connections, or click one to open its drawer.
The views: block at the end asks for a top-to-bottom reading direction, which suits a narrow column. Views explains it.
2. Group related parts into a zone
Section titled “2. Group related parts into a zone”A group is a labelled zone. Add one line to define it and in: on each node that belongs to it.
title: Photo uploads
groups: app: { label: Photo service, caption: Kubernetes }
nodes: browser: { kind: client, icon: react } api: { kind: service, icon: nodejs, title: Upload API, in: app } store: { kind: storage, icon: aws-s3, title: Storage, in: app } queue: { kind: queue, icon: rabbitmq, title: Events, in: app } worker: { kind: worker, icon: python, title: Thumbnailer, in: app } db: { kind: database, icon: postgresql, title: Metadata, in: app }
edges: - browser -> api: POST /photos - api -> store: { kind: data, label: PUT } - api -> db: { kind: data, label: SQL } - api -> queue: { kind: queue, label: photo.uploaded } - queue -> worker: { kind: queue } - worker -> store: { kind: data, label: PUT } - worker -> db: { kind: data }
views: overview: { direction: DOWN }Each zone is laid out on its own, so the diagram stays tidy as it grows. Groups can nest: a group with in: is drawn as a dashed frame inside its parent. See groups.
3. Tell a story
Section titled “3. Tell a story”A scenario is a list of steps. Each step flies a packet along your edges.
title: Photo uploads
groups: app: { label: Photo service, caption: Kubernetes }
nodes: browser: { kind: client, icon: react } api: { kind: service, icon: nodejs, title: Upload API, in: app } store: { kind: storage, icon: aws-s3, title: Storage, in: app } queue: { kind: queue, icon: rabbitmq, title: Events, in: app } worker: { kind: worker, icon: python, title: Thumbnailer, in: app } db: { kind: database, icon: postgresql, title: Metadata, in: app }
edges: - browser -> api: POST /photos - api -> store: { kind: data, label: PUT } - api -> db: { kind: data, label: SQL } - api -> queue: { kind: queue, label: photo.uploaded } - queue -> worker: { kind: queue } - worker -> store: { kind: data, label: PUT } - worker -> db: { kind: data }
views: overview: { direction: DOWN }
scenarios: upload: title: Upload a photo steps: - browser -> api: POST /photos - api -> store: { label: PUT original, kind: lookup } - api -> queue: { label: photo.uploaded, kind: event } - api -> browser: 202 Accepted - queue -> worker: photo.uploaded - worker -> store: PUT thumbnails - worker -> db: { label: mark ready, kind: lookup }A few things to notice while it plays:
browser -> apifollows the edge you declared.api -> browserflies against the edge, so docspp draws it as a response without being told.kind: lookupmarks a quick read of a store.kind: eventmarks something published for others to react to.- Click any line in the step list to jump to it. The address bar follows the step, so you can link a colleague to it.
Scenarios covers steps, phases, notes and the other ways to read the same story.
4. Show a slice, and add a second story
Section titled “4. Show a slice, and add a second story”A view shows part of the model. The new processing view keeps four boxes, and a scenario with view: processing plays on it.
title: Photo uploads
groups: app: { label: Photo service, caption: Kubernetes }
nodes: browser: { kind: client, icon: react } api: { kind: service, icon: nodejs, title: Upload API, in: app } store: { kind: storage, icon: aws-s3, title: Storage, in: app } queue: { kind: queue, icon: rabbitmq, title: Events, in: app } worker: { kind: worker, icon: python, title: Thumbnailer, in: app } db: { kind: database, icon: postgresql, title: Metadata, in: app }
edges: - browser -> api: POST /photos - api -> store: { kind: data, label: PUT } - api -> db: { kind: data, label: SQL } - api -> queue: { kind: queue, label: photo.uploaded } - queue -> worker: { kind: queue } - worker -> store: { kind: data, label: PUT } - worker -> db: { kind: data }
views: overview: { title: Everything, direction: DOWN } processing: title: Processing only include: [queue, worker, store, db]
scenarios: upload: title: Upload a photo steps: - browser -> api: POST /photos - api -> store: { label: PUT original, kind: lookup } - api -> queue: { label: photo.uploaded, kind: event } - api -> browser: 202 Accepted - queue -> worker: photo.uploaded - worker -> store: PUT thumbnails - worker -> db: { label: mark ready, kind: lookup } thumbnails: title: Make thumbnails view: processing steps: - queue -> worker: photo.uploaded - worker -> store: { label: GET original, kind: lookup } - worker -> store: PUT 3 sizes - worker -> db: { label: mark ready, kind: lookup }Use the tabs above the diagram to switch back to the full picture. Nothing was drawn twice: both views come from the same six nodes.
5. Document a box
Section titled “5. Document a box”Create diagrams/photos/nodes/api.md:
The only service that talks to the browser.
| Route | What it does ||---|---|| `POST /photos` | Stores the original and queues thumbnailing. |
It never resizes images itself. See the Thumbnailer.Click Upload API in the diagram and the drawer shows this, followed by the box’s connections. Node docs lists the other ways to document a node.
6. Check it and put it on a page
Section titled “6. Check it and put it on a page”pnpm checkThen add it to a page in src/content/docs/:
import Diagram from '@packagelab/docspp-astro/Diagram.astro'
<Diagram name="photos" scenario="upload" />Where next
Section titled “Where next”- Add
status: plannedto a node that does not exist yet. It is drawn dashed, and readers get a Hide planned toggle. - Write the failure as its own scenario, such as an upload that is too large.
- Read the format reference for everything a node, group, edge and view accepts.