Labs ICT
Pro Login

Introduction to Resolvers

Introduction to Resolvers

Resolvers are the functions that actually fetch the data for your GraphQL queries. While the schema defines what data is available, resolvers define how to get that data.

Think of the schema as a menu and resolvers as the kitchen staff. The menu tells you what you can order, but the kitchen staff actually prepares the food.

Resolver Structure

Each resolver function receives four arguments:

const resolvers = {
  Query: {
    // parent: The return value of the parent resolver
    // args: The arguments passed to this field
    // context: Shared data across all resolvers (db, auth, etc.)
    // info: Metadata about the query (field name, schema, etc.)
    user: (parent, args, context, info) => {
      return context.db.users.find(user => user.id === args.id);
    },
  },
};

The most commonly used arguments are args (for query parameters) and context (for database connections and authentication).

Resolver for Each Field

You can write resolvers for individual fields, not just root queries. This lets you break down complex data fetching into smaller pieces.

const resolvers = {
  Query: {
    user: (parent, args, context) => {
      return context.db.users.find(u => u.id === args.id);
    },
  },
  User: {
    // This resolver runs whenever someone queries the posts field on User
    posts: (parent, args, context) => {
      // parent is the User object
      return context.db.posts.filter(post => post.authorId === parent.id);
    },
    // This resolver computes the full name
    fullName: (parent) => {
      return `${parent.firstName} ${parent.lastName}`;
    },
  },
};

The User resolver for posts takes the parent user and finds all their posts. The fullName resolver combines first and last names.

The Context Object

The context is where you put shared resources that resolvers need. Common things to include are database connections, authentication info, and data loaders.

const { ApolloServer } = require('@apollo/server');

const server = new ApolloServer({
  typeDefs,
  resolvers,
});

// Create context for each request
const context = async ({ req }) => {
  const token = req.headers.authorization || '';
  const user = await getUserFromToken(token);
  
  return {
    db: database,
    user: user,
    loaders: createDataLoaders(),
  };
};

// Access context in resolvers
const resolvers = {
  Query: {
    me: (parent, args, context) => {
      if (!context.user) {
        throw new Error('Not authenticated');
      }
      return context.user;
    },
  },
};

The context is created fresh for each request, so each user gets their own database connection and authentication state.

Resolver Execution Order

GraphQL executes resolvers in a specific order. First, the root Query resolver runs. Then, for each field in the query, the corresponding resolver runs.

query {
  user(id: "1") {
    name
    posts {
      title
    }
  }
}

// Execution order:
// 1. Query.user resolver (fetches user)
// 2. User.name resolver (returns name field)
// 3. User.posts resolver (fetches user's posts)
// 4. Post.title resolver (returns title field)

Understanding this order helps you optimize your resolvers and avoid unnecessary database queries.