Catch the highlights of GraphQLConf 2023! Click for recordings. Or check out our recap blog post.
Docs
Guides
Extending the Unified Schema

Extending the Unified Schema

We saw on the "How to: Combine multiple Sources" guide that additionalResolvers could be used to shape and augment the Unified Schema with custom resolvers.

However, the additionalResolvers value can also be the path to a JavaScript/TypeScript file that exports the custom resolvers implementation.

Programmatic additionalResolvers

In our "How to: Combine multiple Sources" Gateway, the additionalResolvers could have been provided programmatically as shown in the multiple-sources-prog-resolvers example.

The below .meshrc.yaml configuration add the following fields:

  • Store.bookSells: [Sells!]!: to get the selling from a given store
  • Sells.book: Book: to get the book of a given store selling record
  • Book.author: authors_v1_Author: to get the author of a book

.meshrc.yaml

.meshrc.yaml
sources:
  # …
transforms:
  # …
additionalTypeDefs: |
  extend type Store {
    bookSells: [Sells!]!
  }
  extend type Sells {
    book: Book
  }
  extend type Book {
    author: authors_v1_Author
  }
 
additionalResolvers:
  - './resolvers'

resolvers.ts

resolvers.ts
import { Resolvers } from './.mesh'
 
const resolvers: Resolvers = {
  Book: {
    author: {
      selectionSet: /* GraphQL */ `
        {
          authorId
        }
      `,
      resolve(root, _args, context, info) {
        return context.Authors.Query.authors_v1_AuthorsService_GetAuthor({
          root,
          args: {
            input: {
              id: root.authorId
            }
          },
          context,
          info
        })
      }
    }
  },
  Store: {
    bookSells: {
      selectionSet: /* GraphQL */ `
        {
          id
        }
      `,
      resolve(root, _args, context, info) {
        return context.Stores.Query.bookSells({
          root,
          args: {
            storeId: root.id
          },
          context,
          info
        })
      }
    }
  },
  Sells: {
    book: {
      selectionSet: /* GraphQL */ `
        {
          bookId
        }
      `,
      resolve(root, _args, context, info) {
        return context.Books.Query.book({
          root,
          args: {
            id: root.bookId
          },
          context,
          info
        })
      }
    }
  }
}
 
export default resolvers

Mesh relies on GraphQL Code Generator to generate the Resolvers type that gives you access to:

  • fully typed resolvers map
  • fully typed SDK (through the context) to fetch data from Sources

Using the SDK to fetch Sources

Let's take a closer look at the Book.author resolver implementation.

The resolver is accessing the "Authors" source SDK through the context to fetch data from the authors_v1_AuthorsService_GetAuthor(id: ID!) Query as follows:

await context.Authors.Query.authors_v1_AuthorsService_GetAuthor({
  root,
  args: {
    input: {
      id: root.authorId
    }
  },
  context,
  info
})

authors_v1_AuthorsService_GetAuthor is a generated SDK method that allows to query our gRPC Books Source as it was a GraphQL Schema.

Any SDK method take the following arguments:

  • root, context and info are mandatory parameters that we forward from the resolvers calling the method. If the current field's return type doesn't match, you should provide a selectionSet.
  • args: arguments to pass to the Mutation or Query
  • selectionSet: allows to specify a selection on the Query/Mutation to fetch nested data like { id name }
  • valuesFromResults: allows to provide a function to transform the results (if the result is an array, the function will be mapped to each element)
💡

Note: valuesFromResults, compared to selectionSet helps to "flatten"/"un-nest" data from results

Going further

Transforms like "Hoist Field" also helps in shaping and extending the Unified Schema.