Skip to content

Custom directive schema transforms lose resolveReference in federation, breaking _entities queries #243

@fgiova

Description

@fgiova

Description

When using custom directive transformers (e.g. built with @graphql-tools/utils mapSchema) with schemaTransforms in a federated service, _entities queries return null for all entity fields.

The mercurius documentation describes how to use custom directives with federation at https://mercurius.dev/#/docs/custom-directive?id=federation-and-custom-directives, but the recommended approach using makeExecutableSchema + printSchemaWithDirectives + mergeResolvers is verbose and error-prone. More importantly, even following that guide, resolveReference can still be lost depending on how the schema is built and registered.

Root cause

Mercurius sets resolveReference as a non-standard runtime property on GraphQLObjectType instances. When schemaTransforms are applied, mapSchema internally recreates type instances via new GraphQLObjectType(type.toConfig()), which does not carry over non-standard properties. As a result, resolveReference is lost and entity resolution silently fails.

Reproduction

const mercurius = require('mercurius')
const { buildFederationSchema } = require('@mercuriusjs/federation')

const schema = `
  directive @upper on FIELD_DEFINITION

  extend type Query { me: User }

  type User @key(fields: "id") {
    id: ID!
    name: String @upper
    username: String
  }
`

const resolvers = {
  Query: { me: () => users['1'] },
  User: { __resolveReference: (source) => users[source.id] }
}

app.register(mercurius, {
  schema: buildFederationSchema(schema),
  resolvers,
  schemaTransforms: [upperDirectiveTransformer]
})

The following query returns null for name and username:

{
  _entities(representations: [{ __typename: "User", id: "2" }]) {
    ... on User { id name username }
  }
}

Expected behavior

Entity fields should resolve correctly even when schemaTransforms are applied.

Proposed solution

Provide a federationSchemaTransformer wrapper that re-applies resolveReference from the original type map onto the transformed schema after all transformers have run. The mercuriusFederationPlugin should apply this wrapper automatically when schemaTransforms are provided. This also simplifies the workflow described in the mercurius documentation, removing the need for the manual makeExecutableSchema + printSchemaWithDirectives + mergeResolvers approach.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions