Migration from GraphQL Mesh v0 (Experimental - Beta)
This page is for people coming from Mesh v0 (.meshrc.yml, @graphql-mesh/cli, .mesh
artifacts). Mesh v1 is a different product shape: Compose builds a Supergraph SDL, Hive
Gateway serves it. The sections below keep the original v0 concepts and show what they became.
Please make sure all of your @graphql-mesh/ packages are up-to-date on v0 first. Otherwise, the
migration script may not parse your config as expected.
What changed (v0 vs v1)
If you are migrating from GraphQL Mesh v0, you should be aware of the following changes:
- GraphQL Mesh no longer comes with a built-in gateway. You should setup Hive Gateway to serve the generated artifacts.
- GraphQL Mesh no longer generates an executable JavaScript code in
.meshfolder, but instead it builds a GraphQL SDL (Supergraph or Subgraph) that you can use with Hive Gateway. - GraphQL Mesh Transforms no longer have
baremode. There is onlywrapmode now, and all the transforms are using Federation-compatible directives. Any transformed subgraph should be served using Hive Gateway. - If you deploy GraphQL Mesh v0 by using the http handler provided in the artifacts under
.mesh, now you should take a look at Hive Gateway’s Deployment Guide to deploy your gateway. - GraphQL Mesh no longer runs GraphQL Code Generator internally, and
it no longer writes SDKs into
.mesh. You still have two typed paths: an operation SDK (getSdk+ Hive Gateway) and an in-context SDK (context.Source.Query.fieldin resolvers). See Type-safe SDKs. - GraphQL Mesh no longer generates a Persisted Operations store. You should setup your gateway based on your needs. See here for more information.
GraphQL Mesh is now only responsible of generating a GraphQL SDL file supergraph.graphql or
subgraph.graphql that you can use with Hive Gateway. It is not responsible for serving the
generated artifacts.
| Topic | v0 | v1 |
|---|---|---|
| Role of Mesh | One runtime: load sources, merge, transform, and serve GraphQL | Compose only: produce Supergraph/Subgraph SDL |
| Serving | Built into @graphql-mesh/cli (mesh dev / mesh start) | Hive Gateway (hive-gateway supergraph) |
| Config | .meshrc.yml / .meshrc.yaml / .meshrc.json / .meshrc.js | mesh.config.ts (or .js) with composeConfig + gatewayConfig |
| Packages | @graphql-mesh/cli + @graphql-mesh/<handler> | @graphql-mesh/compose-cli + @omnigraph/* + @graphql-hive/gateway |
| Output | .mesh/ (JS SDK, execute, HTTP handler, schema) | supergraph.graphql (or .js) — SDL, not a Node server |
| Schema merge | merger (stitching / bare / federation) | Federation-compatible composition |
| Transforms | bare or wrap; can sit on a source or on the unified schema | Wrap only; source-level in compose; Federation directives |
| Type merging | typeMerging transform / stitching | Federation transform / @resolveTo |
| Operation SDK | documents + sdk → getMeshSDK() | Codegen + sdkRequester (typed-sdk example ) |
| In-context SDK | .mesh MeshContext / context.Source.Query.* | @graphql-mesh/incontext-sdk-codegen |
| Persisted operations | Mesh-generated store | Hive Gateway persisted documents |
| In-process execute | getBuiltMesh() / .mesh execute | Local Execution via Hive Gateway executor |
Mental model: sources vs subgraphs
v0 called each upstream a source (handler + optional transforms), then merged them into a
unified schema. v1 calls each upstream a subgraph. Compose loads it with a sourceHandler,
applies transforms on that subgraph, then composes a supergraph.
| v0 | v1 |
|---|---|
sources[].name | First argument of loadOpenAPISubgraph('Wiki', …) / loadGraphQLHTTPSubgraph('Countries', …) |
sources[].handler.openapi | sourceHandler: loadOpenAPISubgraph(...) |
sources[].transforms | subgraphs[].transforms: [createPrefixTransform(...)] |
Root transforms: (unified schema) | Not supported — move onto a subgraph or use Federation |
additionalTypeDefs | composeConfig.additionalTypeDefs (often with @resolveTo) |
additionalResolvers | gatewayConfig.additionalResolvers (runtime, not compose) |
Configuration Format
Before, Mesh used .meshrc.yml (or yaml/json/js). Now Mesh uses mesh.config.ts / mesh.config.js
with two exports:
composeConfig— what Mesh Compose reads (mesh-compose)gatewayConfig— what Hive Gateway reads (hive-gateway)
The same file can hold both so one project still has a single config, but the jobs are split.
Example: same gateway, two configs
v0 .meshrc.yml:
sources:
- name: Wiki
handler:
openapi:
source: ./wiki.yaml
transforms:
- prefix:
value: Wiki_
- name: Countries
handler:
graphql:
endpoint: https://countries.trevorblades.com
serve:
port: 4000
cors:
origin: '*'
plugins:
- responseCache:
ttl: 1000v1 mesh.config.ts (what the migrator aims to produce):
import {
createPrefixTransform,
defineConfig as defineComposeConfig,
loadGraphQLHTTPSubgraph
} from '@graphql-mesh/compose-cli'
import { defineConfig as defineGatewayConfig } from '@graphql-hive/gateway'
import { loadOpenAPISubgraph } from '@omnigraph/openapi'
export const composeConfig = defineComposeConfig({
subgraphs: [
{
sourceHandler: loadOpenAPISubgraph('Wiki', { source: './wiki.yaml' }),
transforms: [createPrefixTransform({ value: 'Wiki_' })]
},
{
sourceHandler: loadGraphQLHTTPSubgraph('Countries', {
endpoint: 'https://countries.trevorblades.com'
})
}
]
})
export const gatewayConfig = defineGatewayConfig({
port: 4000,
cors: { origin: '*' },
responseCaching: { ttl: 1000 }
})Run the migrator
Install @graphql-mesh/migrate-config-cli to generate mesh.config.ts from your v0 file.
npm install @graphql-mesh/migrate-config-cliThen run the following command in your project directory:
npx mesh-migrate-configOptional flags:
| Flag | Meaning |
|---|---|
--dry-run | Print mesh.config.ts to stdout, do not write |
--force | Overwrite an existing mesh.config.ts |
[directory] | Project root (default: cwd) |
During the migration process, you might get some errors or warnings related to the deprecated or removed functionalities. Please make sure to update your legacy v0 configuration and setup according to those warnings and errors.
- Errors mean the CLI did not write
mesh.config.ts(root-level transforms, unknown handler, removed transform such astypeMerging). - Warnings mean it wrote a file, but you still have manual work (codegen, pubsub, merger, …).
The config migrator is still experimental. Review mesh.config.ts and the messages it prints.
Please report anything it gets wrong.
CLI and daily workflow
| Goal | v0 | v1 |
|---|---|---|
| Dev server (rebuild schema from sources) | mesh dev | mesh-compose -o supergraph.graphql then hive-gateway supergraph (re-compose when sources change) |
| Production schema artifacts | mesh build → .mesh/ | mesh-compose -o supergraph.graphql |
| Production HTTP server | mesh start (loads .mesh) | hive-gateway supergraph (loads SDL) |
| Validate | mesh validate | Compose + Gateway config / Hive schema checks |
| Serve a single source | mesh serve-source | Compose one subgraph, or point Hive Gateway at that subgraph SDL |
v0 package.json often looked like "dev": "mesh dev", "build": "mesh build",
"start": "mesh start". v1 splits that into compose (CI / build) and gateway (runtime).
Package Changes
Instead of @graphql-mesh/cli, you should now use @graphql-mesh/compose-cli and
@graphql-hive/gateway.
npm uninstall @graphql-mesh/cli
npm install @graphql-mesh/compose-cli @graphql-hive/gatewaySource handler packages moved from @graphql-mesh/<handler> to @omnigraph/* (GraphQL HTTP stays
on compose-cli). The migrator prints the exact add/remove list for your config.
v0 handler | v0 package | v1 loader | v1 package |
|---|---|---|---|
graphql | @graphql-mesh/graphql | loadGraphQLHTTPSubgraph | @graphql-mesh/compose-cli |
openapi | @graphql-mesh/openapi | loadOpenAPISubgraph | @omnigraph/openapi |
jsonSchema | @graphql-mesh/json-schema | loadJSONSchemaSubgraph | @omnigraph/json-schema |
grpc | @graphql-mesh/grpc | loadGRPCSubgraph | @omnigraph/grpc |
soap | @graphql-mesh/soap | loadSOAPSubgraph | @omnigraph/soap |
raml | @graphql-mesh/raml | loadRAMLSubgraph | @omnigraph/raml |
mysql | @graphql-mesh/mysql | loadMySQLSubgraph | @omnigraph/mysql |
postgraphile | @graphql-mesh/postgraphile | loadPostgreSQLSubgraph | @omnigraph/postgresql |
mongoose | @graphql-mesh/mongoose | loadMongooseSubgraph | @omnigraph/mongoose |
neo4j | @graphql-mesh/neo4j | loadNeo4jSubgraph | @omnigraph/neo4j |
odata | @graphql-mesh/odata | loadODataSubgraph | @omnigraph/odata |
thrift | @graphql-mesh/thrift | loadThriftSubgraph | @omnigraph/thrift |
tuql | @graphql-mesh/tuql | loadSQLiteSubgraph | @omnigraph/sqlite |
supergraph | consume an existing supergraph as a Mesh source | Do not compose. Give that SDL to Hive Gateway. | @graphql-hive/gateway |
GraphQL handler differences the migrator knows about:
- v0 HTTP
{ endpoint }→ v1loadGraphQLHTTPSubgraph(name, { endpoint })(copied as-is). - v0 code-first
{ source: './schema.ts' }→ v1{ endpoint: './schema.ts' }(the v1 loader usesendpointfor URL or a local schema file). - v0
sources+fallback/race/highestValueis not auto-migrated. In v0 that was one handler with several HTTP backends. In v1, split them into subgraphs or keep a single endpoint.
See Source Handlers.
Transforms
v0 transforms could run in bare mode (mutate the schema in place) or wrap mode (schema
wrapping). v1 only wraps, and emits Federation-compatible directives, so the result must be served
by Hive Gateway (or another Federation-aware gateway). mode: wrap in YAML is dropped; it is
implied.
v0 allowed root-level transforms: on the unified schema. v1 does not. Move them onto the
relevant source, or replace stitching with Federation.
| v0 transform | v1 | What is different |
|---|---|---|
prefix | createPrefixTransform | Same idea; docs |
rename | createRenameTransform | docs |
filterSchema | createFilterTransform | Drop mode. A YAML list of globs becomes { filters: [...] } |
encapsulate | createEncapsulateTransform | docs |
namingConvention | createNamingConventionTransform | docs |
hoistField | createHoistFieldTransform | v0 array → { mapping: [...] } |
prune | createPruneTransform | docs |
federation | createFederationTransform | How you declare keys / @merge in v1 |
extend | createExtendTransform(typeDefs) | typeDefs stay in compose; extend.resolvers → gatewayConfig.additionalResolvers |
typeMerging | manual | Stitching-style merge → Federation / type merging |
cache | manual | Was a transform; now a gateway plugin — response caching |
rateLimit | manual | Was a transform; now gateway / @rateLimit — rate limit |
replaceField | manual | Use hoist, rename, or additionalTypeDefs |
resolversComposition | manual | additionalResolvers or a gateway plugin |
Serve, plugins, and runtime (compose vs gateway)
In v0, serve, plugins, cache, and customFetch lived next to sources in one YAML file. In
v1, anything that builds SDL is compose; anything that runs requests is Hive Gateway.
| v0 | v1 |
|---|---|
sources, source transforms, additionalTypeDefs, customFetch used while introspecting | composeConfig (fetch on compose) |
serve.*, plugins, cache, additionalResolvers, logger, runtime customFetch | gatewayConfig (fetchAPI.fetch) |
codegen, sdk, documents | GraphQL Codegen on the supergraph + Hive Gateway typed SDK (not Mesh CLI) |
merger | No replacement key; composition is Federation-style (migrator warns) |
pubsub | Hive Gateway subscriptions — docs |
persistedOperations | Hive persisted documents |
serve.extraParamNames, serve.browser | not migrated |
serve field names
v0 serve | v1 gatewayConfig |
|---|---|
port | port |
hostname | host |
endpoint (GraphQL path, default /graphql) | graphqlEndpoint |
playground: false | graphiql: false |
playgroundTitle | graphiql: { title } |
cors | cors |
healthCheckEndpoint | healthCheckEndpoint |
batchingLimit | batching: { limit } |
sslCredentials | sslCredentials |
fork | fork |
staticFiles | useStaticFiles(...) plugin |
browser | skipped (dev-only auto-open) |
Plugins
v0 plugins | v1 |
|---|---|
hive | reporting: { type: 'hive', ... } and persistedDocuments when set |
responseCache | responseCaching |
rateLimit | rateLimiting |
prometheus | prometheus |
maskedErrors | maskedErrors |
immediateIntrospection | useImmediateIntrospection() in plugins |
| other Envelop / Yoga / Armor plugins | resolved when possible; otherwise add to gatewayConfig.plugins yourself |
v0 cache: (Redis, Localforage, …) becomes gatewayConfig.cache: new Cache(...) with
@graphql-mesh/cache-*. That is not the old Cache transform.
Type-safe SDKs
v0 mixed two things into .mesh. They are separate in v1.
Operation SDK (getSdk) — client-style
v0 documents + sdk + getMeshSDK(). v1: GraphQL Codegen typescript-generic-sdk + Hive Gateway
sdkRequester. Canonical example:
Hive Gateway examples/typed-sdk
(CodeSandbox ).
| v0 | v1 |
|---|---|
documents: ['./src/**/*.graphql'] | Codegen documents (e.g. sdk/operations.graphql) |
sdk: { generateOperations: { selectionSetDepth } } | You own the .graphql operations |
mesh build → .mesh SDK | graphql-codegen against supergraph.graphql |
import { getMeshSDK } from './.mesh' | import { getSdk } from './sdk/generated' |
getMeshSDK() | getSdk(runtime.sdkRequester) or getSdk(getSdkRequesterForUnifiedGraph(...)) |
v0:
import { getMeshSDK } from './.mesh'
const sdk = getMeshSDK()
const { getSomething } = await sdk.myQuery({ someVar: 'foo' })v1:
import type { CodegenConfig } from '@graphql-codegen/cli'
export default {
schema: './supergraph.graphql',
documents: 'sdk/operations.graphql',
generates: {
'sdk/generated.ts': {
plugins: ['typescript-operations', 'typescript-generic-sdk']
}
}
} satisfies CodegenConfigimport { readFileSync } from 'node:fs'
import { createGatewayRuntime } from '@graphql-hive/gateway'
import { getSdk } from './sdk/generated'
await using runtime = createGatewayRuntime({
supergraph: readFileSync('./supergraph.graphql', 'utf-8')
})
const sdk = getSdk(runtime.sdkRequester)
const todos = await sdk.Todos()See example.ts
for mutations and subscriptions. In-process without createGatewayRuntime:
getSdkRequesterForUnifiedGraph in Local Execution.
The migrator warns on sdk / codegen / documents and does not write codegen.ts.
In-context SDK (context.Source.Query.field) — resolvers
v0 mesh build also typed Mesh context so additionalResolvers could call a source without
hand-writing GraphQL documents: context.Wiki.Query.someField({ root, args, context, info }).
v1: Codegen plugin
@graphql-mesh/incontext-sdk-codegen
against the supergraph. Example:
e2e/openapi-javascript-wiki.
import type { CodegenConfig } from '@graphql-codegen/cli'
export default {
schema: './supergraph.graphql',
generates: {
'./types/incontext-sdk.ts': {
plugins: ['@graphql-mesh/incontext-sdk-codegen']
}
}
} satisfies CodegenConfigimport type { MeshInContextSDK } from './types/incontext-sdk'
async function viewsInPastMonth(root, { project }, context: MeshInContextSDK, info) {
return context.Wiki.Query.metrics_pageviews_aggregate_by_project_by_access_by_agent_by_granularity_by_start_by_end(
{ root, args: { project /* … */ }, context, info, selectionSet: `{ items { views } }` }
)
}Full walkthrough: In-context SDK.
Using Artifacts
GraphQL Mesh no longer generates artifacts with execute or createBuiltMeshHTTPHandler. v0
deployment used those from .mesh after mesh build. v1 deployment uses an SDL file plus Hive
Gateway.
| v0 artifact | v1 |
|---|---|
.mesh/index.js getBuiltMesh / execute | Compose SDL + local execution (getExecutorForUnifiedGraph) |
.mesh createBuiltMeshHTTPHandler | Hive Gateway Node / serverless adapters — deployment |
Generated SDK from sdk: / getMeshSDK() | typed-sdk example (getSdk + sdkRequester) |
MeshContext / context.Source.Query.* | @graphql-mesh/incontext-sdk-codegen |
Playground on mesh dev | GraphiQL on Hive Gateway |
After a successful config migration:
- Install the packages the CLI printed and remove the old handler/CLI packages.
- Fix every error/warning (especially root transforms and type merging).
- Run
npx mesh-compose -o supergraph.graphqlto generate the supergraph schema. - Run
npx hive-gateway supergraphto start the gateway server.
Read more about Hive Gateway’s Deployment Guide to setup your gateway with the new artifacts.
If you need local execution or a typed SDK, see Local Execution & SDK and Type-safe SDKs. Operation SDK: examples/typed-sdk . In-context SDK: e2e/openapi-javascript-wiki . Compose background: Getting Started.