GraphQL resolvers

GraphQL has three predefined ROOT RESOLVERS, called Query, Mutation, and Subscription.

all other resolvers sit within these three categories to do business logic things.

resolvers are like Redux's reducers, they are the ONE AND ONLY ONE place to write logic between your result param (from your database) and your graphql response (what you send back from your GraphQL API).

this is where you make connections to the database and do crud

this is where you do your api requests if you need them

it is the actual implementation of the GraphQL schema.

and this set of rules are enforced by whatever GraphQL engine your code is running on, to make sure everybody holds up their end of the deal.

Query and Mutation

Query and Mutation Function signatures go like this:

const someQuery = (parent, args, context, info) => {
  // code the get the value
  return value
}

it looks like this:

const resolvers = {
  // Query, Mutation and Subscription keys are reserved, graphql knows what to do with these
  Query: {
    someQuery: someQuery, // from above
    info: () => `this is the API endpoints of a Hacker News Clone`,
    feed: () =>
      // your logic to make querying for the feed happen
      [...DB.entries()].map(entry => ({
        id: entry[0],
        ...entry[1],
      })),
    link: (parent, args) => {
      // your logic for making querying for a link by id happen
      return DB.get(args.id)
    },
  },
  Mutation: {
    createLink: (parent, args) => {
      // your logic to make post happen
      const id = `link-${idCount++}`
      const link = {
        description: args.description,
        url: args.url,
      }
      DB.set(id, link)
      return { id, ...link }
    },
    updateLink: (parent, args) => {
      // your logic to make updateLink happen
      if (DB.has(args.id)) {
        const original = DB.get(args.id)
        DB.addToDB(
          args.id,
          args.url || original.url,
          args.description || original.description
        )
        return DB.get(args.id)
      } else {
        return null
      }
    },
    deleteLink: (parent, args) => {
      // your logic to make deleteLink happen
      if (DB.has(args.id)) {
        const data = {
          id: args.id,
          ...DB.get(args.id),
        }
        DB.delete(args.id)
        return data
      } else {
        return null
      }
    },
  },
}

Subscription

Subscription Function signatures go like this:

const someSubscription = {
  subscribe: (parent, args, context, info) => {
    // code the get an Async Iterator object
    return AsyncIteratorThing
  },
  resolve: (payload) => {
    return payload
  }
}
const resolvers = {
  Query: {}, // ...
  Mutation: {}, // ...
  Subscription: {
    someSubscription: someSubscription
  }
}

Resolvers for subscriptions are slightly different than the ones for queries and mutations:

Rather than returning any data directly, they return an AsyncIterator which subsequently is used by the GraphQL server to push the event data to the client. Subscription resolvers are wrapped inside an object and need to be provided as the value for a subscribe field. You also need to provide another field called resolve that actually returns the data from the data emitted by the AsyncIterator.

Params

the following is taken from the ref article and then extended with my notes.

A resolver function takes four arguments (in that order):

  1. parent: The result of the previous resolver call (more info).
    • returned stuff from the previous resolver call (because it can be nested and chained)
  2. args: The arguments of the resolver’s field.
    • input params
  3. context: A custom object each resolver can read from/write to.
    • ~ global state for resolvers
  4. info: An AST representation of the query or mutation.
    • WIP
    • for now seems like its used by libraries to add stuff to your GraphQL schema (like count, where, orderBy, etc...)
    • used when resolving with context param

like so:

const resolvers = {
  Query: {
    feed: (parent, args, context, info) => {
      // your logic for returning a list of links
    },
  },
}

good point

Why was the 'User' type redefined in the application schema when it's already part of the Prisma database schema and could be imported from there?

ref