Handling Updates

Thanks for making it this far! We really appreciate the time you are spending to learn Houdini.

So far we’ve only covered how to read data from our server. Clearly this is only one part of the picture since most projects need to be able to update the server’s state.

Updating Field Values

Before we explain how to use mutations in Houdini, we need a way to visualize if a species is one of our favorites. To start, add the favorite to the route’s query. It should now look something like:

src/routes/[[id]]/+page.gql
query Info($id: Int! = 1) {
    species(id: $id) {
        name
        flavor_text
        favorite
        evolution_chain {
            ...SpeciesPreview
        }
        ...SpriteInfo
    }
}

Once you’ve added the field, add an import for Icon from the component directory and drop the following block of code at the bottom of the left panel:

src/routes/[[id]]/+page.svelte
<script lang="ts">
    import { Icon } from '~/components'
</script>

<button id="favorite">
    <Icon
        name="star"
        id="favorite-star"
        fill={$Info.data.species.favorite ? "gold" :"lightgrey"}
    />
</button>
src/routes/[[id]]/+page.svelte
<script>
    import { Icon } from '~/components'
</script>

<button id="favorite">
    <Icon
        name="star"
        id="favorite-star"
        fill={$Info.data.species.favorite ? "gold" :"lightgrey"}
    />
</button>

With that in place we can now define a function that will invoke the toggleFavorite mutation and pass it to our button. Add the declaration for toggleFavorite shown below to your script tag:

src/routes/[[id]]/+page.svelte
<script lang="ts">
    // ... everything from before

    const toggleFavorite = graphql(`
        mutation ToggleFavorite($id: Int!) {
            toggleFavorite(id: $id) {
                species {
                    id
                    favorite
                }
            }
        }
    `)
</script>
src/routes/[[id]]/+page.svelte
<script>
    // ... everything from before
    
    const toggleFavorite = graphql(`
        mutation ToggleFavorite($id: Int!) {
            toggleFavorite(id: $id) {
                species {
                    id
                    favorite
                }
            }
        }
    `)
</script>

A mutation store provides a mutate method to trigger your mutation. It takes an object with fields to match the mutation’s input and returns a promise that will resolve with the response from the mutation. With that in place, we can now configure the button we added earlier to call this function:

src/routes/[[id]]/+page.svelte
<button id="favorite" on:click={() => toggleFavorite.mutate({
    id: $Info.data.species.id
})}>

Now, try clicking on the grey star for any species. It should flip between gold and grey every time you click it.

That’s all there is to it! You see, Houdini maintains an in-memory representation of all of the data being shown in our UI as well as which components rely on each field. Since we asked for the fields that could change as part of our mutation, Houdini was able to detect that it needed to use the new favorite value to update the field of the species with the matching id and keep our view up to date. By the way, we could have omitted that id in the selection, Houdini will add it behind the scenes if we don’t include it explicitly.

Mutating List Values

This approach for updating field values does cover a lot of the use cases for mutations. However, if our mutation performs some operation on a list in our API, it is not always convenient, performant, or even possible to look up the updated value from the mutation’s payload. Take for example a query for the list of our favorite species

query FavoriteSpecies {
	favorites {
		name
		flavor_text
	}
}

It wouldn’t be possible to look up this list’s new value in the payload of toggleFavorite since that mutation only has a single output field: the species we toggled. We could add a second field to the mutation output but there’s a better way. Don’t worry you won’t have to write complicated imperative logic, Houdini makes this almost as easy as updating a field value.

Before we get too far, let’s add a place in our UI to show us the list of favorites. First, open up the +page.gql file and add the following block:

src/routes/[[id]]/+page.gql
 query Info($id: Int! = 1) {
     species(id: $id) {
         id
         name
         flavor_text
         favorite
         evolution_chain {
             ...SpeciesPreview
         }
         ...SpriteInfo
     }
+    favorites @list(name:"FavoriteSpecies") {
+        ...FavoritePreview
+    }
 }

Then, open up src/routes/[[id]]/+page.svelte, add imports for FavoritePreview and FavoritesContainer, and some components to visualize the list:

src/routes/[[id]]/+page.svelte
<script lang="ts">
    import { FavoritePreview, FavoritesContainer } from '~/components'
</script>

<!-- this should go in the root of your component, just before the Container -->
<FavoritesContainer>
    {#each $Info.data.favorites as favorite}
        <FavoritePreview species={favorite} />
    {:else}
        <p>
            No Favorites Selected
        </p>
    {/each}
</FavoritesContainer>
src/routes/[[id]]/+page.svelte
<script>
    import { FavoritePreview, FavoritesContainer } from '~/components'
</script>

<!-- this should go in the root of your component, just before the Container -->
<FavoritesContainer>
    {#each $Info.data.favorites as favorite}
        <FavoritePreview species={favorite} />
    {:else}
        <p>
            No Favorites Selected
        </p>
    {/each}
</FavoritesContainer>

Don’t worry about the @list directive just yet - we’ll explain what it does in a bit. For now, just confirm that you have to refresh your browser in order to see the effect of clicking the star on the section at the top. Hopefully that’s not too surprising since we haven’t told Houdini how to update our view in response to the mutation. Connecting those dots just requires updating the mutation to look like this:

src/routes/[[id]]/+page.svelte
mutation ToggleFavorite($id: Int!) {
    toggleFavorite(id: $id) {
        species {
            id
            favorite
            ...FavoriteSpecies_toggle
        }
    }
}

Go head, save the file and try clicking on the star. You should see the species pop in and out of the list of favorites.

If you look closely at the mutation you’ll notice that we are using a fragment in the payload that you never defined. That fragment name follows a very specific form (ListName_operation) and it acts as a special instruction to the Houdini runtime. That fragment name references the name specified with the @list decorator and then an operation that you want to perform on the list is appended to the end of the fragment name. So the FavoriteSpecies_toggle fragment name will toggle the object that is referenced by the species field in the mutation, which will add or remove the object in the list named FavoriteSpecies.

We didn’t even have to worry about asking for all of the right fields, once we told Houdini which list we wanted to add it to, it was able to take care of the rest. This was the reason for the @list decorator in the query above: we needed a way to identify the field as a target for a list operation. If we had named that list AllFavorites instead, the query would reference AllFavorites_toggle.

toggle is not the only operation you can perform on a list. Houdini also supports insert and remove as well as more advanced features such as specifying conditions for these operations. For more information, check out the mutations docs.

What’s Next?

Now that you’ve seen how Houdini allows us to declaratively update our client-side cache, it’s time to move onto the next topic for this guide: pagination. We’re going to take a look at how Houdini helps us incrementally load a long list of data.