Cache API

There are times where Houdini’s automatic cache updates or list operation fragments are not sufficient. For those times, Houdini provides a programatic API for interacting with the cache directly. If you find yourself relying on this API for a considerable amount of your business logic, please open an issue or discussion on GitHub so we can try to figure out if there is something that Houdini could be doing better. This should be considered an advanced escape hatch for one-off situations.

Enabling the API

This API is currently considered experimental and while we refine it, we might need to dramatically change it overall shape. Until it’s ready, we want to reserve the ability to break its API on any minor version. We understand this is not technically semantic versioning but ultimately it will let us refine the API against real applications and lead to a better solution faster.

In order to acknowlege this, you will need to enable the acceptImperativeInstability flag in your config file:

houdini.config.js
export default {
    // ...
    acceptImperativeInstability: true
}

Records

The primary unit of the cache api is the Record. It acts as a proxy for interacting with entities in Houdini’s cache and does not actually hold onto any values. Retrieving a record from your cache is done with the get method on the cache exported from $houdini:

import { cache } from '$houdini'

const user = cache.get('User', { id: "1" })

Once you have the reference, you can retrieve values using the get method on the record:

const user = cache.get('User', { id: "1" })

// scalar values can be retrieved, custom scalars will be marshaled
const fullName = user.get({ field: "fullName" })

// links to other records can also be retrieved
const bestFriend = user.get({ field: "bestFriend" })

// if you need to retrieve the value when specific arguments are
// set, you should use the args parameter
const formattedName = user.get({
    field: "fullName",
    args: {
        format: "FIRST_LAST"
    }
})

You can also update values using the set method. Keep in mind that when you set a value that points to a linked record, you need to set it to another instance of Record:

const user = cache.get('User', { id: "1" })

// scalar values will be marshaled appropriately
user.set({ field: "fullName", value: "Houdini" })

// when setting linked records, you need to pass another record
user.set({
    field: "bestFriend",
    value: cache.get('User', { id: "3" })
})

// as with get, you can pass arguments to the field
user.set({
    field: "fullName",
    value: "HOUDINI_GRAPHQL",
    args: {
        format: "FIRST_LAST"
    }
})

You can also delete a record from the cache using the delete method:

const user = cache.get('User', { id: "1" })

user.delete()

Field schema information

There are a few situations where the runtime might complain that it doesn’t know the type of the field that you have set. For example, if you are setting a field that has not yet been queried. When this happens, you will need to provide some additional information so we can ensure that your responses are always valid. To do this, use cache.setFieldType:

cache.setFieldType({
    parent: 'User',
    key: 'fullName',
    type: 'String',
    nullable: false,
})

// links to other records must have `link` set to true
cache.setFieldType({
    parent: 'User',
    key: 'bestFriend',
    type: 'User',
    nullable: true,
    link: true
})

Lists

Another primitive provided by the cache instance is List and it provide a programatic API for the same operations supported by the list operation fragments.

Accessing a list can be done with the list method:

const allUsers = cache.list("All_Users")
const userFriends = cache.list("User_Friends", { parentID: "1" })
const allFriends = cache.list("User_Friends", { allLists: true })

You can mutate the list using the prepend, append, remove, and toggle methods:

const allUsers = cache.list("All_Users")
const user1 = cache.get('User', { id: "1" })

// add it to the beginning
allUsers.prepend(user1)

// add it to the end
allFriends.append(user1)

// remove it from the list
allFriends.remove(user1)

// if its in list, remove it. Otherwise, add it to the front.
// You can also also pass 'last' to insert it to the end of the list
allFriends.toggle('first', user1)

If you only want to operate on the list depending on argument values, you can use the when method

allFriends.when({ favorites: true }).append(user1)