Drupal knows what a node references directly, but not what those references reference in turn. Content Graph keeps a projection of every content entity on your site and every reference between them. It's updated as part of each save.
That lets you ask what a page depends on and get the complete answer: paragraphs inside paragraphs, media and their files, terms, blocks, Layout Builder, Canvas and Site Studio components, all the way down.
This is possible from adding Common Table Expression (CTE) support. Drupal's database API can't express a recursive query: there's no WITH RECURSIVE in core's query builder. The module adds a small builder of its own, CommonTableExpressionQuery that composes the query from three parts:
- An anchor: the root entities.
- A recursive step: join the edge table to the entities reached so far, one hop deeper, up to a depth limit, following only default-revision edges.
- An outer SELECT: dedupe what was reached and keep each entity's shortest depth.
Values are bound, never interpolated. It runs on every database Drupal 11 supports: MySQL 8, MariaDB, PostgreSQL and SQLite.
WITH RECURSIVE reachable (entity_type, uuid, depth) AS (
SELECT ... FROM content_graph_entity WHERE <roots> -- anchor
UNION ALL
SELECT child... FROM content_graph_edge JOIN reachable ... -- one hop deeper
WHERE depth < :max_depth AND <default revision edge>
)
SELECT entity_type, uuid, MIN(depth) FROM reachable GROUP BY entity_type, uuid
They back bulk exports, which can cover the whole site. For one or a few roots, the PHP walk does the same traversal and also records the edges, the missing targets and any cycles it hits.
Why this works:
- One query, any depth. "Everything this depends on, however deep" is a single round trip to the database. Without the CTE, you load an entity, read its reference fields, load those, and repeat. That's what Entity Share does over HTTP and what custom code does with entity loads.
- Every kind of reference in one place. Plain entity references, revision references, files, links, rich text embeds, Layout Builder inline blocks and Canvas inputs all become rows in one edge table. The query walks one table. It doesn't need a resolver for each field type.
- Reverse lookups for free. The same edges walked backwards answer "what uses this?". Live Drupal can only answer that by querying every field that might point at the entity.
- No extra infrastructure. Neo4j or ArangoDB would give you graph traversal, but as a separate service to run and keep in sync. The CTE gets the core traversal out of the database you already have. The mirrors stay optional, for when you want a real graph engine.
- Exact by construction. The index is updated during each save, and the query follows only current-revision edges. So the answer matches the site's published state, not a stale export or a guess.
One practical use case is included with this module - pushing content over the wire to another environment. Pick a content entity, like node, and Content Graph packages the current published version of it and everything it depends on into a single zip. Another environment can validate, preflight, dry-run and apply that zip in one transaction.
Those features are very experimental and can have edge cases. But on a handful of real projects, the module has successfully pushed entity changes, including data in Layout Builder, Paragraphs, Site Studio, and Canvas. This type of functionality has long been asked of Drupal.
Features
- Full dependency closure of any entity, through entity references, revision references, media, files, rich text embeds, links, Layout Builder, Paragraphs, Canvas and Site Studio.
- Promotion packages: checksummed zips with content-addressed file bytes. Import them with Drush or the admin UI, with a preflight that stops early on missing modules, bundles or fields.
- Safe imports: a dry-run plan per entity, conflict detection when the target has changed since the last import, and an all-or-nothing apply.
- Authorship is preserved. Owners and user references travel by UUID, so "edit own content" still works for the right person on the target. Unknown accounts arrive as blocked stubs. Passwords, email addresses and roles are never packaged.
- Access-aware. The sender must be able to view everything a package would carry.
- Deletions are promoted as Trash state, recoverable, not destructive.
- A page-builder inventory of Layout Builder, Paragraphs, Site Studio and Canvas usage.
- Optional mirrors of the graph to Neo4j or ArangoDB.
- Experimental: pushing packages to a remote site from the UI, with a dispatch log and retries, and a read-only JSON API over the graph.
Post-installation
Run drush content-graph:rebuild once to index existing content. After that, every save keeps the graph current automatically. Export with drush cgxp node:UUID --output=/tmp/package.zip and import on the target with drush cgip (dry run) and drush cgia (apply), or use the admin UI under Content.
Additional requirements
Drupal 11.2 or later, or Drupal 12. The main module needs nothing beyond core.
Integrations are separate submodules, each needing its own contrib module or library.
Recommended modules
- Paragraphs, Canvas and Site Studio, for their integrations.
- Trash, for deletion promotion and the remote modules.
- Key, for storing remote destination passwords.
- Views Bulk Operations, for packaging from a view.
Similar projects
- Migrate (core): for moving content between different schemas, upgrades and external sources, with transformation and rollback. Content Graph assumes both sites share configuration and moves content as is.
- Entity Share: for ongoing, pull-based content sharing between independent sites over JSON:API. It finds references during the import, one request at a time, and doesn't carry authorship.
- Workspaces (core): for staging content changes within one site, not between environments.
Ask your assistant about Content Graph
Check compatibility with your Drupal and PHP version, maintenance and security coverage, from current release data. How it works
Depends on
Dependencies of the latest stable release
No dependencies recorded for this project.
Required by
Tracked projects that depend on this one
No tracked projects depend on this one yet.
Activity
Releases
| Version | Type | Core | PHP | Release date | |
|---|---|---|---|---|---|
| 1.0.x-dev | Dev | 11 | Oct 16, 2026 |