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

How to split type definitions and resolvers into separate files in Apollo Server

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

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

类型定义

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 定义)。牢记这一点,我通常会创建一组 "base" 类型定义,例如:

type Query

type Mutation

然后我所有其他类型定义都可以扩展这些类型。请注意,因为这些 "base" 类型没有任何字段,所以我们根本不使用花括号(空的花括号集会导致语法错误!)。

解析器

您的解析器映射是一个普通的 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!]!
}
`

userResolvers.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)
})

我不知道这样做是否好,但你可以这样做。


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

const RootTypeDef = gql`
    ${typeDefA}

    type Query {
        users: [User]
    }
`;

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

请告诉我,这是否是好的做法。