--- tags: [graphql] --- # Mutations with Apollo Client Queries are read-only operations. Mutations are write-only operations. Just like the `Query` type, the `Mutation` type serves as an entrypoint to the schema. ## Naming convention - Use verb: `add`, `create`, `delete` - Refer to the datatype For example `addSpaceCat(){}` ## Demonstration mutation We are going to create a mutation that increments the `numberOfViews` field on the `Track` type: ## Updating the schema ```js type Mutation { incrementTrackViews(id: ID!): IncrementTrackViewsResponse! } // Define a specific response type that specifically matches our needs type IncrementTrackViewsResponse { code: Int! // status code success: Boolean! // whether mutation was successful message: String! // what to say if mutation successful track: Track // not nullable because might error } ``` Based on this schema, the mutation will recieve a `Track` id and increment the specified `Track`. It will return an object comprising the newly updated `Track` and a bundle of properties that provide feedback on the status of the operations: a status code, whether it succeeded, and a message. ## Updating the data source Remember that our sole data source in the demonstration project is a REST API. We handle it within GraphQL using Apollos `RESTDataSource` class. We need to add a method to this class that will increment the track views. We wil use the `PATCH` REST method: ```js class TrackAPI extends RESTDataSource { constructor() {...} getTracksForHome() {...} getAuthor(authorId) {...} getTrack(trackId){...}; incrementTrackViews(trackId) { return this.patch(`track/${trackId}/numberOfViews`); } } ``` The `patch()` method is procided by the `RESTDataSouce` class that `TrackAPI` inherits from ## Adding resolver Next we need a resolver that corresponds to the mutation we have defined in the schema. We will need to handle successful responses as well as errors. ### Success case As always we match the shape of the schema: ```js const resolvers = { Query: { // ...query resolvers }, 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, }; }, }, }; ``` There's more going on with this resolver than the previous one. As is standard, we call the API using the `TrackAPI` class. However we don't just immediately return this when it executes. This is because the schema specifies that the return type `IncrementTrackViewsResponse` requires more than just the updated `Track`. So we wait this and return it with the cluster of metadata about the mutation response (`code`, `success`, and `message`). ### Error case We can extend the Mutation resolver to allow for errors. We'll do this by refactoring the resolver into a `try...catch` block and adding the error handling in the `catch`. We'll harness the details that are provided by Apollos' own `err` object which is returned by the `RESTDataSource` class that our resolver ultimately traces back to: ```js const resolvers = { Query: { // ...query resolvers } Mutation: { incrementTrackViews: async (_, {id}, {dataSources}) => { try { const track = await dataSources.trackAPI.incrementTrackViews(id); return { code: 200, success: true, message: `Successfully incremented number of views for track ${id}`, track }; } catch (err) { return { code: err.extensions.response.status, success: false, message: err.extensions.response.body, track: null }; } }, } } ``` ## The `useMutation` hook We invoke the `useMutation` hook to issue mutations from the client-side. As with queries and [query constants](Apollo_Client.md#query-constants) we wrap our mutation in a `gql` template string: ```js const INCREMENT_TRACK_VIEWS = gql` mutation IncrementTrackViews($incrementTrackViewsId: ID!) { incrementTrackViews(id: $incrementTrackViewsId) { code success message track { id numberOfViews } } } `; ``` We then pass it to the `useMutation` hook including an options object with our variables. (This time the specific variable is named): ```js import { gql, useMutation } from "@apollo/client"; useMutation(INCREMENT_TRACK_VIEWS, { variables: { incrementTrackViewsId: id }, }); ``` `useMutation` returns an array of two elements: 1. The mutation function that actually executes 2. An object comprising (`loading`, `error`, `data`) - this is the same as is the return value of `useQuery`. So we can destructure like so (we don't always need the second element); ```js const [incrementTrackViews, dataObject] = useMutation(INCREMENT_TRACK_VIEWS...) ``` Given that we can isolate the mutation function as the first destructured element of the array, we could then attach `incrementTrackViews` to a button or other frontend interaction.