website/docs/guides/relay-resolvers/derived-fields.mdx
import {FbInternalOnly, fbContent} from 'docusaurus-plugin-internaldocs-fb/internal'; import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem';
In addition to modeling client state, Relay Resolvers also allow you to define fields which are a pure function of other fields. These fields are called derived fields and can be defined on any type no matter if it's defined on the server or client.
For globally relevant data, resolvers have a few advantages of alternative solutions like React Hooks:
Derived resolvers look like any other resolver except that they read GraphQL data instead of being computed from a parent model type. Derived resolvers read GraphQL data by defining a "root fragment" which is a GraphQL fragment defined on the parent type of the field.
The root fragment is defined using the @rootFragment docblock tag followed by the name of the fragment. This tells Relay to pass the resolver function a fragment key for that fragment. The fragment data may then be read using readFragment imported from relay-runtime.
<Tabs groupId="resolver" defaultValue="Docblock" values={fbContent({ internal: [ {label: 'Docblock', value: 'Docblock'}, {label: 'Flow', value: 'Flow'}, ], external: [ {label: 'Docblock', value: 'Docblock'}, ] })}> <TabItem value="Docblock">
import {readFragment} from 'relay-runtime';
/**
* @relayField User.fullName: String
* @rootFragment UserFullNameFragment
*/
export function fullName(key: UserFullNameFragment$key): string {
const user = readFragment(graphql`
fragment UserFullNameFragment on User {
firstName
lastName
}
`, key);
return `${user.firstName} ${user.lastName}`;
}
import {readFragment} from 'relay-runtime';
/**
* @relayType
*/
export function fullName(key: UserFullNameFragment$key): string {
const user = readFragment(graphql`
fragment UserFullNameFragment on User {
firstName
lastName
}
`, key);
return `${user.firstName} ${user.lastName}`;
}
:::info Relay will track all the values read from the fragment and automatically recompute the resolver when any of those values change. :::
One powerful feature of derived resolvers is that they can read other Relay Resolver fields. This means you can define a derived resolver that combines server data, client data and even other derived resolvers. This allows you to build up complex, but explicit, computation graphs.
/**
* @relayField CheckoutItem.isValid: Boolean
* @rootFragment CheckoutItemFragment
*/
export function isValid(key): boolean {
const item = readFragment(graphql`
fragment CheckoutItemFragment on CheckoutItem {
product {
price
}
quantity
}
`, key);
return item.product.price * item.quantity > 0;
}
/**
* @relayField ShoppingCart.canCheckout: Boolean
* @rootFragment ShoppingCartFragment
*/
export function canCheckout(key): boolean {
const cart = readFragment(graphql`
fragment ShoppingCartFragment on ShoppingCart {
items {
isValid
}
}
`, key);
return cart.items.every(item => item.isValid);
}
If a field in a derived resolver's root fragment requires arguments, you can pass them by adding an @arguments tag to the docblock tag. The @argument tag takes the name of the argument and the type of the argument. The argument type must be a valid GraphQL input type. For more information about arguments and Resolvers see Field Arguments.