如何在Apollo Server中将类型定义和解析器拆分成单独的文件

15

index.ts:

  const server = new ApolloServer({
    typeDefs,
    resolvers,
    context: ({ req, res }: any) => ({ req, res })
  });

UserSchema.ts

export const typeDefs = gql`
  scalar TimeStamp
  type Query {
    getUser(id: Int!): User
  }
  type Mutation {
    addUser(
      name: String!
      email: String
      age: Int
      register_at: TimeStamp!
    ): Boolean!
  }
  type User {
    id: Int!
    name: String!
    email: String!
    age: Int!
    register_at: TimeStamp!
  }
`;

UserResolver.ts

export const resolvers = {
  TimeStamp: timeStamp,
  Query: {
    getUser: async (_: any, args: any) => {
      const { id } = args;

      return await User.findOne({ where: { id: id } });
    }
  },
  Mutation: {
    addUser: async (_: any, args: any) => {
      const { name, email, age, register_at } = args;
      try {
        const user = User.create({
          name,
          email,
          age,
          register_at
        });

        await user.save();

        return true;
      } catch (error) {
        return false;
      }
    }
  }
};

我想知道如果我有额外的类型定义和解析器,例如BookSchema.tsBookResolver.ts,该如何初始化我的Apollo Server实例。
2个回答

50

类型定义

ApolloServer 构造函数可以接受数组作为参数,而不仅仅是一个 DocumentNode 对象。所以您可以进行如下操作:

const server = new ApolloServer({
  typeDefs: [userTypeDefs, bookTypeDefs],
  resolvers,
})

请注意,如果您想将单个类型的字段定义拆分开来,则需要使用类型扩展语法。例如:

const typeDefsA = gql`
  type Query {
    users: [User!]!
  }
`
const typeDefsB = gql`
  extend type Query {
    books: [Book!]!
  }
`
const typeDefsC = gql`
  extend type Query {
    posts: [Post!]!
  }
`

以上内容将被合并成一个单独的Query类型。您可以有尽可能多的扩展,但是您要扩展的类型必须存在(即,您不能仅有三个extend type Query定义)。请谨记这一点,我通常会创建一个“基础”类型定义集,如下所示:

type Query

type Mutation

那么我所有的其他类型定义都可以扩展这些类型。请注意,因为这些“基础”类型没有任何字段,所以我们根本不使用花括号(一组空花括号会导致语法错误!)。

解析器

您的解析器映射是一个普通的 JavaScript 对象,因此将其拆分是微不足道的。

const resolversA = {
  Query: {
    users: () => {...},
  }
}

const resolversB = {
  Query: {
    books: () => {...},
  }
}

然而,如果你尝试使用 Object.assign 或展开语法来合并这些解析器映射,那么你会遇到麻烦,因为任何共同的属性(例如Query)都将被每个对象覆盖。所以不要这样做:

const resolvers = {
  ...resolversA,
  ...resolversB,
}

相反,你希望对对象进行深度合并,以便合并任何子属性(以及它们的属性等)。我建议使用lodash,但也可以使用其他实用程序。

const resolvers = _.merge({}, resolversA, resolversB)

将所有内容整合在一起

您的代码可能如下所示:

userTypeDefs.ts

export default gql`
type User {
  id: ID!
  username: String!
  books: [Book!]!
}

extend type Query {
  users: [User!]!
}
`

bookTypeDefs.ts

export default gql`
type Book {
  id: ID!
  title: String!
  author: User!
}

extend type Query {
  books: [Book!]!
}
`

用户解析器.ts

export default {
  Query: {
    users: () => {...},
  },
  User: {
    books: () => {...},
  },
}

bookResolvers.ts

export default {
  Query: {
    books: () => {...},
  },
  Book: {
    author: () => {...},
  },
}

index.ts

import userTypeDefs from '...'
import userResolvers from '...'
import bookTypeDefs from '...'
import bookResolvers from '...'

// Note: This is also a good place to put any types that are common to each "module"
const baseTypeDefs = gql`
  type Query
`

const apollo = new ApolloServer({
  typeDefs: [baseTypeDefs, userTypeDefs, bookTypeDefs],
  resolvers: _.merge({}, userResolvers, bookResolvers)
})

类型扩展仅在您想将单个类型(如QueryMutation)拆分成多个文件时才是必需的。您不能定义超过一次Query,但可以添加extend关键字将定义转换为扩展。 - Daniel Rearden
这里是参考规范 - Daniel Rearden
如果我想要分离typedef,我需要使用extend吗? - user13084267
那已经是一个例子了。它只是type Query没有字段 - 这样你就可以在所有其他文件中使用extend type Query。我更新了最终的例子,加入了更多的细节。希望这有所帮助。 - Daniel Rearden
非常感谢你,Daniel!截至今天(2022年5月28日),这是关于这个问题的最终答案。 - Leonardo Nobre Ghiggino
显示剩余4条评论

0

我不确定这是否是一个好的做法,但你可以这样做。


const typeDefA = `
   name: String!
   email: String!
   phone: String!
`

const RootTypeDef = gql`
    ${typeDefA}

    type Query {
        users: [User]
    }
`;

你可以将用户模式或任何其他模式取出并存储在普通变量中,然后像变量一样将其添加到根模式中。

请告诉我,这是否是良好的实践。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接