Skip to content
GraphQL Schema stitching over a microservice network for co-located type definitions.
Branch: master
Clone or download
Nathan-Schwartz and brad-decker refactor(test): Refactor and add query tests
Splits the query tests out of schemaGeneration into a new file.

Refactor query test suite such that it can be easily re-run with a
different query resolution strategy.

Also changes test imports from src to package.json's "main"
file. This necessitates rebuilding the lib folder before
running tests, but the benefit is a more thorough test suite.
Latest commit 05fc11d Nov 27, 2017
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
flow-typed initialize repo Nov 10, 2017
src feat(gateway): Graphql action on gateway Nov 28, 2017
tests
.babelrc build(setup): sets up build process Nov 10, 2017
.flowconfig initialize repo Nov 10, 2017
.gitignore build(setup): sets up build process Nov 10, 2017
.npmignore build(setup): sets up build process Nov 10, 2017
LICENSE
README.md build(setup): sets up build process Nov 10, 2017
docker-compose.yml build(setup): sets up build process Nov 10, 2017
jest.config.js initialize repo Nov 10, 2017
package.json refactor(test): Refactor and add query tests Nov 28, 2017
schema.snapshot.graphql initialize repo Nov 10, 2017
yarn.lock build(setup): sets up build process Nov 10, 2017

README.md

Moleculer GraphQL

The goal of this utility is to introduce a way to co-locate GraphQL Type definitions with the microservices that manage access to those resources. This allows for looser couplings with microservices and a centralized GraphQL api gateway.

To achieve this, a microservice system must exist that accounts for the following architectural challenges:

  1. The api gateway service must discover other services on the network that define graphql schemas.
  2. Once the api gateway service has collected all schemas from the network, it must stitch them together to create a single schema.
  3. The api gateway service should handle routing of requests to individual services in order to resolve segments of incoming GraphQL queries, depending on where the schema originated from.
  4. The discovery aspect must account for inter-dependencies between services, ie: related types.

Moleculer GraphQL provides a couple of tools to make this super easy.

GraphQLGateway

This class is where the magic happens. This is all you need to create a GraphQL api gateway into your microservice architecture. All that is required is that you provide it a moleculer broker that is configured to communicate with your other services on the network.

  gateway = new GraphQLGateway({
    broker,
  });

  gateway.start().then(() => {
    console.log('gateway is ready! Schema is accessible via gateway.schema');
  });

The gateway will register a service on this broker that listens for nodes connecting to the network and updates the schema dynamically anytime a GraphQL service joins the network. It will also initialize the schema by checking already connected services for schemas.

When the gateway discovers a new GraphQL service it will grab values from the network that are provided via the createGraphqlMixin generated moleculer mixin. The details provided here are enough to use Apollo Link and Schema Stitching to build a collection of remote schemas with interdependcies that traverse the network to fulfill data requirements.

createGraphqlMixin

This function can be called to generate a moleculer mixin that defines a schema on the settings key that allows it to be discovered by an api gateway service. Lets start with a fully baked example (see the tests folder for more)

import createGraphqlMixin from '../../src/createGraphqlMixin';
import { authors } from './data';

// Define our schema specific to our type
// Include all queries and mutations related to this type
const schema = `
  type Author {
    id: Int,
    name: String,
  }

  type Query {
    author(id: Int!): Author,
    authors: [Author],
    authorOf(bookId: Int!): Author,
  }

  input UpdateAuthorInput {
    id: Int!
    clientMutationId: Int!
    name: String
  }

  type UpdateAuthorPayload {
    author: Author
    clientMutationId: Int
  }

  type Mutation {
    updateAuthor(input: UpdateAuthorInput!): UpdateAuthorPayload,
  }
`;

// Using apollo's extend type, lets add in any fields that are
// dependencies on other services
const relationships = `
  extend type Author {
    books: [Book],
  }
`;

// Now lets tell the gateway how to handle resolving those dependencies
const relationDefinitions = {
  books: {
    type: 'query', // Fetch via a 'query'
    operationName: 'booksByAuthor', // Use this query to resolve data
    args: {
      authorId: 'parent.id', // pass parent.id as authorId arg in the query
    },
  },
};

// Here's the resolvers for our author queries defined in schema
const Query = {
  authors: () => authors,
  author: (_, { id }) => authors.find(author => author.id === id),
};

// And our mutation resolvers
const Mutation = {
  updateAuthor(_, { id, name, clientMutationId }) {
    const authorIdx = authors.findIndex(author => author.id === id);
    const author = authors[authorIdx];
    if (!name) return author;
    author.name = name;
    authors[authorIdx] = author;
    return { author, clientMutationId };
  }
};

const resolvers = {
  Query,
  Mutation
};

// Call the createGraphqlMixin to build our mixin
const authorGraphQL = createGraphqlMixin({
  typeName: 'Author',
  schema,
  resolvers,
  relationships,
  relationDefinitions,
});

// Export a moleculer service definition with the mixin!
export default {
  name: 'Author',
  mixins: [authorGraphQL],
};

Now the schema is attached to the service and will be discovered by the GraphQLGateway!

how to use

yarn add moleculer-graphql

Then in your code:

import { GraphQLGateway, createGraphqlMixin } from 'moleculer-graphql';

for Node 6:

import { GraphQLGateway, createGraphqlMixin } from 'moleculer-graphql/node6';
You can’t perform that action at this time.