14. Mutation response
5m

Overview

So far, we've only been working with one type of : queries. These are read-only operations to retrieve data. To modify data, we need to use another type of GraphQL operation: , which are write operations.

In this lesson, we will learn about common conventions for designing and mutation responses

Mutations

On to the next feature in our MusicMatcher project: adding tracks to an existing playlist.

Let's take a look at the corresponding REST API method that enables this feature: POST /playlists/{playlist_id}/tracks.

https://spotify-demo-api-fe224840a08c.herokuapp.com/v1/docs

Mock REST API with POST endpoint

From the documentation, we need the following parameters:

  • playlist_id - The ID of the playlist, as a string (required)
  • position - An integer, zero-indexed, where we want to insert the track(s)
  • uris - A comma-separated string of uri values corresponding to the tracks we want to add

The method then returns an object with a snapshot_id property that represents the state of the playlist at that point in time.

All right, now how do we enable this functionality in ?

Designing mutations

Let's start with our schema.

For names, we recommend starting with a verb that describes the specific action of our update (such as add, delete, or create), followed by whatever data the mutation acts on.

For the return type of a , we could return the the mutation is acting on. However, we recommend following a consistent Response type for responses.

Mutation responses

We'll need to account for any partial errors that might occur and return helpful information to the client. We recommend adding three common to all responses:

  • code: an int that refers to the status of the response, similar to an HTTP status code.
  • success: a bool flag that indicates whether all the updates the was responsible for succeeded.
  • message: a string to display information about the result of the on the client side. This is particularly useful if the mutation was only partially successful and a generic error message can't tell the whole story.

Then, we'll also have a for the we're mutating (Playlist, in our case). In certain , this could be multiple objects!

As for naming conventions, return types usually end with the word Payload or Response.

The AddItemsToPlaylistPayload type

Following the best practices we covered, we'll name our AddItemsToPlaylist. Let's first create the return type for this : AddItemsToPlaylistPayload.

Under api/types, we'll create a new file called api/types/add_items_to_playlist_payload.py.

Inside, we'll create the AddItemsToPlaylistPayload class, applying the @strawberry.type decorator.

api/types/add_items_to_playlist_payload.py
import strawberry
@strawberry.type
class AddItemsToPlaylistPayload:
...

Then, we'll add the properties for code, success and message.

api/types/add_items_to_playlist_payload.py
code: int = strawberry.field(description="Similar to HTTP status code, represents the status of the mutation.")
success: bool = strawberry.field(description="Indicates whether the mutation was successful.")
message: str = strawberry.field(description="Human-readable message for the UI.")

The object we're mutating in this case is a Playlist type, which is nullable because it's possible that something can go wrong in the !

api/types/add_items_to_playlist_payload.py
playlist: Playlist | None = strawberry.field(description="The playlist that contains the newly added items.")

We'll also need to import the Playlist type at the top of the file:

api/types/add_items_to_playlist_payload.py
from .playlist import Playlist

A new entry point: Mutation

Under api, we'll create a new file called mutation.py. In this file, we'll define a new class called Mutation:

api/mutation.py
import strawberry
@strawberry.type
class Mutation:
...

Next, we'll write the function for our AddItemsToPlaylist . It will return the AddItemsToPlaylistPayload type. We'll add a description for it too.

We'll keep the function in Pythonic snake_case, but remember that Strawberry will automatically convert it to the camelCase in the schema.

api/mutation.py
def add_items_to_playlist(self) -> AddItemsToPlaylistPayload:
...

To define this as a , we'd usually use the @strawberry.field decorator, but this time, we're going to use a new function: strawberry.mutation.

The strawberry.mutation defines a for a mutation. It works in the same way as strawberry.field. We can apply it as a decorator and provide a description.

api/mutation.py
@strawberry.mutation(description="Add one or more items to a user's playlist.")
def add_items_to_playlist(self) -> AddItemsToPlaylistPayload:
...

We'll hard-code the results of the for now. Small steps! We'll create a new AddItemsToPlaylistPayload instance, passing in 200 for code, true for the success status, a successful message and a hard-coded Playlist object.

api/mutation.py
return AddItemsToPlaylistPayload(
code=200,
success=True,
message="Successfully added items to playlist.",
playlist=Playlist(
id="6Fl8d6KF0O4V5kFdbzalfW",
name="Sweet Beats & Eats",
description=None,
),
)

Lastly, we'll import the AddItemsToPlaylistPayload and Playlist types at the top of our file:

api/mutation.py
from .types.add_items_to_playlist_payload import AddItemsToPlaylistPayload
from .types.playlist import Playlist

Our needs to know about this new entry point to the schema. Much like the Query type, we'll need to set the type in our schema.

Open up the api/schema.py file and import the Mutation class at the top.

api/schema.py
from .mutation import Mutation

Then, we'll pass it into strawberry.Schema.

api/schema.py
schema = strawberry.Schema(query=Query, mutation=Mutation)

Save all these changes and restart the server.

Explorer time!

Time to take our for a spin! Back in Sandbox Explorer, we'll create a new workspace tab.

In the Documentation panel, let's navigate back to the root of our schema. Beside Query, we can see the Mutation type.

http://localhost:8000

Explorer - mutation added

Let's add the addItemsToPlaylist and all the subfields inside it. For the playlist sub, select the name for now.

GraphQL operation
mutation AddItemsToPlaylist {
addItemsToPlaylist {
code
message
success
playlist {
name
}
}
}

Run the .

http://localhost:8000

Explorer - mutation response

Response data
{
"data": {
"addItemsToPlaylist": {
"code": "200",
"message": "Added items to playlist successfully",
"success": true,
"playlist": {
"name": "Sweet Beats & Eats"
}
}
}
}

Much like responses, our response followed the same shape as our mutation!

Practice

Which of these are good names for mutations based on the recommended conventions above?
In the mutation response type (AddItemsToPlaylistPayload), why is the modified object's return type (Playlist) nullable?

Key takeaways

  • are write used to modify data.
  • Naming usually starts with a verb that describes the action, such as "add," "delete," or "create."
  • It's a common convention to create a consistent response type for responses.

Up next

We'll learn best practices for inputs and return real data.

Previous

Share your questions and comments about this lesson

This course is currently in

beta
. 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.