如何在Apollo服务器中把类型定义和解析器分成独立的文件[英] How to split type definitions and resolvers into separate files in Apollo Server

本文是小编为大家收集整理的关于如何在Apollo服务器中把类型定义和解析器分成独立的文件的处理/解决方法,可以参考本文帮助大家快速定位并解决问题,中文翻译不准确的可切换到English标签页查看源文。

问题描述

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.ts 和 BookResolver.ts,我想知道如何初始化我的 Apollo Server 实例.

推荐答案

类型定义

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!]!
}
`

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

本文地址:https://www.itbaoku.cn/post/1938145.html

问题描述

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

I would like to know how I would initialize my Apollo Server instance if I had additional type definitions and resolvers, for example BookSchema.ts and BookResolver.ts.

推荐答案

Type Definitions

The ApolloServer constructor can accept an array instead of just the one DocumentNode object. So you can do something like:

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

Note that if you want to split up an individual type's field definitions as well, you'll need to use type extension syntax. For example:

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

The above will be combined into a single Query type. You can have as many extensions as you want, but the type you're extending must exist (i.e., you can't have just three extend type Query definitions). Keeping this in mind, I usually create a "base" set of type definitions like:

type Query

type Mutation

Then all my other type definitions can extend these types. Notice that because these "base" types don't have any fields, we don't use curly brackets at all (an empty set of curly brackets will result in a syntax error!).

Resolvers

Your resolver map is a plain JavaScript object, so splitting it it up is trivial.

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

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

However, if you attempt to combine these resolver maps using Object.assign or spread syntax, you'll be hurting because any common properties (like Query) will be overridden by each object. So do not do this:

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

Instead, you want to deep merge the objects, so that any child properties (and their properties, and so on) are merged as well. I recommend using lodash but there's any number of utilities you can use.

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

Putting it all together

Your code might look something like this:

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