Hasura v3 is called DDN — Data Delivery Network — and the most useful thing to understand before touching it is that it is not v2 with new commands. It is a different product that happens to share a name and a query language. This lesson covers what it is, how to get one running, and exactly what changes coming from v2.
How this lesson was written. Everything below is taken from the official Hasura DDN documentation as read on 2026-08-21 — the feature availability matrix, the glossary and the quickstart. Unlike the v2 lessons in this track, it was not run locally, so it shows configuration and commands but never claims output.
The v3 mental model
Three words carry most of it.
A connector is what talks to a data source. In v2 the engine had built-in support for a fixed list of databases; in v3 every source — Postgres, a REST service, another GraphQL API, your own TypeScript functions — is reached through a native data connector implementing a common specification. Adding a new kind of source means writing or installing a connector rather than waiting for a Hasura release.
A subgraph is a self-contained slice of the API: its metadata, its connectors, its permissions, its own repository and release cycle. It is the unit of team ownership.
A supergraph is the composition of those subgraphs into one API. This is the architectural point of v3 — several teams shipping independently into a single graph, without the central bottleneck a single v2 engine becomes.
The other shift is builds. A v2 change is applied to a running engine and takes effect. A v3 change is compiled into an immutable build with its own unique endpoint, which you can test before promoting. Metadata stops being a live thing you mutate and becomes source you compile.
Getting a v3 project running
Install the CLI. Docker Compose v2.20 or later is required.
curl -L https://graphql-engine-cdn.hasura.io/ddn/cli/v4/get.sh | bash
ddn doctorScaffold a supergraph, attach a connector, and let it introspect the database:
ddn supergraph init mysupergraph
ddn connector init my_connector -i
ddn connector introspect my_connectorIntrospection writes what it found into the connector’s configuration; nothing is exposed in the API yet. In v2 you would now “track” tables in the console. In v3 you add models, and the equivalent of tracking everything is a wildcard:
ddn model add my_connector '*'
ddn command add my_connector '*'
ddn relationship add my_connector '*'Then build and run it locally:
ddn supergraph build local
ddn run docker-start
ddn console --localWhat you get is a project directory, not a running engine holding state:
supergraph.yaml
globals/ # AuthConfig, CompatibilityConfig, GraphqlConfig
app/
metadata/*.hml # your models, types, relationships, permissions
connector/my_connector/configuration.json
engine/build/ # immutable build outputThose .hml files are the product. HML is Hasura Metadata Language, an extension of
YAML, and it is what you edit and commit. A model looks like this:
kind: Model
version: v1
definition:
name: Properties
objectType: Properties
source:
dataConnectorName: my_connector
collection: properties
graphql:
selectUniques: []
selectMany:
queryRootField: propertiesThe console still exists but its job has changed: in v3 it is for testing queries, reading traces and analytics, and managing collaboration — not for authoring. Authoring is the CLI, the files, and a VS Code extension that validates them as you type.
v2 to v3, item by item
This is the table to keep open while you are working. Every row is from Hasura’s own documentation on 2026-08-21. Rows marked with a stripe are ones where v3 currently does less than v2.
Legend. (C) connector-dependent · WIP work in
progress · * supported but implemented differently · EE
Enterprise or Cloud only.
| Item | Hasura v2 | Hasura v3 (DDN) | What actually changes |
|---|---|---|---|
| Workflow | |||
| Where you configure it | The web Console — click to track a table, click to add a permission | .hml files in your editor, driven by the ddn CLI and a VS Code extension | Code-first. The DDN console is for testing, traces and analytics, not authoring. |
| Metadata format | A JSON/YAML tree, applied to a running engine over the metadata API | HML — 'an extension of YAML' — compiled into a build | Metadata stops being a live API you mutate and becomes source you compile. |
| Applying a change | Apply metadata; the running engine picks it up immediately | ddn supergraph build create produces an immutable build | Every build is immutable and gets its own unique GraphQL endpoint, so you can test one before it is anyone's production. |
| CLI | hasura | ddn | Different binary, different install, no shared commands. |
| Running it locally | docker run hasura/graphql-engine, then open the console | ddn supergraph init then ddn run docker-start | The CLI scaffolds a project directory; Docker Compose v2.20+ is required. |
| Open source | Community Edition is Apache-2.0 and the whole engine self-hosts | Engine and connectors are open source and self-hostable | ⚠️ The control plane is closed-source and commercial. The v3 split has no v2 counterpart. |
| Data sources | |||
| Connecting a database | Built-in support for a fixed set of sources, configured with a connection URL | A Native Data Connector (NDC) per source, from the Connector Hub | ddn connector init scaffolds it. Adding a new kind of source is writing a connector, not waiting for a Hasura release. |
| Exposing a table | Track the table | Add a Model (ddn model add) | The word changes and so does the mechanism: a Model is a metadata object you keep in git, not a row in the engine's state. |
| Several sources at once | Several sources on one engine | Subgraphs composed into a supergraph | A subgraph is a self-contained domain with its own permissions, SDLC and repository — the unit of team ownership. |
| Query push-down | Hasura compiles to SQL for sources it supports natively | The connector declares its capabilities and Hasura pushes down what it can | Including authorization push-down, which v2 could not do. |
| The GraphQL API | |||
| Queries | ✅ | ✅ | The generated schema is deliberately v2-compatible. |
| Mutations | ✅ generated insert/update/delete | ✅ (C) | Connector-dependent — a connector must implement them. |
| Native mutations | ❌ | ✅ | New in v3 — v2 had native queries but no native mutations. |
| Subscriptions | ✅ live queries and streaming | ✅, streaming WIP | Plain subscriptions work; streaming subscriptions are not finished. |
| Filtering | where on an auto-generated bool_exp | An explicit BooleanExpressionType | You declare which fields are filterable instead of getting all of them. |
| Sorting / aggregates | Auto-generated order_by, _aggregate | OrderByExpression and AggregateExpression (C) | Declared per model rather than generated wholesale. |
| Relationships | Object and array relationships, within one source | The Relationship kind | Can cross connectors and subgraphs, which v2's could not. |
| Field naming | HASURA_GRAPHQL_DEFAULT_NAMING_CONVENTION env var | GraphqlConfig in the globals subgraph | Moves from an env var to a metadata object. |
| REST | RESTified endpoints, built in | Via plugins; plus a first-class JSON:API | JSON:API has no v2 counterpart. |
| Business logic | |||
| Custom logic in the schema | Actions — declare types, point at an HTTP endpoint | Commands on a lambda connector | You write a TypeScript/Python/Go function in the project instead of hosting a webhook and describing it. |
| Existing GraphQL API | Remote schemas | The GraphQL connector | Same goal, different mechanism — it becomes a connector like any other. |
| On data change | Event triggers — webhook on insert/update/delete, with retries | WIP | ⚠️ Not yet available. Plan to keep this outside Hasura when moving to v3. |
| On a schedule | Cron / scheduled triggers | ❌ Not supported | ⚠️ No replacement. This one is simply gone. |
| Auth | |||
| Authentication | HASURA_GRAPHQL_JWT_SECRET or an auth webhook, set as env vars | AuthConfig, in the globals subgraph | Configured as metadata, and it is supergraph-level — one auth setup for every subgraph. |
| Admin access | HASURA_GRAPHQL_ADMIN_SECRET grants the unrestricted built-in admin role | ❌ There is no admin secret | ⚠️ The biggest surprise in this table. Every v2 habit built on the admin secret — seeding, scripts, the console — needs rethinking. API access uses a Cloud PAT (cloud_pat header). |
| Permissions | select / insert / update / delete permissions, per role, per table | TypePermissions (fields) + ModelPermissions (rows) + CommandPermissions | Split by what is being protected instead of by operation, and declared per subgraph rather than globally. |
| Production | |||
| Caching | ✅ @cached — Enterprise/Cloud only | ✅* via plugins | Both are paid surfaces, by different means. |
| Allow list | ✅ Enterprise/Cloud only | ✅* via plugins | |
| API limits / rate limiting | ✅ Enterprise/Cloud only | WIP | |
| Read replicas | ✅ Enterprise/Cloud only | ✅ | No longer gated behind Enterprise. |
| Database migrations | hasura migrate manages your SQL migrations | Not Hasura's job | ⚠️ Use your own migration tool, then ddn connector introspect to pick the change up. A real workflow change for anyone leaning on the v2 CLI. |
| CI/CD | hasura migrate apply + hasura metadata apply | ddn supergraph build create | Builds are immutable and independently testable, so promotion replaces in-place apply. |
| Hosting | Self-host the engine, or Hasura Cloud | DDN Cloud, or Private DDN | The data plane self-hosts; the control plane does not. |
What an existing v2 project has to become
Read down that table and the migration sorts into three piles.
Things that carry over as ideas but not as syntax. Roles, session variables,
row-level filters and column allowlists all still exist — they are just declared differently.
A v2 select permission becomes ModelPermissions for the rows plus
TypePermissions for the fields, split by what is being protected rather than by which
operation you were doing. Relationships become a Relationship object and gain the
ability to cross connectors, which v2 relationships could not do.
Things that change shape entirely. Actions become Commands backed by a lambda
connector, so instead of hosting a webhook and describing its types to Hasura, you write a function
in TypeScript, Python or Go inside the project. Remote schemas become the GraphQL connector. And
filtering stops being generated for every column: you declare a
BooleanExpressionType saying what is filterable.
Things that simply do not come with you. These are the ones worth deciding on before you start, not after.
The admin secret is gone
There is no HASURA_GRAPHQL_ADMIN_SECRET in DDN and no built-in unrestricted role
behind it. Every v2 habit built on that secret — seeding scripts, console access, migration
tooling, the “just use admin for now” shortcut in a test suite — needs another
answer. API access uses a Cloud PAT sent as a cloud_pat header.
This is the single most disruptive row in the table and it is easy to miss, because nothing about it is announced loudly. If your deployment scripts authenticate with an admin secret today, they do not have a direct translation.
Cron triggers do not exist
Scheduled and cron triggers have no v3 replacement. If you run reconciliation jobs, digest emails or nightly cleanups off Hasura’s scheduler, that work moves to your own scheduler before you migrate.
Event triggers are unfinished
Event triggers are marked work in progress. Plan for them to live outside Hasura — a listener on the database’s own change stream, or writes going through a service that emits its own events. Do not architect a v3 migration assuming they will land in time.
Database migrations are no longer Hasura’s job
hasura migrate has no successor. Schema migrations belong to your own tool —
Alembic, Flyway, whatever you already use — and you tell Hasura about the result by
re-introspecting the connector. For teams who leaned on the v2 CLI to version schema and metadata
together, this is a real workflow change and not a small one.
So should you migrate?
If you are starting fresh, are federating across several data sources or teams, and none of the gaps above are load-bearing for you, v3 is the direction the product is going and the supergraph model is genuinely better at that job.
If you have a working v2 installation whose value is a well-tuned permission model over one Postgres database, and you use cron or event triggers, then the honest answer is that migrating buys you architecture you may not need and costs you features you currently have. v2 is not going anywhere yet. Read the table, find your own load-bearing rows, and decide from those rather than from the version number.