This video is available to students only

Building a GraphQL Resolver For Specific Fields

With the root-level `listing` field prepared in our GraphQL API, we'll construct the resolver function for this field to attempt to query for the appropriate listing from the "listings" collection in our database.

Building the Listing Resolvers#

With the root-level listing field prepared in our GraphQL API, we'll construct the resolver function for this field to attempt to query for the appropriate listing from the listings collection in our database. Similar to how the user query field queried for a user from our database with a certain ID, the listing query field will query for a certain listing based on the ID provided.

As a result, we'll update the listing field in our GraphQL type definitions and state it expects a defined argument of id of type GraphQL ID. In addition, the listing field when resolved should return the appropriate Listing GraphQL object.

This Listing GraphQL object has been created in the last module and has fields to describe the certain listing - such as it's title, description, host, etc.

listing()#

We'll now modify the resolver function for the listing field to state that it is to accept an id input from our client and return a Listing object when resolved. First, we'll construct the interface of the expected arguments for this field in a types.ts file kept within the src/graphql/resolvers/Listing folder. We'll create a ListingArgs interface that is to have an id field of type string.

In our listingResolvers map within the src/graphql/resolvers/Listing/index.ts file, we'll import a few things we'll need for our listing() resolver function. We'll first import the Database and Listing interfaces that have been defined in the src/lib/types.ts file. We'll also import the recently created ListingArgs interface from the types.ts file adjacent to this file.

We'll now update the listing() resolver function to query for a certain listing from the database.

  • We'll define the listing() resolver function as an async function.

  • In the listing() resolver function, we'll access the id argument passed in and the db object available in the context of our resolver.

  • When the listing() resolver function is to be complete, it should return a Promise that when resolved is an object of type Listing.

The listing() resolver function will be fairly straightforward to implement. We'll use Mongo's findOne() method to find a listing document from the listings collection where the _id field is the ObjectId representation of the id argument passed in. If this listing document doesn't exist, we'll throw a new Error. If the listing document does exist, we'll return the listing document that's been found. We'll have this implementation be kept in a try block while in a catch statement - we'll catch an error if ever to arise and have it thrown within a new error message.

listing() authorize#

The listing object contains a series of fields where we'll need to define explicit resolver functions for a certain number of them. In the last lesson, we mentioned that the bookings field within a listing object should be authorized and shown only to the user who owns the listing. When we define the resolver for the listing booking field, we'll need to check if the listing query is authorized.

We'll follow a similar format to what we did for the User module and simply get the viewer details with the authorize() function available in the src/lib/utils/ folder. Within the listing() resolver function, we'll have an if statement to check if the viewer id matches that of the listing host field which will determine the viewer is querying for their own listing. If this is true, we'll set an authorized field in the listing object to be true.

With that said, the first thing we'll do is add the authorized field to the Listing TypeScript interface in the src/lib/types.ts file and state that it is to be of type boolean when defined.

In our listingResolvers map file, we'll import the authorize() function from the src/lib/utils/ folder. We'll also import the Request interface from express.

In the listing() resolver function, we'll access the req object available as part of context in all our resolvers. Within the function, we'll have the authorize() function be run and pass in the db and req objects it expects, and we'll do this after the listing document has already been found. With the viewer obtained from the authorize() function, we'll then state that if viewer._id matches the listing.host field, we'll set the authorized value of the listing object to true.

With these changes, the listing() resolver function will be finalized as follows:

Note: The host field within the listing document object is an id of the host of the listing (i.e. the user who owns the listing).

We'll now create explicit resolver functions for the fields in the Listing object that we want to be resolved differently than the value being kept in the database. We already have the id() resolver set-up to resolve the _id of the listing document to an id string representation when queried from the client.

host()#

The host field in a listing document in the database is a string ID of the user that owns the listing. When the client queries for this field, we'll want the client to receive object information of the host. Since we want to resolve the host field to a User object value, we'll import the User interface from the src/lib/types.ts file.

We'll define a resolver function for the host() field in the Listing object within our listingResolvers map, and we'll use MongoDB's findOne() method to find the host from the listing.host id value.

Start a new discussion. All notification go to the author.