Resolvers are a critical part of a GraphQL server. They are responsible for fetching the data for each field in a query. In this section, we will cover the following topics:

  1. What are Resolvers?
  2. How Resolvers Work
  3. Writing Basic Resolvers
  4. Resolver Arguments
  5. Context in Resolvers
  6. Practical Examples
  7. Exercises

  1. What are Resolvers?

Resolvers are functions that handle the logic for fetching the data for a specific field in a GraphQL query. They are the bridge between the schema and the data sources.

  1. How Resolvers Work

When a GraphQL query is executed, the server maps each field in the query to a resolver function. The resolver function then fetches the data and returns it to the client.

Example:

Consider the following GraphQL query:

{
  user(id: "1") {
    name
    email
  }
}

For this query, the server will use resolvers to fetch the user data and then the name and email fields of that user.

  1. Writing Basic Resolvers

Let's start by writing a basic resolver for a simple schema.

Schema Definition:

type Query {
  hello: String
}

Resolver Function:

const resolvers = {
  Query: {
    hello: () => 'Hello, world!',
  },
};

In this example, the hello field in the Query type is resolved by the hello function, which returns the string "Hello, world!".

  1. Resolver Arguments

Resolvers can accept four arguments:

  1. parent: The result of the previous resolver in the chain.
  2. args: An object containing the arguments passed to the field.
  3. context: An object shared by all resolvers in a GraphQL operation.
  4. info: Information about the execution state of the query.

Example:

Consider the following schema:

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

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

Resolver Function:

const users = [
  { id: '1', name: 'John Doe', email: '[email protected]' },
  { id: '2', name: 'Jane Doe', email: '[email protected]' },
];

const resolvers = {
  Query: {
    user: (parent, args, context, info) => {
      return users.find(user => user.id === args.id);
    },
  },
};

In this example, the user resolver uses the args argument to find and return the user with the specified id.

  1. Context in Resolvers

The context argument is useful for sharing data across all resolvers, such as authentication information, database connections, or other utilities.

Example:

const resolvers = {
  Query: {
    user: (parent, args, context, info) => {
      if (!context.user) {
        throw new Error('Not authenticated');
      }
      return context.db.users.find(user => user.id === args.id);
    },
  },
};

In this example, the context object contains the authenticated user and the database connection.

  1. Practical Examples

Example 1: Fetching Data from a Database

const resolvers = {
  Query: {
    user: async (parent, args, context, info) => {
      return await context.db.collection('users').findOne({ id: args.id });
    },
  },
};

Example 2: Using External APIs

const fetch = require('node-fetch');

const resolvers = {
  Query: {
    weather: async (parent, args, context, info) => {
      const response = await fetch(`https://api.weather.com/v3/wx/conditions/current?apiKey=${context.apiKey}&format=json`);
      return await response.json();
    },
  },
};

  1. Exercises

Exercise 1: Basic Resolver

Task: Write a resolver for the following schema that returns a greeting message.

type Query {
  greet(name: String!): String
}

Solution:

const resolvers = {
  Query: {
    greet: (parent, args) => {
      return `Hello, ${args.name}!`;
    },
  },
};

Exercise 2: Resolver with Context

Task: Write a resolver that fetches a user's profile only if the user is authenticated.

type Query {
  profile: User
}

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

Solution:

const resolvers = {
  Query: {
    profile: (parent, args, context) => {
      if (!context.user) {
        throw new Error('Not authenticated');
      }
      return context.db.users.find(user => user.id === context.user.id);
    },
  },
};

Conclusion

In this section, we learned about resolvers, how they work, and how to write basic and advanced resolvers. We also covered how to use arguments and context in resolvers. With this knowledge, you are now equipped to handle data fetching in your GraphQL server. In the next section, we will dive into GraphQL types and how to define them in your schema.

© Copyright 2024. All rights reserved