Hasura – Remote Schemas and Federation

July 24, 20263 min readUpdated 8/21/2026

An Action brings a REST endpoint into your schema. A remote schema brings an entire existing GraphQL API into it — every type and field it exposes, merged into one graph that clients query as though it were all yours.

Adding one

You give Hasura a URL and it introspects:

{
  "type": "add_remote_schema",
  "args": {
    "name": "payments",
    "definition": {
      "url_from_env": "PAYMENTS_GRAPHQL_URL",
      "timeout_seconds": 30,
      "forward_client_headers": true,
      "headers": [{"name": "X-Internal-Key", "value_from_env": "PAYMENTS_KEY"}]
    }
  }
}

url_from_env rather than a literal, so the same metadata works across environments — the same reasoning as any other config, and easy to forget when clicking through a console.

The problem that shows up immediately

Two schemas merged into one namespace collide. If both define User, or both expose a root field called orders, Hasura refuses to merge and tells you so.

The blunt fix is a namespace, which prefixes everything from the remote:

query {
  payments {
    invoice(id: $id) { total status }
  }
}

It is not elegant, and it is much better than the alternative — renaming types in a service you may not own to satisfy a gateway. Decide the namespacing convention before you add the second remote, not after.

Remote relationships

This is the feature worth the trouble. A remote schema alone gives you two APIs at one URL, which is convenient but shallow. A remote relationship joins a database row to a field on another service:

query {
  bookings(where: {publicId: {_eq: $id}}) {
    publicId
    checkIn
    total
    invoice {          # <- lives in the payments service
      status
      paidAt
    }
  }
}

You define it by mapping a column to an argument — bookings.invoice_id feeds the remote’s invoice(id:). Hasura resolves the table rows first, then calls the remote with the ids it found.

This one is n+1

Be clear-eyed here, because it contradicts lesson 5. Database relationships compile to a single SQL statement with a lateral join. A remote relationship cannot — it is a network call to another service.

Hasura batches where the remote supports it, but if the remote exposes only invoice(id:) and no invoices(ids:), twenty bookings mean twenty calls. Ask for a batch field on the remote before you ship a list view that uses one.

Auth across the boundary

Two independent decisions, and conflating them is the usual mistake.

forward_client_headers passes the caller’s headers to the remote, so the remote does its own authentication with the user’s token. The static headers list sends service credentials instead, so the remote trusts Hasura and knows nothing about the user.

Pick deliberately. Forwarding means the remote enforces its own permissions — safer, and it requires the remote to understand your tokens. Service credentials mean Hasura is the only gate, so anything you did not restrict is exposed. Hasura’s own permissions apply to whether a role may query the remote at all, not to individual fields inside it.

Apollo Federation

Hasura can also participate in a federated graph as a subgraph, rather than being the gateway. If your organisation already runs a federation router, that is usually the better fit — Hasura does what it is good at over your database, and the router owns composition.

In v3 (DDN)

On the v3 sections. Everything marked v3 (DDN) is taken from the official Hasura DDN documentation as read on 2026-08-21 and was not run locally — it shows configuration, never claimed output. The v2 material was executed against a running engine.

This is the topic where v3 changes most, because in v3 it stops being a bolt-on and becomes the architecture.

There is no “remote schema” feature. An existing GraphQL API is reached through the GraphQL connector — the same mechanism as any other data source. Postgres has a connector, your REST service has a connector, another GraphQL API has a connector; nothing is privileged.

Composition then happens through subgraphs. Each is a self-contained domain with its own connectors, metadata, permissions and repository, and the supergraph is their composition. The collision problem the namespace hack solved in v2 is handled structurally: subgraphs are separate namespaces by construction.

Crucially, a v3 Relationship can cross connectors and subgraphs, so the booking-to-invoice join above is an ordinary relationship rather than a special kind. It is the same declaration whether the target is a table in the same database or a service another team owns.

Apollo Federation remains supported.

Next

Lesson 12 covers running code when data changes.