DatoCMS plugin that draws the records of a project and the links between them as an interactive graph. It adds a Grafo tab to the main navigation. Each node is a record, and clicking a node shows its incoming and outgoing relations and opens the record in the editor.
Website: <https://spleenteo.github.io/grafo-datocms-plugin/>

Demo video: assets/preview.mp4
link and links fields, from Structured Text (itemLink, inlineItem, blocks and inline blocks), from Modular Content and from single-block fields, following nested blocks to any depth.parent_id appears as a pseudo field named parent (tree).From the DatoCMS marketplace: Configuration → Plugins → Add new → search "Grafo" → Install.
When you install, grant the currentUserAccessToken permission (see below). Without it the page shows a message and loads nothing.

Open the settings from Configuration → Plugins → Grafo. Only roles that can edit the schema can save them; other roles see them read-only.
| Setting | Parameter | Default | Effect |
|---|---|---|---|
| Excluded models | excludedModelApiKeys: string[] | ["schema_migration"] | These models are never requested from the API and never shown. The default covers the model DatoCMS creates when you use migrations. Saving an empty list excludes nothing. |
| Starting models | startingModelApiKeys: string[] | [] (every model) | The models selected when Grafo opens, with "Relations outside selection" on, so their linked records appear as context. Reset returns to them. |
Both lists store model API keys, so they carry over to sandbox environments. A model that is renamed or deleted drops out of the list the next time you save.
Grafo reads data through the Content Management API using ctx.currentUserAccessToken. Every request runs with the permissions of the logged-in user, so each role sees only what it is allowed to read:
ctx.currentRole.meta.final_permissions, so it doesn't need an extra request. Models the role can't read never reach the graph, the rail, the counts or the inspector.The plugin makes no write calls.
| Role | Requests |
|---|---|
| Full read | 1 + max(1, ceil(records / 500)) + max(1, ceil(blocks / 500)) |
| Restricted | 1 + max(1, ceil(records / 30)) |
Each listing is repeated per chunk of 200 models, with at least one request per chunk. The count shown in the status bar is API calls, rate-limit retries included.
The first request fetches the schema: site with include=item_types,item_types.fields. The results are memoised for the page session, keyed by environment, endpoint, user, effective permissions and excluded models. Reload bypasses the memo. The schema, meaning model and field names, is always downloaded.
The editor's filters are saved in localStorage under grafo:filters:v1:<siteId>:<environment>:<userType>:<userId>. Filters are stored by model and field API key, with switched-off models and fields stored as an exclusion list, so models and fields added later show up switched on. When an admin changes the starting models, the stored filters are ignored once. The key is removed when the filters match the starting ones. Grafo works normally when storage is blocked; it just doesn't remember anything.

MIT