GraphQL Magic

Houdini adds a number of runtime definitions to your GraphQL schema in order to support its declarative API.

Fragments

List Operations

A field marked with @list or by passing a name to @paginate can use the following fragments in mutations or subscriptions to update the list:

  • [ListName]_insert: insert the reference into the list
  • [ListName]_remove: remove the reference from the list
  • [ListName]_toggle: if the reference is already in the list, remove it; otherwise, add it to the list

Some things worth mentioning:

  • Insert locations for for the insert and toggle fragments can be specified using the prepend or append directives specified below
  • Conditions for the operations can be specified with @when or @when_not shown below.
  • These fragments can be applied to lists and single values.
  • Sometimes the runtime needs a little help to hunt down the list you want to mutate. For more information on how to use @parentID to help, see below.

For more information on using these fragments, head over to the mutation docs.

Directives

@list(name: String!)

@list marks a field as the target for list operations. Must be passed a name which defines the fragments that can be used to mutate the list. See the list operations for more information.

@prepend

@prepend is used in conjunction with the list operation fragments to tell the runtime to add the element at the start of the list. Can be used with both _insert and _toggle fragments. If the list is a member of a fragment, don’t forget to specify @parentID directive.

@append

@append is used in conjunction with the list operation fragments to tell the runtime to add the element at the end of the list. Can be used with both _insert and _toggle fragments. If the list is a member of a fragment, don’t forget to specify @parentID directive.

@allLists

@allLists is used in conjunction with the list operation fragments to tell the runtime to that it should apply to all lists. Can be used with _insert, _toggle and _remove fragments. You can’t have parentID at the same time.

@when

@when provides a conditional under which the list operation should be executed. It takes arguments that match the arguments of the field tagged with @list or @paginate. For more information, check out the mutation docs.

mutation UncompleteItem($id: ID!) {
	uncheckItem(item: $id) {
		item {
			# only remove the item from the list of filtered items
			# if we are only showing the completed ones
			...All_Items_remove @when(completed: true)
		}
	}
}

@dedupe(cancelFirst: Boolean)

@dedupe lets you control wether or not multiple copies of the same operation (query or mutation) are allowed to run at the same time. If you pass true for the cancelFirst argument then the first copy of the operation will be canceled (including any in-flight requests). If you pass false (or pass nothing at all) then the second request won’t trigger if there is already one pending.

@when_not

@when provides a conditional under which the list operation should not be executed. It takes arguments that match the arguments of the field tagged with @list or @paginate. For more information, check out the mutation docs

mutation NewItem($input: AddItemInput!) {
	addItem(input: $input) {
		# only add the item to the list if we can see uncompleted items
		...All_Items_insert @when_not(completed: true)
	}
}

@cache(policy: CachePolicy, partial: Boolean)

@cache is used on a query document to customize the cache behavior. This includes informaton like what cache policy should be used by the runtime or if you are okay with partial results from the cache. For a full list of cache policies, check out the caching guide and for more information on partial responses, see the section on partial data.

@paginate(name: String, mode: PaginateMode)

A field marked with @paginate is updated with calls to the page loaders returned by the pagination function. For more information on pagination check out the guide. If you pass a value for the name argument, the underlying list can be updated using the operation fragments.

You can overwrite the default pagination behavior on a per-list basis by passing a value to the mode argument:

  • Infinite: new results are added to the existing list.
  • SinglePage: new results will replace the contents of the existing list.

@blocking

A query marked with @blocking will always await the fetch.

@blocking_disable

A query marked with @blocking_disable won’t block while the fetch resolves when navigating on the client. Keep in mind this will prevent throwOnError from capturing the error.

@loading(count: Int, cascade: Boolean)

Used to define the shape of your loading state. For more information, please visit this guide. Setting cascade to true will implicitly add @loading the entire sub-selection under the marked field.

@required

Customizes the behavior of null values at runtime. When a value is null that’s marked with @required, the parent object is set to null instead. For more information, check out this post on the relay blog.

@[TypeName]_delete

@[TypeName]_delete (ie, something like @User_delete) is used to delete the user with an id found in the body of a subscription or mutation. Like the operation fragments, this directive can be applied to lists or singular values. For more information see the mutation docs.

mutation DeleteItem($input: DeleteItemInput!) {
	deleteItem(input: $input) {
		# this will delete the item with the matching id from the cache
		# and remove it from all lists
		deletedID @Item_delete
	}
}

@parentID(value: String!)

@parentID is used to identify the parent ID for the field marked with @list or @paginate when it cannot be inferred. For example, imagine a situation where there are multiple users rendered on a view that shows their top 5 bands. That listing is retrieved with a query containing @list(name: "Favorite_Bands"):

query MyFriendsBandList {
	viewer {
		friends {
			favoriteBands @list(name: "Favorite_Bands") {
				name
			}
		}
	}
}

If you want to add a band to the list of a specific user, you need to pass the id field of the user found in the friend list:

mutation FavoriteBand($input: FavoriteBandInput!, $userID: String!) {
	setFavorite(input: $input) {
		# only add the item to the list if we can see uncompleted items
		...Favorite_Bands_insert @parentID(value: $userID)
	}
}

Enums

Houdini generates runtime representations of every enum in your schema. When passing enum values as inputs to your requests you can use a raw string value or use a utility object if you prefer:

import { MyEnum } from '$houdini'

const update = graphql(`
	mutation Update($input: MyEnum!) {
		update(input: $input)
	}
`)

function onClick() {
	// could use a utility object
	update.mutate({ variables: { input: MyEnum.Value1 } })
	// or pass a string directly
	update.mutate({ variables: { input: 'Value1' } })
}

Enum Value Types

Sometimes you want to define the types for function or component and you need to refer to the list of all values for a particular enum. To do this, import the $options variation on your enum:

import type { MyEnum$options } from '$houdini'

function (val: MyEnum$options) {

}