Please explain where "user.role" is coming from

In Airlock’s case, each user in the accounts database has a role attribute that is set to either “Host” or “Guest”. This is perfect to use, because the two types that implement this interface are also Host or Guest.

  • The first argument, obj, is the object returned by the resolver for the field returning the interface.

I do not understand how this is available in the object returned by the resolver. The ‘User’ interface has id, name, and profilePicture, but no role. The only place I see role defined is in userInfo = { userId: data.id, userRole: data.role };

I see the description says it’s located in the database, but then why don’t we add this value onto the ‘User’ interface? I’m so confused here… sorry, hopefully this makes sense.

Ok wait, I think I’ve maybe got it… but please correct me if I’m wrong:

  getUser(userId) {
    return this.get(`user/${userId}`);
  }

This is the result that is being returned in the User resolver?

So basically, if I were to run the user query user(id: ID!): User, this in turn calls await dataSources.accountsAPI.getUser(id);, and because that returns the User from the database (or in this case REST endpoint [in turn calling the DB]) the User resolver is then called??? which in turn calls the __resolveType method on the returned User object from the accountsAPI?

What a chain…

Great question! You’re on the right track.

To use the example you mentioned, let’s imagine the client is sending this request to our GraphQL server:

query {
  user(id: "user-1") {
    name
  }
}

The GraphQL server is going to resolve the fields in this request one at a time, starting from the top with the user(id: "user-1").

The server calls the Query.user() resolver, which calls the AccountsAPI using the getUser() method you mentioned. This calls the locally running accounts service, which returns an object from the database with info about the user. This object gets returned all the way back up to the GraphQL server, the same way it normally would.

But now the server moves on to resolve the next field: name. And it gets stuck, because the user(id) field returns a User type, which is an interface, not a resolvable type. So in order to figure out whether to use the Host or Guest resolvers, the server calls the User.__resolveType() resolver.

In our case, User.__resolveType() returns the .role field on the user object, which comes from the database already set to either “Host” or “Guest”. (For this example it’s set to “Host”.) Once the server knows what kind of object it’s working with, it knows which set of resolvers to use for the rest of the fields inside that part of the query.

This might not seem super useful for this particular example (since both Hosts and Guests resolve the name field in the same way), but it makes a bigger difference once you have query fragments, which include fields that will only be resolved by objects of a certain type. (More on that in the next lesson in that side quest!)

Hope that explanation helps. Feel free to reach out if you have any other questions!

2 Likes