Connecting Schemas and Resolvers

Apollo Server connects a GraphQL schema with resolver implementations.

Consider this schema:

type Query {
  hello: String!
  user(id: ID!): User
}

type User {
  id: ID!
  name: String!
}

The resolver map can mirror the schema structure:

const resolvers = {
  Query: {
    hello: () => "Hello GraphQL",
    user: (_, { id }, context) => {
      return context.userService.findById(id);
    }
  }
};

The Query property corresponds to the root Query type. The hello and user properties correspond to fields on that type.

Nested types can have their own resolver maps:

const resolvers = {
  Query: {
    user: (_, { id }, context) => context.userService.findById(id)
  },
  User: {
    fullName: (user) => `${user.firstName} ${user.lastName}`
  }
};

The resolver map therefore acts as a runtime implementation of the schema.

A useful rule is to keep schema definitions descriptive and resolver implementations focused. Avoid putting database queries, authentication parsing, validation, transformation, and business workflows into a single resolver function.

Schema and Resolver Map
const typeDefs = `
  type User {
    id: ID!
    name: String!
  }

  type Query {
    user(id: ID!): User
  }
`;

const resolvers = {
  Query: {
    user: (_, { id }, context) => {
      return context.userService.findById(id);
    }
  }
};
💡 The Query.user resolver receives the requested ID and delegates data retrieval to an application service. The GraphQL layer therefore remains separate from the underlying persistence implementation.
Key Takeaways
  • Resolver maps mirror GraphQL schema types and fields.
  • Root operations are implemented under Query, Mutation, and Subscription.
  • Nested object types can have their own resolver maps.
  • Keep resolver functions focused on orchestration.
Knowledge Check

Where is the resolver for a field named `user` on the root `Query` type normally placed?