Odyssey

Lift-off IV: Mutations

Feature OverviewWhat is a mutation?Adding a mutation to our schemaUpdating our TrackAPI data sourceResolving a mutation successfullyResolving a mutation with errorsTesting a mutation in the ExplorerThe useMutation hookOur mutation in the browser
5. Resolving a mutation successfully
3m

đź§Ş Mutation resolvers

We've set up our data source to make a PATCH call to our REST API. Now it's time to write resolvers to put our data source to work.

In the server/src folder, let's open up the resolvers.js file. We need to add another entry to our resolvers object for our mutation.

We can add it just below our Query resolvers, and we need to give it the same name as in our schema, which is Mutation (note the capital M!).

const resolvers = {
Query: {
// ... query resolvers
},
Mutation: {
// where our new resolver function will go
},
};

Inside this Mutation object, we'll add the resolver function for our mutation, which also needs to have the same name as in our schema incrementTrackViews. And of course, let's add a helpful comment at the top.

// increments a track's numberOfViews property
incrementTrackViews: (parent, args, contextValue, info) => {},
Which of these are true about the structure of the resolvers object?

We won't need the parent argument in this resolver, because it's for a root field in our schema. By convention, we'll just add an underscore. We do need to destructure the second parameter args to retrieve the id of the track we're updating. As usual, we'll also destructure the third parameter contextValue for the dataSources object. And finally, we have no need for the fourth parameter info.

Here's what our resolver signature looks like now:

server/src/resolvers.js
incrementTrackViews: (_, { id }, { dataSources }) => {
// where we'll call the TrackAPI
},

In the body of the resolver, we want to call the method we added to our TrackAPI class. As we saw in Lift-off II, we get access to an instance of that class, trackAPI, through the dataSources object because we configured it in the Apollo Server options.

So inside the incrementTrackViews resolver body, we'll call dataSources.trackAPI.incrementTrackViews, passing it the id from args.

dataSources.trackAPI.incrementTrackViews(id);

So far, we've always immediately returned the results of our TrackAPI calls, because the results match the structure required by our schema.

However, in this case, we have three fields required by our schema that are not present in the returned response: code, success and message. This makes sense because they are tied to the REST operation status itself, so we need to set those three based on that status.

Why can't this resolver immediately return the results of the TrackAPI call in this case?

đź“„ Fulfilling the schema requirements

First, let's wait for the TrackAPI call to finish returning the data. We can do this by making the function async, then storing the results in a variable called track, making sure to await the results.

incrementTrackViews: async (_, {id}, {dataSources}) => {
const track = await dataSources.trackAPI.incrementTrackViews(id);
},

Next, let's return an object with all the properties needed to fulfill our schema. For now, we'll assume that the API call was successful, and worry about the error handling in the next lesson.

return {
code: 200,
success: true,
message: `Successfully incremented number of views for track ${id}`,
track,
};

So here the code will return 200, the success property will be set to true, and we can return message to show that the number of views was successfully incremented for this specific track ID. And finally, we can return the modified object, track.

In the end, the resolvers object should have a new entry that looks like this:

Mutation: {
// increments a track's numberOfViews property
incrementTrackViews: async (_, { id }, { dataSources }) => {
const track = await dataSources.trackAPI.incrementTrackViews(id);
return {
code: 200,
success: true,
message: `Successfully incremented number of views for track ${id}`,
track,
};
},
},
Code Challenge!

Add a resolver for the new assignSpaceship mutation provided in the schema, using arrow function syntax. Use the dataSources.spaceAPI class and its method assignSpaceshipToMission, which takes the spaceshipId and the missionId as arguments (in that order). This method returns an object with the newly updated spaceship and mission. Follow the schema requirements to return an object for the successful result. code should be 200, success should be true and message should say Successfully assigned spaceship ${spaceshipId} to mission ${missionId}

Loading...
Loading editor

All right, we've got our successful case resolved, but what happens when our TrackAPI throws an error? Let's take care of this in the next lesson.

Previous
Next

Share your questions and comments about this lesson

Your feedback helps us improve! If you're stuck or confused, let us know and we'll help you out. All comments are public and must follow the Apollo Code of Conduct. Note that comments that have been resolved or addressed may be removed.

You'll need a GitHub account to post below. Don't have one? Post in our Odyssey forum instead.

              resolvers

              A function that populates data for a particular field in a GraphQL schema. For example:

              const resolvers = {
              Query: {
              author(root, args, context, info) {
              return find(authors, { id: args.id });
              },
              },
              };
              mutation

              A GraphQL operation that modifies data on the server. It allows clients to perform create, update, or delete operations, altering the underlying data.

              resolvers

              A function that populates data for a particular field in a GraphQL schema. For example:

              const resolvers = {
              Query: {
              author(root, args, context, info) {
              return find(authors, { id: args.id });
              },
              },
              };
              resolver

              A function that populates data for a particular field in a GraphQL schema. For example:

              const resolvers = {
              Query: {
              author(root, args, context, info) {
              return find(authors, { id: args.id });
              },
              },
              };
              mutation

              A GraphQL operation that modifies data on the server. It allows clients to perform create, update, or delete operations, altering the underlying data.

              argument

              A key-value pair associated with a particular schema field that lets operations pass data to that field's resolver.

              Argument values can be hardcoded as literal values (shown below for clarity) or provided via GraphQL variables (recommended).

              query GetHuman {
              human(id: "200") {
              name
              height(unit: "meters")
              }
              }
              resolver

              A function that populates data for a particular field in a GraphQL schema. For example:

              const resolvers = {
              Query: {
              author(root, args, context, info) {
              return find(authors, { id: args.id });
              },
              },
              };
              field

              A unit of data that belongs to a type in a schema. Every GraphQL query requests one or more fields.

              type Author {
              # id, firstName, and lastName are all fields of the Author type
              id: Int!
              firstName: String
              lastName: String
              }
              resolver

              A function that populates data for a particular field in a GraphQL schema. For example:

              const resolvers = {
              Query: {
              author(root, args, context, info) {
              return find(authors, { id: args.id });
              },
              },
              };
              resolver

              A function that populates data for a particular field in a GraphQL schema. For example:

              const resolvers = {
              Query: {
              author(root, args, context, info) {
              return find(authors, { id: args.id });
              },
              },
              };
              Apollo Server

              An open-source library for server-side GraphQL operation handling. It can be used as a monolithic GraphQL server or a subgraph server within a supergraph.

              resolver

              A function that populates data for a particular field in a GraphQL schema. For example:

              const resolvers = {
              Query: {
              author(root, args, context, info) {
              return find(authors, { id: args.id });
              },
              },
              };
              fields

              A unit of data that belongs to a type in a schema. Every GraphQL query requests one or more fields.

              type Author {
              # id, firstName, and lastName are all fields of the Author type
              id: Int!
              firstName: String
              lastName: String
              }
              operation

              A single query, mutation, or subscription that clients send to a GraphQL server to request or manipulate data.

              variable

              A placeholder for dynamic values in an operation allowing parameterization and reusability in requests. Variables can be used to fill arguments or passed to directives.

              query GetUser($userId: ID!) {
              user(id: $userId) {
              firstName
              }
              }

              In the query above, userId is a variable. The variable and its type are declared in the operation signature, signified by a $. The type of variable is a non-nullable ID. A variable's type must match the type of any argument it's used for.

              NEW COURSE ALERT

              Introducing Apollo Connectors

              Connectors are the new and easy way to get started with GraphQL, using existing REST APIs.

              Say goodbye to GraphQL servers and resolvers—now, everything happens in the schema!

              Take the course