This lesson is for PRO members.

Unlock this lesson NOW!
Already subscribed? sign in

Use Arguments in a GraphQL Query

3:58 JavaScript lesson by

In GraphQL, every field and nested object is able to take in arguments of varying types in order to do common operations like fetching an object by it's ID, filtering, sorting, and more. In this video, we'll update a field to take in an id argument and then learn how to use that argument in our resolve method to fetch a video by its id.

Get the Code Now
click to level up

egghead.io comment guidelines

Avatar
egghead.io

In GraphQL, every field and nested object is able to take in arguments of varying types in order to do common operations like fetching an object by it's ID, filtering, sorting, and more. In this video, we'll update a field to take in an id argument and then learn how to use that argument in our resolve method to fetch a video by its id.

Avatar
David

Ran into a confusing error: getVideoById is not a function

Author was using es6 exports in getting-started/sr/data/index.js

If you run into this, just use normal node exports:

module.exports = {
    getVideoById: getVideoById
};
Avatar
Josh Black

Hi there David! Wanted to say sorry for the confusion.

The syntax used for getVideoById doesn't actually leverage ES2015/ES6 Named Exports, it actually is using a default Node.js feature, similar to module.exports which you mentioned above. Since we are assigning the value of getVideoById explicitly on exports, the way that we import it changes.

For example:

// a.js
const b = require('./b');

b.foo();

// OR
const { foo } = require('./b');

b.foo();
// b.js
exports.foo = function foo() {
  // ...
}

Hope that helps!

In reply to David
Avatar
Austin Witherow

Thanks for the awesome tutorial :) Super useful that it was the one released today, as I am having to dive into some graphql stuff at work!

I am getting an error in graphiql, when I type in...

{
  video(id: "a") {
    title
  }
}

It says to me

{
  "errors": [
    {
      "message": "Unknown argument \"id\" on field \"video\" of type \"QueryType\".",
      "locations": [
        {
          "line": 2,
          "column": 8
        }
      ]
    }
  ]
}

My querytype looks correct from what I can see?

const queryType = new GraphQLObjectType({
  name: 'QueryType',
  description: 'the root query type',
  args: {
    id: {
      type: GraphQLID,
      description: 'id of the video'
    }
  },
  fields: {
    video: {
      type: videoType,
      resolve: (_, args) => getVideoById(args.id)
    }
  }
})

Any ideas?

Austin

Avatar
Austin Witherow

I figured it out. I accidentally put the args in under the GraphQLObjectType queryType instead of within the fields for video :)

In reply to Austin Witherow
Avatar
Josh Black

Glad you figured it out! 😄 Sorry if there was any confusion!

In reply to Austin Witherow
Avatar

When using schema language, it seems that the args are the FIRST argument passed to my resolver. I noticed in the docs it says that the function signature for a resolver is usually (obj, args, context) => {...}

obj The previous object, which for a field on the root Query type is often not used.

Could you elaborate on this? It seems strange that the arguments passed to the resolver fn could be different.

Avatar
Josh Black

Hi there! First off, wanted to say thanks for watching.

It seems strange that the arguments passed to the resolver fn could be different.

That's a great point, and I think the reason for that only comes from when you have a field that needs to be fetched/computed outside of the initial resolve function.

Say we have the following schema:

type User {
  name: String
  friends: [User]
}

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

Our resolve function for the user field will probably look like:

resolve: (rootObject, args, context) => {
  /**
   * Some promise-returning function that returns a user-object,
   * for example: { id: 'abcd', name: 'Josh Black', friends: [1, 2, 3] }
   * where `friends` is an array of ids
   */
  return userLoader.load(args.id);
}

This resolve function will satisfy the name part of the User type, however it's not sure how to handle the friends bit. We can try and pre-load the friends in this initial call, and that will definitely work out, or we can delegate to the friends field on the User type to figure out how to resolve that for us.

In the latter case, we'll need access to object in order to get the list of friend ids. A resolve function for that might look like:

resolve: (user) => {
  return userLoader.loadMany(user.friends);
}

So since we have access to the current object, we can take fields off of that object in order to fully resolve the field.

Hope that helps! Feel free to ask more questions if that example didn't cover exactly what you were looking for.

In reply to
Avatar

Thank you very much for that explanation! Great course btw

In reply to Josh Black

Right now, we have this GraphQL schema that's being defined by a single query type, and inside of this query type, we have a single field called video, but we're resolving this field with a single object in a resolve statement.

While this is great for our examples, it's way more likely that we'll have a collection of videos like we have defined down here. Then when we're querying for a specific video, we want to get that video by its ID.

In GraphQL, we're actually able to pass arguments to fields by using the Args key. Inside of the Args, we give it the name of the argument, which in this case would be ID, its type, which would be GraphQL ID, and then a description, which in this case would be the ID of the video.

Now, when we go and run our server using node index.js and we actually go and query for that video field, we can see that we can add an argument right after the term video. The argument that we can pass in is ID and we'll pass in the ID of the video that we would want.

When we execute that, we still get the same video. Let's actually go and plug in some more diverse data to demonstrate how we could query by ID and resolve that in our resolve statement.

Let's hop back into our terminal and actually create a new directory. Now, we're going to have one called source/data and we'll just add a file inside of there called index.js.

Next up, we'll go and grab all of the video information that we have in this file, and we'll cut and paste it inside of that data/index.js file.

Finally, let's add a helper function called GetVideoByID. This is going to be a function that takes an ID and will return a new promise.

To implement this, we're going to go through and get the video after filtering through all the videos. For each video, what we're going to do is check to see if the current video ID matches the given ID. At the end, we'll just resolve with the video.

Finally, let's go and use exports.GetVideoByID to export our function. Then inside of our index.js file, let's go to the top and do const and GetVideoByID is equal to require, go into Source, and then Data.

Now that we have this request to help simulate fetching a video, let's go and update our resolve statement. Now, instead of resolving with a static object, instead, what we can do is write out our resolve as a function.

The first argument we don't care about, but the second one will contain all of the arguments that are being passed in to our resolve statement for our field.

We can use that inside of our function by doing return GetVideoByID, which will return a promise. Then, we'll pass in the argument.ID, which will be the specific ID for the video that we want.

Now, let's go into our command line and just restart our server by doing node index.js and then switching to our Chrome window and run the same request one more time.

Now, we get a video for the ID of A. We can change it to the ID B and make the same kind of request, and we'll get the other video. Now, we're fetching our videos by the given ID.

HEY, QUICK QUESTION!
Joel's Head
Why are we asking?