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

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.

Daniel Rearden
  • 80,636
  • 11
  • 185
  • 183

2 Answers2

49

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)
})
Daniel Rearden
  • 80,636
  • 11
  • 185
  • 183
  • I didn't quite understand the extension part, could you show me how i could by types and solve on same file ? –  Mar 18 '20 at 22:16
  • Not sure I understand what you're asking. What exactly do you still have questions about? – Daniel Rearden Mar 18 '20 at 22:27
  • basically i wanted to try to put my resolvers and types in a file that is userschema: types and resolvers: bookschema: types and resolvers but I was a little confused about extends –  Mar 18 '20 at 22:31
  • Type extensions are only necessary if you want to take a single type like `Query` or `Mutation` and split it across multiple files. You can't define `Query` more than once, but you can add the `extend` keyword to turn a definition into an extension. – Daniel Rearden Mar 18 '20 at 22:47
  • Here's the [spec](http://spec.graphql.org/June2018/#sec-Object-Extensions) for reference. – Daniel Rearden Mar 18 '20 at 22:48
  • in case if I want to separate the typedefs do I need to use extend? –  Mar 18 '20 at 23:17
  • I think you understand, you could just give me an example of this: I usually create a "base" set of type definitions like: –  Mar 18 '20 at 23:23
  • That is the example already. It's just `type Query` with no fields -- that way you can use `extend type Query` in all other files. I updated the final example with additional details. Hopefully that helps. – Daniel Rearden Mar 18 '20 at 23:32
  • Thank you so freaking much, Daniel! This is THE definitive answer to this problem as of today (2022-05-28) – Leonardo Nobre Ghiggino May 28 '22 at 10:28
0

I dont know, if it is good way of doing this, but you can do it like this.


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

const RootTypeDef = gql`
    ${typeDefA}

    type Query {
        users: [User]
    }
`;

you can just take out user schema or any other schema and store it in normal variable, then add it like a variable in root schema.

Please let me know, whether it is good practice or not.