首页 > 解决方案 > 将 GraphQL 类型模块化到单独的文件中

问题描述

我有一个带有单个整体types/index.js文件的 GraphQL 实现,该文件当前包含两个类型定义:

const graphql = require('graphql');
const Book = require('../../../models/book');
const Author = require('../../../models/author');

const {
  GraphQLObjectType,
  GraphQLString,
  GraphQLSchema,
  GraphQLID,
  GraphQLInt,
  GraphQLList,
  GraphQLNonNull,
} = graphql;

const BookType = new GraphQLObjectType({
  name: 'Book',
  fields: () => ({
    id: { type: GraphQLID },
    name: { type: GraphQLString },
    genre: { type: GraphQLString },
    author: {
      type: AuthorType,
      resolve: (parent, args) => {
        // code to get data from db
        return Author.findById(parent.authorId);
      },
    },
  }),
});

const AuthorType = new GraphQLObjectType({
  name: 'Author',
  fields: () => ({
    id: { type: GraphQLID },
    name: { type: GraphQLString },
    age: { type: GraphQLInt },
    books: {
      type: new GraphQLList(BookType),
      resolve: (parent, args) => {
        // code to get data from db
        return Book.find({authorId: parent.id});
      },
    },
  }),
});

module.exports = {BookType, AuthorType};

这是我导入到我的 schema.js 文件中的文件,它被根查询和突变使用:

const {
  GraphQLObjectType,
  GraphQLString,
  GraphQLSchema,
  GraphQLID,
  GraphQLInt,
  GraphQLList,
  GraphQLNonNull,
} = require('graphql');
const Book = require('../../../models/book');
const Author = require('../../../models/author');
const {BookType, AuthorType} = require('../types');

// QUERIES
//------------------------------------------------------------------------------------------------------
const RootQuery = new GraphQLObjectType({
  name: 'RootQueryType',
  fields: {
    book: {
      type: BookType,
      args: { id: { type: GraphQLID } },
      resolve: (parent, args) => {
        // code to get data from db
        return Book.findById(args.id);
      },
    },
    author: {
      type: AuthorType,
      args: { id: { type: GraphQLID } },
      resolve: (parent, args) => {
        // code to get data from db
        return Author.findById(args.id);
      },
    },
    books: {
      type: new GraphQLList(BookType),
      resolve: (parent, args) => {
        // code to get data from db
        return Book.find({});
      },
    },
    authors: {
      type: new GraphQLList(AuthorType),
      resolve: (parent, args) => {
        // code to get data from db
        return Author.find({});
      }
    },
  },
});

// MUTATIONS
//------------------------------------------------------------------------------------------------------
const Mutation = new GraphQLObjectType({
  name: 'Mutation',
  fields: {
    addAuthor: {
      type: AuthorType,
      args: {
        name: { type: new GraphQLNonNull(GraphQLString) },
        age: { type: new GraphQLNonNull(GraphQLInt) }
      },
      resolve(parent, args) {
        let author = new Author({
          name: args.name,
          age: args.age
        });
        return author.save();
      }
    },
    addBook: {
      type: BookType,
      args: {
        name: { type: new GraphQLNonNull(GraphQLString) },
        genre: { type: new GraphQLNonNull(GraphQLString) },
        authorId: { type: new GraphQLNonNull(GraphQLID) },
      },
      resolve(parent, args) {
        let book = new Book({
          name: args.name,
          genre: args.genre,
          authorId: args.authorId,
        });
        return book.save();
      },
    },
  }
});

module.exports = new GraphQLSchema({
  query: RootQuery,
  mutation: Mutation,
});

但随着项目的发展,我预计会有数十种类型具有大量的双向关系。所以我想将我的所有类型模块化到单独的文件中,例如types/BookType.jstypes/AuthorType.js等,而不是像我现在拥有的单个types/index.js 。考虑到双向关系,实现这一目标的最佳方法是什么?

标签: graphql

解决方案


在将类型分离到单独的文件中时,您需要处理双向关系。在这种情况下,AuthorType 需要 BookType,反之亦然。因此,您需要在 types/BookTypes.js 中导入 AuthorType,在 types/AuthorType.js 中导入 BookType,但这会引入一个经典的循环依赖问题(在 AuthorType 导出之前它需要 BookType,反之亦然),这在节点项目中很常见。你可以在这里阅读更多关于它的信息。要处理此问题,请在两种类型的文件末尾转移您的 require 调用。所以你的代码看起来有点像这样:

类型/BookType.js

const graphql = require('graphql');
const Book = require('../../../models/book');
const Author = require('../../../models/author');

const {
  GraphQLObjectType,
  GraphQLString,
  GraphQLSchema,
  GraphQLID,
  GraphQLInt,
  GraphQLList,
  GraphQLNonNull,
} = graphql;

const BookType = new GraphQLObjectType({
  name: 'Book',
  fields: () => ({
    id: { type: GraphQLID },
    name: { type: GraphQLString },
    genre: { type: GraphQLString },
    author: {
      type: AuthorType,
      resolve: (parent, args) => {
        // code to get data from db
        return Author.findById(parent.authorId);
      },
    },
  }),
});

module.exports = BookType;

// This is here to prevent circular dependencies problem which will lead to the formation of infinite loop
const AuthorType = require("./AuthorType");

类型/AuthorType.js

const graphql = require('graphql');
const Book = require('../../../models/book');
const Author = require('../../../models/author');

const {
  GraphQLObjectType,
  GraphQLString,
  GraphQLSchema,
  GraphQLID,
  GraphQLInt,
  GraphQLList,
  GraphQLNonNull,
} = graphql;

const AuthorType = new GraphQLObjectType({
  name: 'Author',
  fields: () => ({
    id: {
      type: GraphQLID
    },
    name: {
      type: GraphQLString
    },
    age: {
      type: GraphQLInt
    },
    books: {
      type: new GraphQLList(BookType),
      resolve: (parent, args) => {
        // code to get data from db
        return Book.find({
          authorId: parent.id
        });
      },
    },
  }),
});

module.exports = AuthorType;

// This is here to prevent circular dependencies problem which will lead to the formation of infinite loop
const BookType = require("./BookType");

此外,最好有一个 types/index.js 作为导入/导出的处理程序。您将每种类型导出到 index.js 并在任何地方获取您想要的任何内容。这使您免于编写大量杂乱的代码,因为现在您可以执行以下操作:

const { BookType, AuthorType, OtherType } = require("../types/index");

推荐阅读