Skip to content

PATTERN Cited by 1 source

Multi-tenant GraphQL runtime

When to use

You have:

  • Many teams wanting to contribute to one GraphQL schema.
  • Pressure to avoid running one GraphQL server per contributing team (Federation's cost) and also to avoid one giant codebase that every team commits into (UBFF's shared release train).
  • A platform team that can build and operate a shared runtime.
  • Domain teams that don't want to operate GraphQL infrastructure.

The pattern

One shared multi-tenant runtime hosts many independently developed and tested tenant modules, each owning a portion of the schema. The tenant module is the unit of decentralisation (see patterns/module-based-graphql-decentralization).

The contribution contract

A tenant team's contract is intentionally minimal — the canonical formulation from Viaduct's 1.0 announcement (verbatim): "A team wanting to contribute simply creates a directory for their module, defines their schema definition language (SDL) and resolvers, and they are ready to serve. There is no need to set up or operate a separate GraphQL service, manage router composition, or become experts in GraphQL infrastructure. Teams focus on domain logic; the platform handles execution, scaling, and integration." (Source: sources/2026-05-13-airbnb-viaduct-1-0-and-the-future-of-airbnbs-data-mesh)

The module:

  • A directory in the runtime's codebase.
  • An SDL declaring the module's Types / Queries / Mutations.
  • Resolvers implementing the contract.

The runtime:

  • Execution — runs the module's resolvers on incoming requests.
  • Scaling — handles capacity for the shared substrate; tenant modules don't scale themselves.
  • Integration — composes module SDLs into the runtime's global schema; routes incoming GraphQL requests to the right module's resolvers.
  • Multi-tenancy — isolates tenant modules' state, observability, and (ideally) blast radius from each other.

Topology

              clients
       ┌───────────────────────┐
       │ multi-tenant GraphQL   │
       │ runtime                │
       │                        │
       │  ┌───────┐  ┌───────┐  │
       │  │tenant │  │tenant │  │
       │  │module │  │module │  │
       │  │  A    │  │  B    │  │
       │  └───┬───┘  └───┬───┘  │
       │      │          │      │
       │  ┌───┴───┐  ┌───┴───┐  │
       │  │tenant │  │tenant │  │
       │  │module │  │module │  │
       │  │  C    │  │  D    │  │
       │  └───────┘  └───────┘  │
       └───────────────────────┘

Canonical wiki instance: Viaduct

Viaduct is the canonical wiki instance. Airbnb's GraphQL data-oriented service mesh runs as a multi-tenant runtime hosting tenant modules contributed by many teams across the company; the 1.0 announcement (2026-05-13) released it as open source on Maven Central.

The pattern's load-bearing framing quote, verbatim from the same post: "Federation distributes development through services. Each team owns and operates its own GraphQL subgraph server; those subgraphs are composed by a federation router into a single unified graph. Viaduct distributes development through modules. A shared multi-tenant runtime hosts tenant modules that define and implement portions of the schema."

Trade-offs vs adjacent patterns

Three GraphQL decentralisation topologies sit on the wiki: UBFF (see patterns/unified-graphql-backend-for-frontend + Zalando UBFF as the canonical instance); the multi-tenant runtime (this pattern + Viaduct as the canonical instance); and Federation (see patterns/federated-graphql-subgraph-per-domain + Yelp CHAOS on Apollo Federation as the canonical instance). Their trade-offs:

Property UBFF (one service, one module) Multi-tenant runtime (Viaduct) Federation (many services, one module each)
Servers per organisation 1 Few (one per Viaduct instance) Many (one per subgraph)
Modules per server 1 (the whole graph) Many 1
Per-team operational cost Low (no server to operate) Low (no server to operate) High (each team owns a subgraph server)
Per-team release autonomy Low (shared release train) Medium (shared per-runtime) High (each team deploys independently)
Cross-team blast radius High (single deploy) Medium (per-runtime deploy) Low (per-subgraph)
Composition complexity None Module composition inside runtime Federation router composition

The multi-tenant runtime pattern lives between UBFF and Federation on every axis. It collapses Federation's per-team server cost (good) at the price of giving up Federation's per-team deploy independence (a runtime-level deploy moves all its tenant modules together).

Composition with Federation

Multi-tenant runtimes and Federation are complementary: a multi-tenant runtime can participate as a subgraph inside a federated supergraph. The Viaduct post names this directly: "In a large organization where hundreds of teams contribute to the overall graph, a federated approach requires running hundreds of independent subgraph servers. With Viaduct, organizations can instead run a smaller number of Viaduct instances, each hosting many closely related tenant modules. Federation can then compose those instances into a larger enterprise graph."

             enterprise Federation router
       ┌───────────────┼───────────────┐
       ▼               ▼               ▼
   Viaduct A       Viaduct B       Viaduct C
   ┌───────┐       ┌───────┐       ┌───────┐
   │mods   │       │mods   │       │mods   │
   │ 1-10  │       │ 11-20 │       │ 21-30 │
   └───────┘       └───────┘       └───────┘

Compared with N × M independent subgraph servers, this collapses the operational cost by a factor of M while preserving cross-organisation composition at the router.

Hard problems this pattern introduces

  • Per-module isolation boundary. A bad / runaway / leaking resolver in module A must not take down module B. The runtime must enforce per-module CPU / memory / time / connection budgets — and there's no out-of-the-box solution. The Viaduct GraphQLConf 2026 talk "Sharding a GraphQL Gateway for Blast Radius Reduction" (Linquan Zhang & Cetin Sahin, 2026-05-20) is the explicit follow-up on this.
  • Per-module observability. Whose alert fires and who pays for the cost of a query that crosses module boundaries is non-obvious. Built-in ownership tags + cost-aware tracing are the disclosed-but-not-detailed answer (see Viaduct's GraphQLConf 2026 talk "Observability for a Multi-Tenant GraphQL Gateway at Scale" by Vickey Yeh).
  • Schema composition across modules. When two modules declare the same Type, or one module's Query references another's Type, the runtime needs deterministic composition rules. UBFF avoids this (one schema source); Federation formalises it (@key, @external, @requires directives); multi-tenant runtimes need their own variant.
  • Release-train coupling within a runtime. Tenant modules inside the same runtime share a deploy. The pattern only helps team-autonomy along one axis: it removes the "operate-a-server" tax, but it doesn't remove the "deploy-with-others" coupling.
  • Cross-runtime composition still needs Federation. If you need true cross-runtime ownership autonomy, you re-introduce the Federation router. The pattern is most attractive when most contributing teams can fit into a small number of related runtimes.

Trade-offs not solved by the pattern

  • GraphQL caching is hard. Multi-tenant runtimes don't change this — every query is still a unique URL+body, so a GraphQL-aware caching layer is still required.
  • Resolver fan-out is still a footgun. A naive multi-module query can hit many backends; protect with DataLoader + depth / complexity limits, same as any GraphQL deployment.
  • Schema governance is still load-bearing. Who can declare a Type, who must approve a deprecation, what naming is allowed — none of these are solved by switching to a multi-tenant runtime.

Seen in

Last updated · 542 distilled / 1,571 read