HomeServicesOfAmerica / moleculer-graphql

[Archived] GraphQL Schema stitching over a microservice network for co-located type definitions.
GNU General Public License v3.0
48 stars 17 forks source link

Related Entities Not included #9

Open go4cas opened 6 years ago

go4cas commented 6 years ago

I have the following models:

Author

const graphql = require('moleculer-graphql');
const authors = require('./data').authors;

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,
  }
`;

const relationships = `
  extend type Author {
    books: [Book]
  }
`;

const relationDefinitions = {
  books: {
    type: 'query',
    operationName: 'booksByAuthor',
    args: {
      authorId: 'parent.id',
    },
  },
};

const Query = {
  authors: () => authors,
  author: (_, { id }) => authors.find(author => author.id === id),
};

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
};

const authorGraphQL = graphql.createGraphqlMixin({
  typeName: 'Author',
  schema,
  resolvers,
  relationships,
  relationDefinitions
});

module.exports = {
  authorGraphQL
};

Book

const graphql = require('moleculer-graphql');
const books = require('./data').books;

const schema = `
  type Book {
    id: Int,
    title: String,
    authorId: Int,
    year: Int,
  }
  type Query {
    book(id: Int!): Book,
    books: [Book],
    booksByAuthor(authorId: Int!): [Book],
  }
`;

const relationships = `
  extend type Book {
    author: Author,
    chapters: [Chapter],
  }
`;

const relationDefinitions = {
  chapters: {
    type: 'query',
    operationName: 'chaptersInBook',
    args: {
      bookId: 'parent.id',
    },
  },
  author: {
    type: 'query',
    operationName: 'author',
    args: {
      id: 'parent.authorId',
    },
  },
};

const queries = {
  books: () => books,
  book: (_, { id }) => books.find(book => book.id === id),
  booksByAuthor: (_, { authorId }) => books.filter(book => book.authorId === authorId),
};

const resolvers = {
  Query: queries,
};

const bookGraphQL = graphql.createGraphqlMixin({
  typeName: 'Book',
  schema,
  resolvers,
  relationships,
  relationDefinitions,
});

module.exports = {
    bookGraphQL
};

Used by the following services:

author.service

"use strict";

const authorGraphQL = require("../models/Author").authorGraphQL;

module.exports = {
    name: "author",
    mixins: [authorGraphQL],
};

book.service

"use strict";

const bookGraphQL = require("../models/Book").bookGraphQL;

module.exports = {
    name: "book",
    mixins: [bookGraphQL],
};

The GraphQLGateway is started in the API Gateway:

"use strict";

const ApiGateway = require("moleculer-web");
const graphqlGW = require("moleculer-graphql");

module.exports = {
    name: "api",
    mixins: [ApiGateway],

    settings: {
        port: process.env.APIPORT || 3030,

        routes: [{
            path: "/",
            whitelist: [
                "*"
            ],
            aliases: {
                "GET users/:id": "user.getUser"
            }
        }]

    },

    started() {
        let broker = this.broker;
        let gateway = new graphqlGW.GraphQLGateway({
            broker
        });

        gateway.start()
        .then((gw) => {
            console.log('GraphQL Gateway Started!');
        })
        .catch((err) => {
            console.error('ERROR:',err)
        });
    },

};

The gateway starts up correctly. When calling one of the services, this works fine: call author.graphql --query "{authors {name id}}"

But, when including a related entity: call author.graphql --query "{authors {name id books{title year authorId id}}}", the following exception:

Cannot query field "books" on type "Author"

go4cas commented 6 years ago

Okay, I have figured it out.

Simple solution: call the gateway, as opposed to the entity services: Use call gateway.graphql --query "{authors {name id books{title year authorId id}}}" Not call author.graphql --query "{authors {name id books{title year authorId id}}}"

go4cas commented 6 years ago

By the way, I realized that the latest master code has not been published to NPM. I had to clone the moluculer-graphql repo, and add "moleculer-graphql": "file:../moleculer-graphql" to my package.json.

creatzor commented 6 years ago

@go4cas thanks for the heads up about npm.. I was going crazy wondering why the gateway.graphql call wasn't working. Would be great if an updated release could be made to npm @brad-decker . This module is awesome!

brad-decker commented 6 years ago

@go4cas and @albert-the-creator -- sorry about that guys will get an updated release out soon. And just to reiterate, the individual graphql actions created on each schema service are intended to be used only by the gateway. The gateway is the only entity on the network that has the "Full view" of the schemas and how they work together.

go4cas commented 6 years ago

@brad-decker, thanks for looking into the release. Yes, I fully agree that the gateway should be the only entry point into the schema of the underlying services. It wasn't that clear in the docs, but after a while I figured it out, and things just started working. Really easy, and very powerfull!

go4cas commented 6 years ago

@brad-decker, any update on the NPM release? Thanks!

faeron commented 6 years ago

@brad-decker would love to hear about an updated release too. Reads like the current npm version doesn't work as intended (gateway as only entry point)without publishing the fixes.