Skip to content

Latest commit

 

History

History

federation-services

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 

Federation services

This example demonstrates the integration of Apollo Federation services into a stitched schema.

⚠️ NOTE: integration with Apollo Federation and its evolving roadmap is NOT a core concern of GraphQL Tools Schema Stitching. While the two systems have plenty of similarities and you can pull many strings to make them talk to each other, there is no formal contract that guarentees their interoperability. The following guide outlines commonalities between the two systems. Buyer beware that you're assuming your own testing and maintenance overhead if you choose to couple these systems in a production environment. Also note that the Federation roadmap outlines several prospective features that will tightly-couple subservices with a proprietary Apollo Gateway implementation.

As you get the hang of schema stitching, you may find that Federation services are fairly complex for what they do. The buildFederatedSchema method from the @apollo/federation package creates a nuanced GraphQL resource that does not guarentee itself to be independently consistent, but plugs seamlessly into a greater automation package. By comparison, stitching encourages services to be independently valid and self-contained GraphQL resources, which makes them quite primitive and durable. While federation automates service bindings at the cost of tightly-coupled complexity, stitching embraces loosely-coupled bindings at the cost of manual setup. The merits of each strategy are likely to be a deciding factor for developers selecting a platform. Stitching is a library used to build a framework like Federation.

Stitching is less opinionated than Federation, and is made considerably simpler without the complexity added by buildFederatedSchema. However, when integrating with existing servers or in the process of a migration, nothing says you can't incorporate your existing federation resources into a stitched gateway going through the federation _entities query – which is fundamentally just a GraphQL service.

This example demonstrates:

  • Integrating Apollo Federation services into a stitched schema.
  • Fetching and parsing Federation SDLs.

Setup

cd federation-services

yarn install
yarn start

The following services are available for interactive queries:

This example is based on the Federation intro example.

Summary

Ever wonder what Federation is doing under the hood? Visit the products service and check out some User objects:

query {
  _entities(representations: [
    { __typename: "User", id: "1" },
    { __typename: "User", id: "2" },
    { __typename: "User", id: "3" }
  ]) {
    ...on User {
      id
      recentPurchases {
        upc
        name
        price
      }
    }
  }
}

A federation service automatically configures an _entities query that recieves typed keys (i.e.: objects with a __typename), and returns abstract _Entity objects that may assume the shape of any type in the service. Apollo Gateway then automates the exchange of typed keys for typed results, all going through the dedicated _entities protocol in each subservice. Stitching can also integrate with this _entities query by sending it properly formatted keys.

Now go to the gateway and check out the stitched results:

query {
  user(id: "1") {
    username
    recentPurchases {
      upc
      name
    }
    reviews {
      body
      author {
        id
        username
      }
      product {
        upc
        name
        acceptsNewReviews
      }
    }
  }
}

The stitched gateway has loaded all federation SDLs, converted them into stitching SDLs, and then integrates them like any other GraphQL service with types merged through their _entities query.

Adapting Federation services

Federation and Stitching use fundamentally similar patterns to combine underlying subservices (in fact, both tools have shared origins in Apollo Stitching). However, their specific implementations have an important differentiator:

  • Apollo Federation uses a centralized approach, where all types have a single "origin" service (i.e.: where the unextended type definition is). Querying for a type builds from its origin service.
  • Stitching uses a decentralized approach, where any service may equally originate any type. Regardless of where a typed object is first represented, that original object is filled in with missing details from other services.

How each system handles origins informs how a federation service gets translated into a stitched subschema:

  1. All types with a @key directive become merged types; the key fields go into selectionSet.
  2. All fields with a @requires directive are made into computed fields. Computed fields are slightly more robust than their federation counterparts because they may resolve dependencies from any number of services.
  3. All fields with an @external directive are removed unless they are part of the @key. Stitching expects schemas to only publish fields that they actually have data for. This is considerably simpler than the federation approach where services may be responsible for data they don't have.
  4. By eliminating the indirection of @external fields, the @provides directive is no longer necessary. The Stitching query planner can automate the optimial selection of as many fields as possible from as few services as possible.

SDL integration

The simplest way to make the above adaptions is to translate a Federation SDL string into a Stitching SDL string, which can be done using the federationToStitchingSDL utility function from @graphql-tools/stitching-directives package. A federation service's SDL can be obtained through its _service API:

query {
  _service {
    sdl
  }
}

Once fetched, it can be translated into a Stitching SDL and then built into a stitched schema:

const { buildSchema } = require('graphql');
const { stitchingDirectives, federationToStitchingSDL } = require('@graphql-tools/stitching-directives');
const makeRemoteExecutor = require('./lib/make_remote_executor');
const stitchingConfig = stitchingDirectives();

const executor = makeRemoteExecutor('http://localhost:4001/graphql');
const federationSDL = await executor({ document: '{ _service { sdl } }' });
const stitchingSDL = federationToStitchingSDL(federationSDL, stitchingConfig);

const gatewaySchema = stitchSchemas({
  subschemaConfigTransforms: [stitchingConfig.stitchingDirectivesTransformer],
  subschemas: [{
    schema: buildSchema(stitchingSDL),
    executor
  }]
});

Static config

Written as static subservice configuration, a federation service merges types within a stitched gateway using the following:

const { pick } = require('lodash');

const gatewaySchema = stitchSchemas({
  subschemas: [{
    schema: buildSchema(stitchingSDL),
    merge: {
      Product: {
        selectionSet: '{ id }',
        fields: {
          shippingEstimate: { selectionSet: '{ price weight }', computed: true }
        },
        fieldName: '_entities',
        key: (originObj) => ({ __typename: 'Product', ...pick(originObj, ['id', 'price', 'weight']) }),
        argsFromKeys: (representations) => ({ representations }),
      }
    }
  }]
});