SyntaxStudy
Sign Up
GraphQL Resolver Function Anatomy
GraphQL Beginner 1 min read

Resolver Function Anatomy

Every field in a GraphQL schema is backed by a resolver function. The execution engine calls each resolver with four arguments: parent (the resolved value of the parent field), args (the field's arguments), context (a request-scoped object shared across all resolvers), and info (AST and schema metadata for advanced use cases). Default resolvers handle the common case where the field name matches a property on the parent object, which is why you rarely need to write resolvers for every leaf field. You only need custom resolvers for root fields (Query/Mutation), computed fields, or fields that require database or service calls.
Example
// Resolver signature: (parent, args, context, info) => value | Promise<value>

const resolvers = {
  // Root resolvers — no parent value
  Query: {
    user: async (_, { id }, { db, currentUser }) => {
      if (!currentUser) throw new Error('Unauthenticated');
      return db.users.findById(id);
    },
    posts: async (_, { authorId, limit = 20 }, { db }) => {
      return db.posts.findAll({ where: { authorId }, limit });
    },
  },

  // Type resolvers — parent is the resolved User object
  User: {
    // Computed field: not stored in DB
    fullName: (user) => `${user.firstName} ${user.lastName}`,

    // Relation: load posts for this user
    posts: async (user, { limit }, { db }) => {
      return db.posts.findAll({ where: { authorId: user.id }, limit });
    },

    // Hide sensitive data based on context
    email: (user, _, { currentUser }) => {
      if (currentUser?.id === user.id || currentUser?.role === 'ADMIN') {
        return user.email;
      }
      return null;
    },
  },

  Post: {
    // Resolve author from authorId stored on post
    author: (post, _, { db }) => db.users.findById(post.authorId),
    wordCount: (post) => post.body.split(/\s+/).length,
  },
};