<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <id>https://relay.dev/blog/</id>
    <title>Relay Blog</title>
    <updated>2025-06-13T00:00:00.000Z</updated>
    <generator>https://github.com/jpmonette/feed</generator>
    <link rel="alternate" href="https://relay.dev/blog/"/>
    <subtitle>Relay Blog</subtitle>
    <icon>https://relay.dev/img/favicon.png</icon>
    <entry>
        <title type="html"><![CDATA[Relay v20.0]]></title>
        <id>https://relay.dev/blog/2025/06/13/relay-20/</id>
        <link href="https://relay.dev/blog/2025/06/13/relay-20/"/>
        <updated>2025-06-13T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Announcement: ESLint Plugin v2.0.0 Released]]></summary>
        <content type="html"><![CDATA[<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="announcement-eslint-plugin-v200-released">Announcement: ESLint Plugin v2.0.0 Released<a href="https://relay.dev/blog/2025/06/13/relay-20/#announcement-eslint-plugin-v200-released" class="hash-link" aria-label="Direct link to Announcement: ESLint Plugin v2.0.0 Released" title="Direct link to Announcement: ESLint Plugin v2.0.0 Released" translate="no">​</a></h2>
<p>Relay's ESLint plugin, <a href="https://github.com/relayjs/eslint-plugin-relay" target="_blank" rel="noopener noreferrer" class="">eslint-plugin-relay</a>, was recently updated to v2.0.0. This release includes a number of compatibility updates and removes a couple of deprecated rules. For more info, see the <a href="https://github.com/relayjs/eslint-plugin-relay/blob/main/CHANGELOG.mdx" target="_blank" rel="noopener noreferrer" class="">eslint-plugin-relay changelog</a>.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="generated-documentation">Generated Documentation<a href="https://relay.dev/blog/2025/06/13/relay-20/#generated-documentation" class="hash-link" aria-label="Direct link to Generated Documentation" title="Direct link to Generated Documentation" translate="no">​</a></h2>
<p>This release includes a brand new page covering the <a href="https://relay.dev/docs/next/getting-started/compiler-config/" target="_blank" rel="noopener noreferrer" class="">Relay compiler config</a>. This has largely been undocumented so far and now includes autogenerated documentation! We also added tooling to autogenerate API docs from source code. The documentation for <a href="https://relay.dev/docs/next/api-reference/use-relay-environment/" target="_blank" rel="noopener noreferrer" class=""><code>useRelayEnvironment</code></a> and <a href="https://relay.dev/docs/next/api-reference/use-lazy-load-query/" target="_blank" rel="noopener noreferrer" class=""><code>useLazyLoadQuery</code></a> are examples of the new autogenerated tooling.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="breaking-changes">Breaking Changes<a href="https://relay.dev/blog/2025/06/13/relay-20/#breaking-changes" class="hash-link" aria-label="Direct link to Breaking Changes" title="Direct link to Breaking Changes" translate="no">​</a></h2>
<ul>
<li class="">Deprecate returning non-model weak types from resolvers. If you were using client side resolvers with the <code>@outputType</code> directive, these must be migrated to be strong or weak objects. You can continue to use the <code>@outputType</code> directive by enabling the <code>allow_output_type_resolvers</code> feature flag in the compiler config. (#5004) by Jordan Eldredge (<a href="https://github.com/facebook/relay/commit/8fbc0b9cc87f" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="bug-fixes">Bug fixes<a href="https://relay.dev/blog/2025/06/13/relay-20/#bug-fixes" class="hash-link" aria-label="Direct link to Bug fixes" title="Direct link to Bug fixes" translate="no">​</a></h2>
<ul>
<li class="">Fix operation cleanup in RelayModernMockEnvironment by Martin Booth (<a href="https://github.com/facebook/relay/commit/239f6d9351b1" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Fix nested @defer + 3D when server doesn't support streaming by Tianyu Yao (<a href="https://github.com/facebook/relay/commit/00594e1b7141" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Fix nested @defer when server doesn't support streaming by Tianyu Yao (<a href="https://github.com/facebook/relay/commit/b801db036d13" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Fix incremental bug where resolver artifacts were not cleaned up by Jordan Eldredge (<a href="https://github.com/facebook/relay/commit/b270988c0849" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Fetch missing client edge server queries discovered in nested fragments (#4992) by Jordan Eldredge (<a href="https://github.com/facebook/relay/commit/611f6d9cfc99" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Pass client edge context to resolver root fragments by Jordan Eldredge (<a href="https://github.com/facebook/relay/commit/4177f3e27b26" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Fix variable name for imported model resolvers when using ES module imports (#4984) by Jordan Eldredge (<a href="https://github.com/facebook/relay/commit/1073517d9842" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Fix excluding generated dir under xplat_react by Tianyu Yao (<a href="https://github.com/facebook/relay/commit/9cbd126f113e" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="improvements">Improvements<a href="https://relay.dev/blog/2025/06/13/relay-20/#improvements" class="hash-link" aria-label="Direct link to Improvements" title="Direct link to Improvements" translate="no">​</a></h2>
<ul>
<li class="">Add more time loggings in try_saved_state by Tianyu Yao (<a href="https://github.com/facebook/relay/commit/cba7007c7aaf" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Log saved state info query time in relay compiler by Lynn Yu (<a href="https://github.com/facebook/relay/commit/60b906b921a4" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Annotate read time resolver promises by Tianyu Yao (<a href="https://github.com/facebook/relay/commit/5ec796e48509" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Add VSCode tasks for common commands (#5003) by Jordan Eldredge (<a href="https://github.com/facebook/relay/commit/6ab766a3e684" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Surface prefetchExpiryInHours from the entrypoint by Alice Liu (<a href="https://github.com/facebook/relay/commit/ca7ddea7ed4b" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Regression test for client edge server data discovered missing in resolver root fragment (#4994) by Jordan Eldredge (<a href="https://github.com/facebook/relay/commit/48c81ad22bbf" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Run client edge tests for both versions of useFragment by Jordan Eldredge (<a href="https://github.com/facebook/relay/commit/16cd20078c84" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Update babel transform to default to ES modules to match compiler behavior (#4982) by Jordan Eldredge (<a href="https://github.com/facebook/relay/commit/281d76a0b0b4" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Stop unnecessary runtime transforms by Lynn Yu (<a href="https://github.com/facebook/relay/commit/1eca4a5720ee" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Support enquoted field alias by Arseniy Panfilov (<a href="https://github.com/facebook/relay/commit/1182ca64e0a0" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Parser option to allow literal string aliases by Arseniy Panfilov (<a href="https://github.com/facebook/relay/commit/fb9f4d7fcb10" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="documentation-improvements">Documentation Improvements<a href="https://relay.dev/blog/2025/06/13/relay-20/#documentation-improvements" class="hash-link" aria-label="Direct link to Documentation Improvements" title="Direct link to Documentation Improvements" translate="no">​</a></h2>
<ul>
<li class="">Update graphql.md readability (#5018) by Josh Maloon (<a href="https://github.com/facebook/relay/commit/9facd47af415" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Add argument definitions to usePaginationFragment docs (#5015) by DrillableBit (<a href="https://github.com/facebook/relay/commit/2b85d3740f40" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Remove docs on comet_routing_prefetch by Nithik Balachandran (<a href="https://github.com/facebook/relay/commit/5cb92f41b9fc" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Fix typo in description of the @throwOnFieldError directive by Marius Schulz (<a href="https://github.com/facebook/relay/commit/abedb68a3ad1" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">A couple grammar fixes (#5008) by Roman A (<a href="https://github.com/facebook/relay/commit/8c30f80fe6a2" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Replace out of date compiler readme with link to up-to-date docs page (#5005) by Jordan Eldredge (<a href="https://github.com/facebook/relay/commit/2c773876c7e8" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Handle args and returns, convert useLazyLoadQuery by Tianyu Yao (<a href="https://github.com/facebook/relay/commit/c3b70bc3a787" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Parse some docblock syntax and gen markdown for <code>useRelayEnvironment</code> by Tianyu Yao (<a href="https://github.com/facebook/relay/commit/9b7f643c006b" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Pass compiler config json schema as a prop (#4997) by Jordan Eldredge (<a href="https://github.com/facebook/relay/commit/9f242bb00f54" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Add docs page describing the Relay lint rules by Jordan Eldredge (<a href="https://github.com/facebook/relay/commit/7eca724d0aea" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Prevent json schema css for h2 from leaking into the rest of the site by Jordan Eldredge (<a href="https://github.com/facebook/relay/commit/35229211f119" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Compiler Docs: Improve separators in JSON arrays and objects (#4990) by Jordan Eldredge (<a href="https://github.com/facebook/relay/commit/e59619e23998" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Make comment into doc comment in config struct (#4989) by Jordan Eldredge (<a href="https://github.com/facebook/relay/commit/0b10aa591cf1" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Fill in missing documentation in compiler config by Jordan Eldredge (<a href="https://github.com/facebook/relay/commit/70b27dfdfa16" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Add page documenting the compiler config (#4985) by Jordan Eldredge (<a href="https://github.com/facebook/relay/commit/50fcf8726308" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Add missing generic type parameter for mutation tutorial example (#4981) by Joey Yu (<a href="https://github.com/facebook/relay/commit/f25a9d19aa91" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Fixups to quickstart (#4983) by Jordan Eldredge (<a href="https://github.com/facebook/relay/commit/3de18da10cdf" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Update quick start guide to align with new version of Relay by Jordan Eldredge (<a href="https://github.com/facebook/relay/commit/19c33bbb4d02" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
</ul>
<h1>Experimental Changes</h1>
<ul>
<li class="">Do not mark a query as inactive when waiting for resolver or server payload by Tianyu Yao (<a href="https://github.com/facebook/relay/commit/dce1a2eaa7ec" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">In a exec time query, mark query as completed if all initial payloads are received by Tianyu Yao (<a href="https://github.com/facebook/relay/commit/ba09a88925c0" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Fix operation executor for exec time by Tianyu Yao (<a href="https://github.com/facebook/relay/commit/4136a8cd71d9" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Expose a loadClientQuery helper by Tianyu Yao (<a href="https://github.com/facebook/relay/commit/21926a531f4f" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Support normalized responses in OperationExecutor by Tianyu Yao (<a href="https://github.com/facebook/relay/commit/f3a9afa895ba" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
</ul>]]></content>
        <author>
            <name>The Relay Team</name>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Relay v19.0]]></title>
        <id>https://relay.dev/blog/2025/04/02/relay-19/</id>
        <link href="https://relay.dev/blog/2025/04/02/relay-19/"/>
        <updated>2025-04-02T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Relay 19.0.0 includes many documentation improvements, bug fixes, and improved capabilities.]]></summary>
        <content type="html"><![CDATA[<p>Relay 19.0.0 includes many documentation improvements, bug fixes, and improved capabilities.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="alias-required-on-conditional-fragments"><code>@alias</code> required on conditional fragments<a href="https://relay.dev/blog/2025/04/02/relay-19/#alias-required-on-conditional-fragments" class="hash-link" aria-label="Direct link to alias-required-on-conditional-fragments" title="Direct link to alias-required-on-conditional-fragments" translate="no">​</a></h2>
<p>To improve type safety, the <a href="https://relay.dev/docs/next/guides/alias-directive/" target="_blank" rel="noopener noreferrer" class=""><code>@alias</code> directive</a> is now required on all fragments that are only conditionally fetched either due to <code>@skip</code>/<code>@include</code> or fragment type conditions which only conditionally match. You can opt out of this validation on a per-fragment basis with the <code>@dangerously_unaliased_fixme</code> directive.</p>
<p>To enable incremental migration we <a href="https://relay.dev/docs/next/guides/codemods/#mark-dangerous-conditional-fragment-spreads" target="_blank" rel="noopener noreferrer" class="">include a codemod</a> which will automatically add the <code>@dangerously_unaliased_fixme</code> in all required places:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:black;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:black;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:black"><span class="token plain">npx relay-compiler codemod mark-dangerous-conditional-fragment-spreads</span><br></span></code></pre></div></div>
<p>You can also opt out of this validation entirely via compiler config feature flag:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:black;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:black;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:black"><span class="token punctuation" style="color:#b58a5e">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// ...</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">  </span><span class="token property" style="color:black">"featureFlags"</span><span class="token operator" style="color:#b58a5e">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#b58a5e">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">    </span><span class="token property" style="color:black">"enforce_fragment_alias_where_ambiguous"</span><span class="token operator" style="color:#b58a5e">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#b58a5e">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">      </span><span class="token property" style="color:black">"kind"</span><span class="token operator" style="color:#b58a5e">:</span><span class="token plain"> </span><span class="token string" style="color:#65822b">"disabled"</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">    </span><span class="token punctuation" style="color:#b58a5e">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">  </span><span class="token punctuation" style="color:#b58a5e">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain"></span><span class="token punctuation" style="color:#b58a5e">}</span><br></span></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="improved-docs">Improved Docs<a href="https://relay.dev/blog/2025/04/02/relay-19/#improved-docs" class="hash-link" aria-label="Direct link to Improved Docs" title="Direct link to Improved Docs" translate="no">​</a></h2>
<p>We've merged ~30 commits to clean up and improve our docs since the last release:</p>
<ul>
<li class="">Added new pages:<!-- -->
<ul>
<li class=""><a href="https://relay.dev/docs/next/getting-started/quick-start/" target="_blank" rel="noopener noreferrer" class="">Quick Start</a> - A single page guide to get Relay up and running locally</li>
<li class=""><a href="https://relay.dev/docs/next/getting-started/production/" target="_blank" rel="noopener noreferrer" class="">Production Setup</a> - A list of best practices for setting your Relay application up for production use cases</li>
<li class=""><a href="https://relay.dev/docs/next/getting-started/babel-plugin/" target="_blank" rel="noopener noreferrer" class="">Relay Babel Plugin</a> - Information about Relay's Babel plugin and how to install it in your app.</li>
<li class=""><a href="https://relay.dev/docs/next/api-reference/relay-runtime/relay-environment/" target="_blank" rel="noopener noreferrer" class="">Relay Environment</a> - API docs for the core Relay environment</li>
<li class=""><a href="https://relay.dev/docs/next/api-reference/runtime-config/" target="_blank" rel="noopener noreferrer" class="">Runtime Configuration</a> - Documentation covering runtime feature flags and more</li>
</ul>
</li>
<li class="">Removed outdated or orphaned doc pages</li>
<li class="">Streamlined and fixed issues in existing docs</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="react-19-compatible">React 19 Compatible<a href="https://relay.dev/blog/2025/04/02/relay-19/#react-19-compatible" class="hash-link" aria-label="Direct link to React 19 Compatible" title="Direct link to React 19 Compatible" translate="no">​</a></h2>
<p>React 19 is now a valid peer dependency of Relay (#4944) by Krzysztof Karol (<a href="https://github.com/facebook/relay/commit/aeb26c3ac05f" target="_blank" rel="noopener noreferrer" class="">commit</a>)</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="breaking-changes">Breaking Changes<a href="https://relay.dev/blog/2025/04/02/relay-19/#breaking-changes" class="hash-link" aria-label="Direct link to Breaking Changes" title="Direct link to Breaking Changes" translate="no">​</a></h2>
<ul>
<li class="">Relay now defaults to generating ES module imports in its generated js files. You can add <code>"eagerEsModules": false</code> in your <code>relay.config.json</code> to opt back into the old behavior. (<a href="https://github.com/facebook/relay/commit/bd321a231397" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Relay's NPM modules no longer include a pre-bundled module. (#4935) by Iha Shin (<a href="https://github.com/facebook/relay/commit/cc4cf767dd44" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="improvements">Improvements<a href="https://relay.dev/blog/2025/04/02/relay-19/#improvements" class="hash-link" aria-label="Direct link to Improvements" title="Direct link to Improvements" translate="no">​</a></h2>
<ul>
<li class="">Avoid duplication in config JSON Schema by Jordan Eldredge (<a href="https://github.com/facebook/relay/commit/1e4b1646c334" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Go to definition in the LSP can now navigate to the correct column (#4969) by Jordan Eldredge (<a href="https://github.com/facebook/relay/commit/4cab6f1a4d4e" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Make store an optional argument when constructing a Relay Environment by Jordan Eldredge (<a href="https://github.com/facebook/relay/commit/288aebdbd77d" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Include mixed loggerContext in handled snapshot errors by Itamar Kestenbaum (<a href="https://github.com/facebook/relay/commit/d800611ad70d" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Add id collision logging in RelayResponseNormalizer with typename metadata by Monica Tang (<a href="https://github.com/facebook/relay/commit/419353ce8b50" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Mark useLazyLoadQuery options as ReadOnly by Marco Wang (<a href="https://github.com/facebook/relay/commit/7730d71ec4a0" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Add onPause in cacheConfig for Relay subscriptions by Aria Fallah (<a href="https://github.com/facebook/relay/commit/76b801a7946f" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Add typename metadata to id collision log event by Monica Tang (<a href="https://github.com/facebook/relay/commit/027572b82f89" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Allow @dangerously_unaliased_fixme on updatable fragment spreads by Jordan Eldredge (<a href="https://github.com/facebook/relay/commit/837d4ab00f54" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Add a feature flag to throw on nested updates in dev by Tianyu Yao (<a href="https://github.com/facebook/relay/commit/0ea5dfbbd836" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Do not emit union type for __typename selection on non-abstract type (#4923) by tobias-tengler (<a href="https://github.com/facebook/relay/commit/d9967e8fc676" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Reenable warning if fetchQuery is called in render (using new unstable React APIs) by Jordan Eldredge (<a href="https://github.com/facebook/relay/commit/9f7fb21578c0" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Log ID collisions in production by Monica Tang (<a href="https://github.com/facebook/relay/commit/65b9b9a6124a" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Add log events by Monica Tang (<a href="https://github.com/facebook/relay/commit/c640e2ef97c5" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Pass operation availability to the network layer for loadQuery by Tianyu Yao (<a href="https://github.com/facebook/relay/commit/ab3b117680c5" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Add compiler validation to error on resolver that returns plural server type by Jordan Eldredge (<a href="https://github.com/facebook/relay/commit/687e9964123f" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Enable by default recreating loadMore optimization by Andrei Marchenko (<a href="https://github.com/facebook/relay/commit/a6f66ebe468b" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Allow @dangerously_unaliased_fixme in updatable fragments by Jordan Eldredge (<a href="https://github.com/facebook/relay/commit/3a16e220a2ff" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Error on empty selections after fragment argument transform (#4908) by tobias-tengler (<a href="https://github.com/facebook/relay/commit/56fbda68b171" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Add error message to Resolver error by Itamar Kestenbaum (<a href="https://github.com/facebook/relay/commit/134aea416143" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Fix codemod crashing on aliases in inline fragments by Gordy French (<a href="https://github.com/facebook/relay/commit/002f9ed05aed" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Allow required alias codemod to be applied to a rollout range by Jordan Eldredge (<a href="https://github.com/facebook/relay/commit/afca975cd55b" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Add onResume in cacheConfig for Relay subscriptions by Xiangxin Sun (<a href="https://github.com/facebook/relay/commit/40b579fe4088" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Shorten relay read time resolver key prefix by Tianyu Yao (<a href="https://github.com/facebook/relay/commit/a5d6b23af4f0" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Arguments in prefetch pagination variables bug fix by Lynn Yu (<a href="https://github.com/facebook/relay/commit/b6291f97cb98" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">handle local variables vs global variables in prefetch pagination by Lynn Yu (<a href="https://github.com/facebook/relay/commit/59ae29292204" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Manual rebase of: Enable __token field based on compiler schema config #4347 (#4889) by Jordan Eldredge (<a href="https://github.com/facebook/relay/commit/9b90142ae813" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Fix conditional sub-selections in raw response type (#4774) by tobias-tengler (<a href="https://github.com/facebook/relay/commit/38ae4692b3a3" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Add execute.unsubsribe logging by Tianyu Yao (<a href="https://github.com/facebook/relay/commit/45984275bf72" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Enhance <code>schemaExtensions</code> to Support Both Files and Directories (#4859) by Sverre Johansen (<a href="https://github.com/facebook/relay/commit/be8f5f77e113" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Add plural fragment support to <code>observeFragment()</code> (#4862) by Iha Shin (<a href="https://github.com/facebook/relay/commit/9ce48174b60a" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Handle CRLF when parsing docblocks (#4865) by Sverre Johansen (<a href="https://github.com/facebook/relay/commit/79f0355e28ec" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Validate that client schema extensions within @throwOnFieldError have @catch by Jordan Eldredge (<a href="https://github.com/facebook/relay/commit/572a90f626a7" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Add @catch to client schema extension fields within @throwOnFieldError by Jordan Eldredge (<a href="https://github.com/facebook/relay/commit/61b028d932f4" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Make Result type fields readonly by Jordan Eldredge (<a href="https://github.com/facebook/relay/commit/6186a7da2f50" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Limit WalkDir to the root directories referenced by the configuration (#4850) by Sverre Johansen (<a href="https://github.com/facebook/relay/commit/9503d7be26aa" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Simplify dependencies for loadMore function by Andrei Marchenko (<a href="https://github.com/facebook/relay/commit/8c29557d54d2" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Propegate empty arrays into the store when handling errors on noncompliant lists by Ryan Holdren (<a href="https://github.com/facebook/relay/commit/6f76a2af2f88" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="bug-fixes">Bug fixes<a href="https://relay.dev/blog/2025/04/02/relay-19/#bug-fixes" class="hash-link" aria-label="Direct link to Bug fixes" title="Direct link to Bug fixes" translate="no">​</a></h2>
<ul>
<li class="">Fix nullable refetchedFragmentRef in checkSameIDAfterRefetch (#4945) by Krzysztof Karol (<a href="https://github.com/facebook/relay/commit/677c1e3dc44e" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Add missing <code>readFragment</code> export in relay-runtime (#4931) by Jay Jaeho Lee (<a href="https://github.com/facebook/relay/commit/51845382b662" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Fix bug with caching incomplete used variables for fragment cycles by Jordan Eldredge (<a href="https://github.com/facebook/relay/commit/3b732fc0ac88" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Fix <code>observeFragment</code> triggering unhandled rejections on network error (#4885) by Iha Shin (<a href="https://github.com/facebook/relay/commit/10073c88f8ec" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Checked for missed updates in effect create phase by Jack Pope (<a href="https://github.com/facebook/relay/commit/616508125a8e" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Avoid writing to stdout in LSP by Jordan Eldredge (<a href="https://github.com/facebook/relay/commit/51fac5d3c9a6" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Run required transform on IR before validating @required on semantic non null fields in LSP by Jordan Eldredge (<a href="https://github.com/facebook/relay/commit/911e0831f77f" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Fix: Don't report @required on field that can be null due to @required bubbling as unnecessary by Jordan Eldredge (<a href="https://github.com/facebook/relay/commit/f11501758ab7" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Unbreak LiveState unsub when references.size === 0 (#4832) by Tom Aylott (<a href="https://github.com/facebook/relay/commit/1e60e4716a5e" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Remove PointerAddress from generate_typename by Gordy French (<a href="https://github.com/facebook/relay/commit/d0da5251e8cb" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Context not properly provided through data injector and subscriptions (#4846) by Mark Polak (<a href="https://github.com/facebook/relay/commit/2211ac6c529b" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Fix rare client_extension instability by Gordy French (<a href="https://github.com/facebook/relay/commit/28be5c299075" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Enforce TTL-based GC when release buffer is full by Monica Tang (<a href="https://github.com/facebook/relay/commit/29b7a1e08c66" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Fix usePagination stuck in isLoading by Tianyu Yao (<a href="https://github.com/facebook/relay/commit/e9e556cf9bf3" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="documentation-improvements">Documentation Improvements<a href="https://relay.dev/blog/2025/04/02/relay-19/#documentation-improvements" class="hash-link" aria-label="Direct link to Documentation Improvements" title="Direct link to Documentation Improvements" translate="no">​</a></h2>
<ul>
<li class="">Update language to reference hooks not containers (#4964) by Jordan Eldredge (<a href="https://github.com/facebook/relay/commit/5893f3a06f4b" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Move the "Organizing Operations" tutorial page into the guides by Evan Yeung (<a href="https://github.com/facebook/relay/commit/91ec3275ae13" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Fix code examples in the connections tutorial page by Evan Yeung (<a href="https://github.com/facebook/relay/commit/155865207912" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Migrate most blockquotes in our docs to admonitions by Evan Yeung (<a href="https://github.com/facebook/relay/commit/07e662edd398" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Update VSCode docs/errors to use pathToRelay (#4965) by Jordan Eldredge (<a href="https://github.com/facebook/relay/commit/91c22be60cb5" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Iteration on new quick start guide based on feedback (#4966) by Jordan Eldredge (<a href="https://github.com/facebook/relay/commit/c7078a15e13f" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Quick Start: Add note about watchman by Jordan Eldredge (<a href="https://github.com/facebook/relay/commit/66550c3399a0" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Remove "TBD" sections from the docs by Evan Yeung (<a href="https://github.com/facebook/relay/commit/0470769e8235" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Rework onboarding flow by Jordan Eldredge (<a href="https://github.com/facebook/relay/commit/2784dfc48f68" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Remove migration and compatibility pages by Jordan Eldredge (<a href="https://github.com/facebook/relay/commit/479fde84fcc4" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Make footer logo smaller by Monica Tang (<a href="https://github.com/facebook/relay/commit/010b8e1e75b8" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Delete some pages by Monica Tang (<a href="https://github.com/facebook/relay/commit/ce0b8c3396aa" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Remove orphaned page "workflow" by Jordan Eldredge (<a href="https://github.com/facebook/relay/commit/7bf190f14fd9" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Delete empty docs pages by Monica Tang (<a href="https://github.com/facebook/relay/commit/bb7bde0e3d1f" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Fix typos and improve clarity in the tutorial by Evan Yeung (<a href="https://github.com/facebook/relay/commit/c13dbb18f180" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Delete legacy API docs (with broken links) by Monica Tang (<a href="https://github.com/facebook/relay/commit/a178610d4c06" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Fix subscription call signature in observeFragment docs by Jordan Eldredge (<a href="https://github.com/facebook/relay/commit/6d10effc2d56" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Fix broken markdown in OssOnly blocks by Itamar Kestenbaum (<a href="https://github.com/facebook/relay/commit/caf95e9700b0" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Fix broken markdown in FbInternalOnly blocks by Itamar Kestenbaum (<a href="https://github.com/facebook/relay/commit/bcd5c070fbce" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Document configuring relay runtime globally (#4906) by Jordan Eldredge (<a href="https://github.com/facebook/relay/commit/d51fae0970ed" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Update docs on usePaginationFragement &amp; transitions in Relay tutorial. (#4842) by Daniel Stocks (<a href="https://github.com/facebook/relay/commit/3bec307e636e" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">fix mistake in refetching-queries-with-different-data by Lynn Yu (<a href="https://github.com/facebook/relay/commit/61c7de97d28b" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Add documentation for RecordSourceProxy by Lynn Yu (<a href="https://github.com/facebook/relay/commit/3151894ad717" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Remove dead links from Relay users page by Evan Yeung (<a href="https://github.com/facebook/relay/commit/d91fa3fadd45" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Add documentation for Relay performance logger by Tianyu Yao (<a href="https://github.com/facebook/relay/commit/d64b0e9e4bc0" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Add docs for @gqlField resolvers by Evan Yeung (<a href="https://github.com/facebook/relay/commit/29fb05b5a3bd" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Document resolvers returning abstract types by Jordan Eldredge (<a href="https://github.com/facebook/relay/commit/620e7a63f23f" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Start to fix <code>Updating Data</code> section of relay docs by Lynn Yu (<a href="https://github.com/facebook/relay/commit/e946bb41a2c7" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">make it clear that 3D <code>@module</code> will only work if each fragment is on a different concrete type by Lynn Yu (<a href="https://github.com/facebook/relay/commit/87432667c110" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Fix typo in documentation by Allan Spreys (<a href="https://github.com/facebook/relay/commit/a8a075079ae3" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="experimental-changes">Experimental Changes<a href="https://relay.dev/blog/2025/04/02/relay-19/#experimental-changes" class="hash-link" aria-label="Direct link to Experimental Changes" title="Direct link to Experimental Changes" translate="no">​</a></h2>
<ul>
<li class="">Fix relay reader module import bug by Lynn Yu (<a href="https://github.com/facebook/relay/commit/9eb1ad09a5d0" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Load component module earlier into record store 1/2 by Lynn Yu (<a href="https://github.com/facebook/relay/commit/9d1fe445d97d" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Generate data driven dependencies for static resources used by exec resolver normalization artifacts by Lynn Yu (<a href="https://github.com/facebook/relay/commit/9ba58ebc2b81" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">add ability to configure operationModuleProvider path separately from componentModuleProvider by Lynn Yu (<a href="https://github.com/facebook/relay/commit/543918ee73ee" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Check for .read_time_resolvers in module_metadata before adding exec time directive in split_module_import by Lynn Yu (<a href="https://github.com/facebook/relay/commit/2fd469ae95d7" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">add exec time resolvers directive to split module import by Lynn Yu (<a href="https://github.com/facebook/relay/commit/07ef8d231aa5" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Generate exec time ASTs in rootFragment $normalization files by Tianyu Yao (<a href="https://github.com/facebook/relay/commit/aa6f91aeca86" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Compiler client 3D changes to support exec time resolvers by Lynn Yu (<a href="https://github.com/facebook/relay/commit/52f1e24926a3" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Create feature flag for typename prefixing of ids by Monica Tang (<a href="https://github.com/facebook/relay/commit/ae9ed10010ba" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">support client 3d on concrete objects by Lynn Yu (<a href="https://github.com/facebook/relay/commit/78e1499ba1a9" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Add test cases for @defer behavior in read time resolvers by Tianyu Yao (<a href="https://github.com/facebook/relay/commit/0cd69573cf21" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Modify _readClientSideDirectiveField in RelayReader to work for exec time resolvers by Lynn Yu (<a href="https://github.com/facebook/relay/commit/6b38703d0fce" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Allow @match on client edges by Tianyu Yao (<a href="https://github.com/facebook/relay/commit/ee924ca4a526" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Add enabledProvider argument inside @exec_time_resolvers directive (runtime + artifacts) by Lynn Yu (<a href="https://github.com/facebook/relay/commit/e29e1f947be8" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Modify useLazyLoadClientQuery to take a flag for exec time resolvers on or off by Lynn Yu (<a href="https://github.com/facebook/relay/commit/322d60a4cc31" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">amend compiler changes to handle exec_time_resolvers ONLY case differently by Lynn Yu (<a href="https://github.com/facebook/relay/commit/2e41a4518fa2" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">COMPILER changes add enabled argument inside @exec_time_resolvers directive by Lynn Yu (<a href="https://github.com/facebook/relay/commit/8c32ec4bab1b" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Parse the @gqlField docblock for the description and deprecated tag by Evan Yeung (<a href="https://github.com/facebook/relay/commit/89872b1e3f00" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Add logic to find property lookup resolver docblocks by Evan Yeung (<a href="https://github.com/facebook/relay/commit/1c02417a669c" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
<li class="">Pipe information through compiler for simplified resolver generation for property lookup resolvers by Evan Yeung (<a href="https://github.com/facebook/relay/commit/24951b9b6511" target="_blank" rel="noopener noreferrer" class="">commit</a>)</li>
</ul>]]></content>
        <author>
            <name>The Relay Team</name>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[How Relay Enables Optimal Data Fetching]]></title>
        <id>https://relay.dev/blog/2023/10/24/how-relay-enables-optimal-data-fetching/</id>
        <link href="https://relay.dev/blog/2023/10/24/how-relay-enables-optimal-data-fetching/"/>
        <updated>2023-10-24T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Exploring the tradeoffs that most data fetching strategies are forced to make, and how Relay allows you to have your cake and eat it too.]]></summary>
        <content type="html"><![CDATA[<p>Relay’s approach to application authorship enables a unique combination of
optimal runtime performance and application maintainability. In this post I’ll
describe the tradeoffs most apps are forced to make with their data fetching and
then describe how Relay’s approach allows you to sidestep these tradeoffs and
achieve an optimal outcome across multiple tradeoff dimensions.</p>
<hr>
<p>In component-based UI systems such as React, one important decision to make is
where in your UI tree you fetch data. While data fetching can be done at any
point in the UI tree, in order to understand the tradeoffs at play, let’s
consider the two extremes:</p>
<ul>
<li class="">Leaf node: Fetch data directly within each component that uses data</li>
<li class="">Root node: Fetch all data at the root of your UI and thread it down to leaf
nodes using prop drilling</li>
</ul>
<p>Where in the UI tree you fetch data impacts multiple dimensions of the
performance and maintainability of your application. Unfortunately, with naive
data fetching, neither extreme is optimal for all dimensions. Let’s look at
these dimensions and consider which improve as you move data fetching closer to
the leaves, vs. which improve as you move data fetching closer to the root.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="loading-experience">Loading experience<a href="https://relay.dev/blog/2023/10/24/how-relay-enables-optimal-data-fetching/#loading-experience" class="hash-link" aria-label="Direct link to Loading experience" title="Direct link to Loading experience" translate="no">​</a></h3>
<ul>
<li class="">🚫 Leaf node: If individual nodes fetch data, you will end up with request
cascades where your UI needs to make multiple request roundtrips in series
(waterfalls) since each layer of the UI is blocked on its parent layer
rendering. Additionally, if multiple components happen to use the same data,
you will end up fetching the same data multiple times</li>
<li class="">✅ Root node: If all your data is fetched at the root, you will make single
request and render the whole UI without any duplicate data or cascading
requests</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="suspense-cascades">Suspense cascades<a href="https://relay.dev/blog/2023/10/24/how-relay-enables-optimal-data-fetching/#suspense-cascades" class="hash-link" aria-label="Direct link to Suspense cascades" title="Direct link to Suspense cascades" translate="no">​</a></h3>
<ul>
<li class="">🚫 Leaf node: If each individual component needs to fetch data separately,
each component will suspend on initial render. With the current implementation
of React, unsuspending results in rerendering from the nearest parent suspense
boundary. This means you will have to reevaluate product component code O(n)
times during initial load, where n is the depth of the tree.</li>
<li class="">✅ Root node: If all your data is fetched at the root, you will suspend a
single time and evaluate product component code only once.</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="composability">Composability<a href="https://relay.dev/blog/2023/10/24/how-relay-enables-optimal-data-fetching/#composability" class="hash-link" aria-label="Direct link to Composability" title="Direct link to Composability" translate="no">​</a></h3>
<ul>
<li class="">✅ Leaf node: Using an existing component in a new place is as easy as
rendering it. Removing a component is as simple as not-rendering it. Similarly
adding/removing data dependencies can be done fully locally.</li>
<li class="">🚫 Root node: Adding an existing component as a child of another component
requires updating every query that includes that component to fetch the new
data and then threading the new data through all intermediate layers.
Similarly, removing a component requires tracing those data dependencies back
to each root component and determining if the component you removed was that
data’s last remaining consumer. The same dynamics apply to adding/removing new
data to an existing component.</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="granular-updates">Granular updates<a href="https://relay.dev/blog/2023/10/24/how-relay-enables-optimal-data-fetching/#granular-updates" class="hash-link" aria-label="Direct link to Granular updates" title="Direct link to Granular updates" translate="no">​</a></h3>
<ul>
<li class="">✅ Leaf node: When data changes, each component reading that data can
individually rerender, avoiding the need to rerender unaffected components.</li>
<li class="">🚫 Root node: Since all data originates at the root, when any data updates it
always forces the root component to update forcing an expensive rerender of
the entire component tree.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="relay">Relay<a href="https://relay.dev/blog/2023/10/24/how-relay-enables-optimal-data-fetching/#relay" class="hash-link" aria-label="Direct link to Relay" title="Direct link to Relay" translate="no">​</a></h2>
<p>Relay leverages GraphQL fragments and a compiler build step to offer a more
optimal alternative. In an app that uses Relay, each component defines a GraphQL
fragment which declares the data that it needs. This includes both the concrete
values the component will render as well as the fragments (referenced by name)
of each direct child component it will render.</p>
<p>At build time, the Relay compiler collects these fragments and builds a single
query for each root node in your application. Let’s look at how this approach
plays out for each of the dimensions described above:</p>
<ul>
<li class="">✅ Loading experience - The compiler generated query fetches all data needed
for the surface in a single roundtrip</li>
<li class="">✅ Suspense cascades - Since all data is fetched in a single request, we only
suspend once, and it’s right at the root of the tree</li>
<li class="">✅ Composability - Adding/removing data from a component, including the
fragment data needed to render a child component, can be done locally within a
single component. The compiler takes care of updating all impacted root
queries</li>
<li class="">✅ Granular updates - Because each component defines a fragment, Relay knows
exactly which data is consumed by each component. This lets relay perform
optimal updates where the minimal set of components are rerendered when data
changes</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="summary">Summary<a href="https://relay.dev/blog/2023/10/24/how-relay-enables-optimal-data-fetching/#summary" class="hash-link" aria-label="Direct link to Summary" title="Direct link to Summary" translate="no">​</a></h2>
<p>As you can see, Relay’s use of a declarative composable data fetching language
(GraphQL), combined with a compiler step, allows us to achieve optimal outcomes
across all of the tradeoff dimensions outlined above:</p>
<table><thead><tr><th></th><th>Leaf node</th><th>Root node</th><th>GraphQL/Relay</th></tr></thead><tbody><tr><td>Loading experience</td><td>🚫</td><td>✅</td><td>✅</td></tr><tr><td>Suspense cascades</td><td>🚫</td><td>✅</td><td>✅</td></tr><tr><td>Composability</td><td>✅</td><td>🚫</td><td>✅</td></tr><tr><td>Granular updates</td><td>✅</td><td>🚫</td><td>✅</td></tr></tbody></table>]]></content>
        <author>
            <name>Jordan Eldredge</name>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Relay v15.0]]></title>
        <id>https://relay.dev/blog/2023/03/30/relay-15/</id>
        <link href="https://relay.dev/blog/2023/03/30/relay-15/"/>
        <updated>2023-03-30T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[The Relay team is happy to announce the release of Relay v15. While this release is a major version bump and includes a couple of breaking changes, we expect that most users will be unaffected and will experience a seamless upgrade. You can find the full list of changes in the v15 Release Notes.]]></summary>
        <content type="html"><![CDATA[<p>The Relay team is happy to announce the release of Relay v15. While this release is a major version bump and includes a couple of breaking changes, we expect that most users will be unaffected and will experience a seamless upgrade. You can find the full list of changes in the <a href="https://github.com/facebook/relay/releases/tag/v15.0.0" target="_blank" rel="noopener noreferrer" class="">v15 Release Notes</a>.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="whats-new-in-relay-15">What's new in Relay 15?<a href="https://relay.dev/blog/2023/03/30/relay-15/#whats-new-in-relay-15" class="hash-link" aria-label="Direct link to What's new in Relay 15?" title="Direct link to What's new in Relay 15?" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="support-for-refetchable-on-interfaces">Support for <code>@refetchable</code> on interfaces<a href="https://relay.dev/blog/2023/03/30/relay-15/#support-for-refetchable-on-interfaces" class="hash-link" aria-label="Direct link to support-for-refetchable-on-interfaces" title="Direct link to support-for-refetchable-on-interfaces" translate="no">​</a></h3>
<p>Previously it wasn't possible to add the <code>@refetchable</code> directive on fragment definitions on server interface types.</p>
<div class="language-javascript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:black;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-javascript codeBlock_bY9V thin-scrollbar" style="color:black;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:black"><span class="token comment" style="color:#999988;font-style:italic">// schema.graphql</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:black"><span class="token plain"></span><span class="token keyword" style="color:#00009f">interface</span><span class="token plain"> </span><span class="token class-name">RefetchableInterfaceFoo</span><span class="token plain"> @</span><span class="token function" style="color:black">fetchable</span><span class="token punctuation" style="color:#b58a5e">(</span><span class="token plain">field_name</span><span class="token operator" style="color:#b58a5e">:</span><span class="token plain"> </span><span class="token string" style="color:#65822b">"id"</span><span class="token punctuation" style="color:#b58a5e">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#b58a5e">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">    </span><span class="token literal-property property" style="color:black">id</span><span class="token operator" style="color:#b58a5e">:</span><span class="token plain"> </span><span class="token constant" style="color:black">ID</span><span class="token operator" style="color:#b58a5e">!</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain"></span><span class="token punctuation" style="color:#b58a5e">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:black"><span class="token plain">extend type </span><span class="token maybe-class-name">Query</span><span class="token plain"> </span><span class="token punctuation" style="color:#b58a5e">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">  </span><span class="token function" style="color:black">fetch__RefetchableInterfaceFoo</span><span class="token punctuation" style="color:#b58a5e">(</span><span class="token plain">id</span><span class="token operator" style="color:#b58a5e">:</span><span class="token plain"> </span><span class="token constant" style="color:black">ID</span><span class="token operator" style="color:#b58a5e">!</span><span class="token punctuation" style="color:#b58a5e">)</span><span class="token operator" style="color:#b58a5e">:</span><span class="token plain"> </span><span class="token maybe-class-name">RefetchableInterfaceFoo</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain"></span><span class="token punctuation" style="color:#b58a5e">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:black"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// fragment</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:black"><span class="token plain">fragment </span><span class="token maybe-class-name">RefetchableFragmentFoo</span><span class="token plain"> on </span><span class="token maybe-class-name">RefetchableInterfaceFoo</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">  @</span><span class="token function" style="color:black">refetchable</span><span class="token punctuation" style="color:#b58a5e">(</span><span class="token plain">queryName</span><span class="token operator" style="color:#b58a5e">:</span><span class="token plain"> </span><span class="token string" style="color:#65822b">"RefetchableFragmentFooQuery"</span><span class="token punctuation" style="color:#b58a5e">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#b58a5e">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">  id</span><br></span><span class="token-line" style="color:black"><span class="token plain"></span><span class="token punctuation" style="color:#b58a5e">}</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="persisted-query-improvements">Persisted query improvements<a href="https://relay.dev/blog/2023/03/30/relay-15/#persisted-query-improvements" class="hash-link" aria-label="Direct link to Persisted query improvements" title="Direct link to Persisted query improvements" translate="no">​</a></h3>
<p>If you use URL-based persisted queries, you can now specify custom headers to send with the request that persists the query. For example, this can be used to send auth headers to your query persistence URL endpoint.</p>
<div class="language-js codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:black;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-js codeBlock_bY9V thin-scrollbar" style="color:black;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:black"><span class="token literal-property property" style="color:black">persistConfig</span><span class="token operator" style="color:#b58a5e">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#b58a5e">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">  </span><span class="token literal-property property" style="color:black">url</span><span class="token operator" style="color:#b58a5e">:</span><span class="token plain"> </span><span class="token string" style="color:#65822b">'example.com/persist'</span><span class="token punctuation" style="color:#b58a5e">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">  </span><span class="token literal-property property" style="color:black">headers</span><span class="token operator" style="color:#b58a5e">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#b58a5e">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">    </span><span class="token literal-property property" style="color:black">Authorization</span><span class="token operator" style="color:#b58a5e">:</span><span class="token plain"> </span><span class="token string" style="color:#65822b">'bearer TOKEN'</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">  </span><span class="token punctuation" style="color:#b58a5e">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain"></span><span class="token punctuation" style="color:#b58a5e">}</span><br></span></code></pre></div></div>
<p>For file-based persisted queries, we added a new feature flag, <code>compact_query_text</code>, that removes all whitespace from the persisted query text. This can make the file more than 60% smaller. This new feature flag can be enabled within your Relay config file.</p>
<div class="language-js codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:black;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-js codeBlock_bY9V thin-scrollbar" style="color:black;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:black"><span class="token literal-property property" style="color:black">persistConfig</span><span class="token operator" style="color:#b58a5e">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#b58a5e">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">  </span><span class="token literal-property property" style="color:black">file</span><span class="token operator" style="color:#b58a5e">:</span><span class="token plain"> </span><span class="token string" style="color:#65822b">'path/to/file.json'</span><span class="token punctuation" style="color:#b58a5e">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">  </span><span class="token literal-property property" style="color:black">algorithm</span><span class="token operator" style="color:#b58a5e">:</span><span class="token plain"> </span><span class="token string" style="color:#65822b">'SHA256'</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain"></span><span class="token punctuation" style="color:#b58a5e">}</span><span class="token punctuation" style="color:#b58a5e">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain"></span><span class="token literal-property property" style="color:black">featureFlags</span><span class="token operator" style="color:#b58a5e">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#b58a5e">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">  </span><span class="token literal-property property" style="color:black">compact_query_text</span><span class="token operator" style="color:#b58a5e">:</span><span class="token plain"> </span><span class="token boolean" style="color:black">true</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain"></span><span class="token punctuation" style="color:#b58a5e">}</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="typesafe-updates-now-support-missing-field-handlers">Typesafe updates now support missing field handlers<a href="https://relay.dev/blog/2023/03/30/relay-15/#typesafe-updates-now-support-missing-field-handlers" class="hash-link" aria-label="Direct link to Typesafe updates now support missing field handlers" title="Direct link to Typesafe updates now support missing field handlers" translate="no">​</a></h3>
<p>Typesafe updaters now support missing field handlers. Previously, if you selected <code>node(id: 4) { ... on User { name, __typename } }</code> in a typesafe updater, but that user was fetched in a different way (e.g. with <code>best_friend { name }</code>), you would not be able to access and mutate that user using the typesafe updater.</p>
<p>In this release, we add support for missing field handlers in typesafe updaters, meaning that if a missing field handler is set up for node (as in <a href="https://relay.dev/docs/next/guided-tour/reusing-cached-data/filling-in-missing-data/#internaldocs-banner" target="_blank" rel="noopener noreferrer" class="">this example</a>), you will be able to update the user's name with this missing field handler.</p>
<p>In order to support this, the signature of <a href="https://relay.dev/docs/guided-tour/reusing-cached-data/filling-in-missing-data" target="_blank" rel="noopener noreferrer" class="">missing field handlers</a> has been changed. The <code>record</code> argument to the handler used to receive a <code>Record</code> type (which is an untyped grab-bag of data). It now receives a <code>ReadOnlyRecordProxy</code>. Furthermore, the field argument of type <code>NormalizationLinkedField</code> is now <code>CommonLinkedField</code>, which is a type containing the properties found in both <code>ReaderLinkedField</code> and <code>NormalizationLinkedField</code>.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="flow-type-improvements">Flow type improvements<a href="https://relay.dev/blog/2023/03/30/relay-15/#flow-type-improvements" class="hash-link" aria-label="Direct link to Flow type improvements" title="Direct link to Flow type improvements" translate="no">​</a></h3>
<p>Flow users will now get types inferred from <code>graphql</code> literals with more Relay APIs. No longer do Flow users need to explicitly type the return value of the <code>usePreloadedQuery</code>, <code>useQueryLoader</code>, <code>useRefetchableFragment</code>, <code>usePaginationFragment</code>, and <code>useBlockingPaginationFragment</code> API methods.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="relay-resolver-improvements">Relay Resolver improvements<a href="https://relay.dev/blog/2023/03/30/relay-15/#relay-resolver-improvements" class="hash-link" aria-label="Direct link to Relay Resolver improvements" title="Direct link to Relay Resolver improvements" translate="no">​</a></h3>
<p>A significant portion of our development effort since our last release has gone into improving <a href="https://relay.dev/api-reference/relay-resolvers/introduction/" target="_blank" rel="noopener noreferrer" class="">Relay Resolvers</a> (a mechanism for exposing derived data in the graph). It is worth noting that Relay Resolvers are still experimental and API changes might occur in the future.</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="terser-docblock-tags">Terser docblock tags<a href="https://relay.dev/blog/2023/03/30/relay-15/#terser-docblock-tags" class="hash-link" aria-label="Direct link to Terser docblock tags" title="Direct link to Terser docblock tags" translate="no">​</a></h4>
<p>The annotation for Relay Resolver functions has been simplified. In many scenarios you can now use the <code>ParentType.field_name: ReturnType</code> syntax to define what new field your Relay Resolver exposes.</p>
<p>Before:</p>
<div class="language-js codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:black;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-js codeBlock_bY9V thin-scrollbar" style="color:black;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:black"><span class="token doc-comment comment" style="color:#999988;font-style:italic">/**</span><br></span><span class="token-line" style="color:black"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> * @RelayResolver</span><br></span><span class="token-line" style="color:black"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> * </span><span class="token doc-comment comment keyword" style="color:#00009f;font-style:italic">@onType</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> User</span><br></span><span class="token-line" style="color:black"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> * </span><span class="token doc-comment comment keyword" style="color:#00009f;font-style:italic">@fieldName</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> favorite_page</span><br></span><span class="token-line" style="color:black"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> * </span><span class="token doc-comment comment keyword" style="color:#00009f;font-style:italic">@rootFragment</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> myRootFragment</span><br></span><span class="token-line" style="color:black"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> */</span><br></span></code></pre></div></div>
<p>After:</p>
<div class="language-js codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:black;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-js codeBlock_bY9V thin-scrollbar" style="color:black;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:black"><span class="token doc-comment comment" style="color:#999988;font-style:italic">/**</span><br></span><span class="token-line" style="color:black"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> * @RelayResolver User.favorite_page: Page</span><br></span><span class="token-line" style="color:black"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> * </span><span class="token doc-comment comment keyword" style="color:#00009f;font-style:italic">@rootFragment</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> myRootFragment</span><br></span><span class="token-line" style="color:black"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> */</span><br></span></code></pre></div></div>
<p>In the above example, the <code>Page</code> type is a schema type. If your Relay Resolver doesn't return a schema type, you can use fixed <code>RelayResolverValue</code> value as your return type</p>
<div class="language-js codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:black;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-js codeBlock_bY9V thin-scrollbar" style="color:black;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:black"><span class="token doc-comment comment" style="color:#999988;font-style:italic">/**</span><br></span><span class="token-line" style="color:black"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> * @RelayResolver User.best_friend: RelayResolverValue</span><br></span><span class="token-line" style="color:black"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> * </span><span class="token doc-comment comment keyword" style="color:#00009f;font-style:italic">@rootFragment</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> myRootFragment</span><br></span><span class="token-line" style="color:black"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> */</span><br></span></code></pre></div></div>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="define-multiple-resolvers-per-file">Define multiple resolvers per file<a href="https://relay.dev/blog/2023/03/30/relay-15/#define-multiple-resolvers-per-file" class="hash-link" aria-label="Direct link to Define multiple resolvers per file" title="Direct link to Define multiple resolvers per file" translate="no">​</a></h4>
<p>Prior to this release we only allowed a single Relay Resolver per file and required the Relay Resolver function to be the default export. In Relay 15 you're now able to define multiple Relay Resolvers per file and use named exports.</p>
<div class="language-js codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:black;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-js codeBlock_bY9V thin-scrollbar" style="color:black;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:black"><span class="token doc-comment comment" style="color:#999988;font-style:italic">/**</span><br></span><span class="token-line" style="color:black"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> * @RelayResolver User.favorite_page: Page</span><br></span><span class="token-line" style="color:black"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> * </span><span class="token doc-comment comment keyword" style="color:#00009f;font-style:italic">@rootFragment</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> favoritePageFragment</span><br></span><span class="token-line" style="color:black"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> */</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain"></span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:black">usersFavoritePage</span><span class="token punctuation" style="color:#b58a5e">(</span><span class="token punctuation" style="color:#b58a5e">)</span><span class="token punctuation" style="color:#b58a5e">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">  </span><span class="token spread operator" style="color:#b58a5e">...</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain"></span><span class="token punctuation" style="color:#b58a5e">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:black"><span class="token plain"></span><span class="token doc-comment comment" style="color:#999988;font-style:italic">/**</span><br></span><span class="token-line" style="color:black"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> * @RelayResolver User.best_friend: RelayResolverValue</span><br></span><span class="token-line" style="color:black"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> * </span><span class="token doc-comment comment keyword" style="color:#00009f;font-style:italic">@rootFragment</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> bestFriendFragment</span><br></span><span class="token-line" style="color:black"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> */</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain"></span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:black">usersBestFriend</span><span class="token punctuation" style="color:#b58a5e">(</span><span class="token punctuation" style="color:#b58a5e">)</span><span class="token punctuation" style="color:#b58a5e">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">  </span><span class="token spread operator" style="color:#b58a5e">...</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain"></span><span class="token punctuation" style="color:#b58a5e">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:black"><span class="token plain">module</span><span class="token punctuation" style="color:#b58a5e">.</span><span class="token property-access">exports</span><span class="token plain"> </span><span class="token operator" style="color:#b58a5e">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#b58a5e">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">  usersFavoritePage</span><span class="token punctuation" style="color:#b58a5e">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">  usersBestFriend</span><br></span><span class="token-line" style="color:black"><span class="token plain"></span><span class="token punctuation" style="color:#b58a5e">}</span><br></span></code></pre></div></div>
<p>Happy Querying!</p>]]></content>
        <author>
            <name>The Relay Team</name>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Resilient Relay Applications]]></title>
        <id>https://relay.dev/blog/2023/01/03/resilient-relay-apps/</id>
        <link href="https://relay.dev/blog/2023/01/03/resilient-relay-apps/"/>
        <updated>2023-01-03T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Resilient Relay Applications]]></summary>
        <content type="html"><![CDATA[<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>Guest Post</div><div class="admonitionContent_BuS1"><p>This is a guest post written by Ernie Turner, a Staff Engineer at Coinbase. Coinbase has thoroughly adopted Relay in their applications and is a strong ally of the Relay Team. Last year they helped co-develop the <a href="https://marketplace.visualstudio.com/items?itemName=meta.relay" target="_blank" rel="noopener noreferrer" class="">Relay VSCode extension</a>. Ernie has agreed to share this internal engineering blog post with us.</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="how-to-provide-the-best-experience-for-customers-during-service-disruptions">How to provide the best experience for customers during service disruptions<a href="https://relay.dev/blog/2023/01/03/resilient-relay-apps/#how-to-provide-the-best-experience-for-customers-during-service-disruptions" class="hash-link" aria-label="Direct link to How to provide the best experience for customers during service disruptions" title="Direct link to How to provide the best experience for customers during service disruptions" translate="no">​</a></h2>
<p>In a perfect world, none of the services at Coinbase would suffer outages, and all fields in our GraphQL schema would resolve correctly all the time. As this isn't practical, Coinbase applications should be resilient to downtime and minimize the impact on customers: a single service suffering downtime should not prevent users from using or interacting with an entire app. However, it's also important that we convey issues to users when our applications aren't working as expected. Showing error messages that convey downtime with retry buttons is a better experience than confusing users with missing content or UI they can't interact with.</p>
<p>This post will cover the common patterns and best practices for dealing with missing data in a Relay application.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="screen-architecture-and-error-boundaries">Screen Architecture and Error Boundaries<a href="https://relay.dev/blog/2023/01/03/resilient-relay-apps/#screen-architecture-and-error-boundaries" class="hash-link" aria-label="Direct link to Screen Architecture and Error Boundaries" title="Direct link to Screen Architecture and Error Boundaries" translate="no">​</a></h2>
<p>Before we discuss handling service downtime and failures in GraphQL queries, let's first discuss broader screen architecture and how React Error Boundaries can help create a better user experience when used correctly.</p>
<p>Like most things in life, Error Boundaries should be used in moderation. Let's look at a common screen in the Coinbase Retail app.</p>
<div style="text-align:center;padding-top:1rem;padding-bottom:1rem"><img src="https://relay.dev/assets/images/2023-01-03-resilient-relay-apps-asset-screen-8bf495a00add38597fe997cecd3791ff.png" width="35%" alt="" title="Asset detail screen"></div>
<p>Any section in the above screen could fail to get the data required to render, but it's how we approach these failures that differentiates what experience a user has with our app. For example, only using a single screen-level ErrorBoundary for any failure causes the app to be unusable when any error occurs, regardless of the significance of that error. In contrast, wrapping each component in its own ErrorBoundary can create just as bad of an experience. Lastly, omitting components with errors entirely is as bad as the other two options. There is no one-size-fits-all approach, so let's break down each of these and explain why they create poor user experiences.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="full-screen-error">Full Screen Error<a href="https://relay.dev/blog/2023/01/03/resilient-relay-apps/#full-screen-error" class="hash-link" aria-label="Direct link to Full Screen Error" title="Direct link to Full Screen Error" translate="no">​</a></h3>
<div style="text-align:center;padding-top:1rem;padding-bottom:1rem"><img src="https://relay.dev/assets/images/2023-01-03-resilient-relay-apps-full-app-error-5ea0539d3141ab84d51f5768e2517676.png" width="35%" alt="" title="Full Screen Error"></div>
<p>The UI above is Coinbase's full-screen error fallback that is displayed if a service is experiencing disruptions and we couldn't get the data necessary to render the components on this screen. In certain situations, this actually creates a good user experience. We may not be giving the user detailed information as to what happened, but in most situations providing the technical cause is not possible, nor would it improve the users' experience. However, we are telling them something isn't working correctly and giving them a clear Retry button to attempt to get the app working again.</p>
<p>If the reason we're showing this to the user is because we can't load something non-critical, like the asset price history graph or their watchlist status, we shouldn't take down the entire screen. Hiding the current price of bitcoin and preventing the user from trading, just because we can't tell them whether bitcoin is on their watchlist, is a negative user experience.</p>
<p>Another negative of this UI is that it hides all app navigation from the user. Even if we have a good reason to show the user a full screen error, that doesn't mean we should hide the rest of the app in the process. A user should still be able to navigate to a different screen. In practice, we should only show users a “full screen error” and not a “full app error”.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="error-messages-everywhere">Error Messages Everywhere<a href="https://relay.dev/blog/2023/01/03/resilient-relay-apps/#error-messages-everywhere" class="hash-link" aria-label="Direct link to Error Messages Everywhere" title="Direct link to Error Messages Everywhere" translate="no">​</a></h3>
<div style="text-align:center;padding-top:1rem;padding-bottom:1rem"><img src="https://relay.dev/assets/images/2023-01-03-resilient-relay-apps-errors-everywhere-0181401186dc69b371358f74ccc6b7db.png" width="35%" alt="" title="Error Messages Everywhere"></div>
<p>The UI pictured above is, in many ways, worse. This is the opposite end of the previous experience and showing the user a full-screen error would be preferable. Error messages for the price history graph make sense, because the user would expect that UI to be on this screen, but if the user can't even see the price of bitcoin or find the Trade button, we really ought to show them the UI in the first screenshot (but with navigation) - as the core goal and purpose of this screen has been lost.</p>
<p>This image also demonstrates how ErrorBoundaries can be too prevalent. The entire price history graph with the time range selectors should only have a single error message, not one per time range.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="empty-fallbacks">Empty Fallbacks<a href="https://relay.dev/blog/2023/01/03/resilient-relay-apps/#empty-fallbacks" class="hash-link" aria-label="Direct link to Empty Fallbacks" title="Direct link to Empty Fallbacks" translate="no">​</a></h3>
<div style="text-align:center;padding-top:1rem;padding-bottom:1rem"><img src="https://relay.dev/assets/images/2023-01-03-resilient-relay-apps-empty-fallbacks-bdc4d221f15bc80db48505b71eaf0e60.png" width="35%" alt="" title="Empty Fallbacks"></div>
<p>The UI above is just as bad as the example prior. In this case, our ErrorBoundaries fall back to empty content. For certain UI elements, this makes sense. The missing Share button next to the watchlist isn't critical for this UI, so omitting it makes sense. However, hiding the current price of bitcoin, the price history graph, and the Trade button makes the UI unusable and even somewhat misleading. Even users who don't use the app every day would know that something is off. We also aren't giving the user any option to retry any failures —the user just sees empty content with no way to recover.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-should-the-user-see-instead">What should the user see instead?<a href="https://relay.dev/blog/2023/01/03/resilient-relay-apps/#what-should-the-user-see-instead" class="hash-link" aria-label="Direct link to What should the user see instead?" title="Direct link to What should the user see instead?" translate="no">​</a></h3>
<p>The following two screenshots show an example of a better experience for the user. The first screenshot is what the user should see if we can't get the current price of bitcoin or if we can't determine whether the user is allowed to trade. The second screenshot would be a better experience for a user if we couldn't get the current change in the price of bitcoin or the price history.</p>
<div style="display:flex;justify-content:space-around"><div style="text-align:center;padding-top:1rem;padding-bottom:1rem"><img src="https://relay.dev/assets/images/2023-01-03-resilient-relay-apps-full-screen-error-5d2a2ff3f60afd313c7da21b49006cf3.png" width="90%" alt="" title="Bummer, but at least I know something is wrong and can navigate to a different screen or try to refresh this screen."></div><div style="text-align:center;padding-top:1rem;padding-bottom:1rem"><img src="https://relay.dev/assets/images/2023-01-03-resilient-relay-apps-missing-sparkline-4e908ee49044c181b875e97066c88649.png" width="90%" alt="" title="I wish I could see the price history, but at least I can still Trade or try to refresh the price history chart."></div></div>
<p>All of this points to a need to classify sections of the UI on a screen: what is critical for the user's experience, what UI the user expects to see, and what supporting content is optional to the experience.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="critical-vs-expected-vs-optional-ui">Critical vs Expected vs Optional UI<a href="https://relay.dev/blog/2023/01/03/resilient-relay-apps/#critical-vs-expected-vs-optional-ui" class="hash-link" aria-label="Direct link to Critical vs Expected vs Optional UI" title="Direct link to Critical vs Expected vs Optional UI" translate="no">​</a></h2>
<p>Not all UI elements in an application screen are the same. Some portions of the UI are critical to the core purpose of the screen, others might just be more informational and helpful to users. For application design at Coinbase, we group UI elements into three categories, <strong>Critical</strong>, <strong>Expected</strong>, and <strong>Optional</strong>.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="critical-ui-elements">Critical UI Elements<a href="https://relay.dev/blog/2023/01/03/resilient-relay-apps/#critical-ui-elements" class="hash-link" aria-label="Direct link to Critical UI Elements" title="Direct link to Critical UI Elements" translate="no">​</a></h3>
<p>The parts of a screen that define the core information or interaction a user has with the UI. Without these elements in the UI, the screen does not make sense, and if they were missing, users would be confused and/or angry, as it isn't clear why the app wasn't working as expected.</p>
<p>Suppose we can't load the data necessary to display these critical UI elements. In that case, we should show the user a full-screen error message explaining the problem (if possible) with a retry button that lets them easily attempt to re-request the missing data.</p>
<p>Letting users interact with an application that is missing critical UI elements will cause confusion, anger, and even possible loss of funds if the user is able to complete a transaction without knowing the full details of what is happening.</p>
<p>Examples of Critical UI elements:</p>
<ul>
<li class="">The user's current portfolio balance on the Coinbase app home screen</li>
<li class="">The Asset Price, Payment Method, and total Purchase Price on the order preview screen</li>
<li class="">The user's lifetime earnings and earnings per asset on the Earn screen</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="expected-ui-elements">Expected UI Elements<a href="https://relay.dev/blog/2023/01/03/resilient-relay-apps/#expected-ui-elements" class="hash-link" aria-label="Direct link to Expected UI Elements" title="Direct link to Expected UI Elements" translate="no">​</a></h3>
<p>Expected UI elements are the parts of a screen that might not serve the core purpose of a screen, but that most users would expect to be present. If Expected UI elements are missing from a screen, the user is likely to think that something is wrong, but this wouldn't prevent them from performing the core actions of the screen.</p>
<p>If we can't load the data necessary to display these expected UI elements, we should show the user a component-local error message telling them that there is an expected UI that is missing. These error messages should also be accompanied by a retry button to let the user re-request the missing data. Localized errors have a higher chance of not being seen or interacted with by the user, which is somewhat acceptable since they aren't required for the core purpose of the screen.</p>
<p>Letting users interact with an application that is missing expected UI elements should be acceptable but it might cause confusion about what is happening. Completely omitting these UI elements without an accompanying error message would create a worse experience.</p>
<p>Examples of Expected UI elements:</p>
<ul>
<li class="">An asset's current price on the Buy Asset screen (where they enter the amount to buy)</li>
<li class="">The price history graph on an asset detail screen</li>
<li class="">A list of recent transactions on the Coinbase Card screen</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="optional-ui-elements">Optional UI Elements<a href="https://relay.dev/blog/2023/01/03/resilient-relay-apps/#optional-ui-elements" class="hash-link" aria-label="Direct link to Optional UI Elements" title="Direct link to Optional UI Elements" translate="no">​</a></h3>
<p>Optional UI elements are the parts of a screen that are purely supportive to the main purpose of a screen. Some users might notice these missing elements, but others might be completely unaware that they're supposed to be present at all. In either scenario, a user wouldn't be prevented from accomplishing their main goal on the screen.</p>
<p>If we can't load the data necessary to display these Optional UI elements, we should instead just omit them entirely from the UI. However, this comes with the following risks:</p>
<p>A. The user might not know that anything is missing
B. There won't be a way for the user to re-request the data for this UI unless they do a full screen refresh.</p>
<p>Developers should consider these downsides and ensure that they do not cause a negative user experience. Instead, these failures should be logged so that product engineers are notified when the user experience is less than ideal.</p>
<p>Examples of Optional UI Elements:</p>
<ul>
<li class="">Offer cards on the asset detail screen</li>
<li class="">Asset category sections on the Trade screen (New on Coinbase, Top Movers, etc.)</li>
<li class="">News feed on the Home Screen</li>
</ul>
<p>Let's return to the image above and classify the sections of the UI into these categories.</p>
<div style="text-align:center;padding-top:1rem;padding-bottom:1rem"><img src="https://relay.dev/assets/images/2023-01-03-resilient-relay-apps-sections-ff21f3c07349fa588b5ae74a1228ecee.png" width="35%" alt="" title="Red: critical, Orange: expected, Yellow: optional"></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="element-classification-limits">Element Classification Limits<a href="https://relay.dev/blog/2023/01/03/resilient-relay-apps/#element-classification-limits" class="hash-link" aria-label="Direct link to Element Classification Limits" title="Direct link to Element Classification Limits" translate="no">​</a></h3>
<p>In the example above, we have a screen that has two critical components, two expected components, and one optional component. Most screens in an app should only have a handful of critical UI components on them. For some screens, the entire UI might be composed of one single critical component.</p>
<p>The same is true for expected elements. If we have a screen that's composed of five separate expected UI elements, we'd end up with the screenshot above with ‘Try Again' buttons littered across the app. Limit the number of expected elements and retry buttons on a single screen to only one or two if possible.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="pull-to-refresh">Pull To Refresh<a href="https://relay.dev/blog/2023/01/03/resilient-relay-apps/#pull-to-refresh" class="hash-link" aria-label="Direct link to Pull To Refresh" title="Direct link to Pull To Refresh" translate="no">​</a></h3>
<p>For all of the above scenarios, users on mobile apps should be able to pull-to-refresh to retry any failed request on a screen. With Relay applications, this will usually mean retrying the full screen-level query. If a screen has any error messages or hidden components because of missing data, using pull-to-refresh should always attempt to fix all of those error conditions.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="work-with-your-product-managers-and-designers">Work with your Product Managers and Designers<a href="https://relay.dev/blog/2023/01/03/resilient-relay-apps/#work-with-your-product-managers-and-designers" class="hash-link" aria-label="Direct link to Work with your Product Managers and Designers" title="Direct link to Work with your Product Managers and Designers" translate="no">​</a></h3>
<p>All of this classification is subjective — and all of the examples above are just one opinion and a designer or PM may have different opinions on how screens should degrade. It is important for cross-functional alignment when designing application UI. Teams should consult engineers, designers, and product managers to ensure seamless and on-brand screens across your entire app.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="how-relay-can-help">How Relay Can Help<a href="https://relay.dev/blog/2023/01/03/resilient-relay-apps/#how-relay-can-help" class="hash-link" aria-label="Direct link to How Relay Can Help" title="Direct link to How Relay Can Help" translate="no">​</a></h2>
<p>Once you've classified your screen into sections, the next step is to add the proper ErrorBoundaries to your app and configure your components' GraphQL fragments depending on their classification. This is where <a href="https://relay.dev/" target="_blank" rel="noopener noreferrer" class="">Relay</a> can help. Based on our experience working with Relay apps, we've created several best practices around how to deal with missing data from GraphQL queries.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="background">Background<a href="https://relay.dev/blog/2023/01/03/resilient-relay-apps/#background" class="hash-link" aria-label="Direct link to Background" title="Direct link to Background" translate="no">​</a></h3>
<p>Our goal at Coinbase is to work with a nullable schema as <a href="https://relay.dev/docs/guides/required-directive/#why-not-implement-this-at-the-schemaserver-level" target="_blank" rel="noopener noreferrer" class="">recommended by the Relay team</a>. The primary driver is that it puts the decision on how to handle service outages and missing query data in the hands of the client engineer. Without a nullable schema, the decision of what to do with missing data is made on the server (by bubbling up null values to the nearest nullable parent), and the client code has no recourse to change this decision.</p>
<p>This decision is buoyed by the existence of the <a href="https://relay.dev/docs/guides/required-directive/" target="_blank" rel="noopener noreferrer" class="">Relay <code>@required</code> directive</a>, which allows client engineers to annotate their queries and fragments with directives that tell Relay how to handle missing data at runtime. This reduces boilerplate code that engineers would be required to write otherwise. On the surface, the directive seems very simple: it only comes with three options which are all pretty straightforward. However, when attempting to use this directive for various use cases, it becomes clear that the choice of which option to pick is not always obvious, nor is the decision of whether to use the directive at all.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="locality-of-required">Locality of @required<a href="https://relay.dev/blog/2023/01/03/resilient-relay-apps/#locality-of-required" class="hash-link" aria-label="Direct link to Locality of @required" title="Direct link to Locality of @required" translate="no">​</a></h3>
<p>One great feature of the <code>@required</code> directive is that it only affects the fragment in which you use it. It will never change the behavior of other fragments that query the same field. This allows you to add or remove the directive without thinking about anything outside your component's scope. This is important because different components may be categorized differently, even if they get data from the same query. Being able to mark fields in fragments of the same query with different <code>@required</code> arguments is important to help build ideal user experiences.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="using-action-log-vs-action-none">Using action: LOG vs action: NONE<a href="https://relay.dev/blog/2023/01/03/resilient-relay-apps/#using-action-log-vs-action-none" class="hash-link" aria-label="Direct link to Using action: LOG vs action: NONE" title="Direct link to Using action: LOG vs action: NONE" translate="no">​</a></h3>
<p>The <code>LOG</code> and <code>NONE</code> actions both have the same runtime behavior, but <code>LOG</code> will send a message to your logging mechanism of choice, logging the full path to the field that was returned as null. For most use cases where the <code>@required</code> directive is needed, <code>LOG</code> should be used over <code>NONE</code>. The only time <code>NONE</code> should be preferred is if a field is expected to be null for some users.</p>
<p>While the log entry created by using <code>action: LOG</code> isn't likely to be actionable on its own, however, it can be a useful signal as a breadcrumb for future errors. Being able to look at the history of an error and see that a specific field was unexpectedly null can help track down future errors the user might encounter in a workflow.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="when-to-use-requiredactionlognone">When to use <code>@required(action:LOG/NONE)</code><a href="https://relay.dev/blog/2023/01/03/resilient-relay-apps/#when-to-use-requiredactionlognone" class="hash-link" aria-label="Direct link to when-to-use-requiredactionlognone" title="Direct link to when-to-use-requiredactionlognone" translate="no">​</a></h3>
<p>The <code>LOG/NONE</code> actions should only be used on fields which are necessary to display Optional UI in your components. There are two distinct use cases that this shows up when designing your application</p>
<ol>
<li class="">Your component is Optional UI and shouldn't be rendered at all if a field or set of fields is null</li>
<li class="">A portion of your component is Optional UI and relies on an object type field where that object makes no sense without one or more of its child fields</li>
</ol>
<p>Let's look at a fragment that encompasses both of these use cases:</p>
<div class="language-graphql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:black;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-graphql codeBlock_bY9V thin-scrollbar" style="color:black;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:black"><span class="token keyword" style="color:#00009f">fragment</span><span class="token plain"> </span><span class="token fragment function" style="color:black">MyFragment</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">on</span><span class="token plain"> </span><span class="token class-name">Asset</span><span class="token plain"> </span><span class="token punctuation" style="color:#b58a5e">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">  </span><span class="token property" style="color:black">id</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">  </span><span class="token property" style="color:black">name</span><span class="token plain"> </span><span class="token directive function" style="color:black">@required</span><span class="token punctuation" style="color:#b58a5e">(</span><span class="token attr-name" style="color:#2a6883">action</span><span class="token punctuation" style="color:#b58a5e">:</span><span class="token plain"> </span><span class="token constant" style="color:black">LOG</span><span class="token punctuation" style="color:#b58a5e">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">  </span><span class="token property" style="color:black">slug</span><span class="token plain"> </span><span class="token directive function" style="color:black">@required</span><span class="token punctuation" style="color:#b58a5e">(</span><span class="token attr-name" style="color:#2a6883">action</span><span class="token punctuation" style="color:#b58a5e">:</span><span class="token plain"> </span><span class="token constant" style="color:black">LOG</span><span class="token punctuation" style="color:#b58a5e">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">  </span><span class="token property" style="color:black">color</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">  </span><span class="token object">supply</span><span class="token plain"> </span><span class="token punctuation" style="color:#b58a5e">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">    </span><span class="token property" style="color:black">total</span><span class="token plain"> </span><span class="token directive function" style="color:black">@required</span><span class="token punctuation" style="color:#b58a5e">(</span><span class="token attr-name" style="color:#2a6883">action</span><span class="token punctuation" style="color:#b58a5e">:</span><span class="token plain"> </span><span class="token constant" style="color:black">LOG</span><span class="token punctuation" style="color:#b58a5e">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">    </span><span class="token property" style="color:black">circulating</span><span class="token plain"> </span><span class="token directive function" style="color:black">@required</span><span class="token punctuation" style="color:#b58a5e">(</span><span class="token attr-name" style="color:#2a6883">action</span><span class="token punctuation" style="color:#b58a5e">:</span><span class="token plain"> </span><span class="token constant" style="color:black">LOG</span><span class="token punctuation" style="color:#b58a5e">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">  </span><span class="token punctuation" style="color:#b58a5e">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain"></span><span class="token punctuation" style="color:#b58a5e">}</span><br></span></code></pre></div></div>
<p>For this fragment, we're saying that the entire fragment is invalid if we don't get the name or slug fields. If those fields are returned from the server as null, we can't render this component at all. This fragment also shows how to use the <code>@required(action: LOG/NONE)</code> directive to invalidate an entire object type field. This fragment says that if we don't have either of the <code>supply.total</code> or <code>supply.circulating</code> fields, then the entire supply object is itself invalid and should be null. This nullability will then be used to hide an optional portion of this component's UI.</p>
<p>Now let's see how our component will handle the results from this query:</p>
<div class="language-tsx codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:black;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-tsx codeBlock_bY9V thin-scrollbar" style="color:black;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:black"><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> asset </span><span class="token operator" style="color:#b58a5e">=</span><span class="token plain"> </span><span class="token function" style="color:black">useFragment</span><span class="token punctuation" style="color:#b58a5e">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">  graphql</span><span class="token template-string template-punctuation string" style="color:#65822b">`</span><span class="token template-string graphql language-graphql"></span><br></span><span class="token-line" style="color:black"><span class="token template-string graphql language-graphql">    </span><span class="token template-string graphql language-graphql keyword" style="color:#00009f">fragment</span><span class="token template-string graphql language-graphql"> </span><span class="token template-string graphql language-graphql fragment function" style="color:black">MyFragment</span><span class="token template-string graphql language-graphql"> </span><span class="token template-string graphql language-graphql keyword" style="color:#00009f">on</span><span class="token template-string graphql language-graphql"> </span><span class="token template-string graphql language-graphql class-name">Asset</span><span class="token template-string graphql language-graphql"> </span><span class="token template-string graphql language-graphql punctuation" style="color:#b58a5e">{</span><span class="token template-string graphql language-graphql"></span><br></span><span class="token-line" style="color:black"><span class="token template-string graphql language-graphql">      </span><span class="token template-string graphql language-graphql property" style="color:black">id</span><span class="token template-string graphql language-graphql"></span><br></span><span class="token-line" style="color:black"><span class="token template-string graphql language-graphql">      </span><span class="token template-string graphql language-graphql property" style="color:black">name</span><span class="token template-string graphql language-graphql"> </span><span class="token template-string graphql language-graphql directive function" style="color:black">@required</span><span class="token template-string graphql language-graphql punctuation" style="color:#b58a5e">(</span><span class="token template-string graphql language-graphql attr-name" style="color:#2a6883">action</span><span class="token template-string graphql language-graphql punctuation" style="color:#b58a5e">:</span><span class="token template-string graphql language-graphql"> </span><span class="token template-string graphql language-graphql constant" style="color:black">LOG</span><span class="token template-string graphql language-graphql punctuation" style="color:#b58a5e">)</span><span class="token template-string graphql language-graphql"></span><br></span><span class="token-line" style="color:black"><span class="token template-string graphql language-graphql">      </span><span class="token template-string graphql language-graphql property" style="color:black">slug</span><span class="token template-string graphql language-graphql"> </span><span class="token template-string graphql language-graphql directive function" style="color:black">@required</span><span class="token template-string graphql language-graphql punctuation" style="color:#b58a5e">(</span><span class="token template-string graphql language-graphql attr-name" style="color:#2a6883">action</span><span class="token template-string graphql language-graphql punctuation" style="color:#b58a5e">:</span><span class="token template-string graphql language-graphql"> </span><span class="token template-string graphql language-graphql constant" style="color:black">LOG</span><span class="token template-string graphql language-graphql punctuation" style="color:#b58a5e">)</span><span class="token template-string graphql language-graphql"></span><br></span><span class="token-line" style="color:black"><span class="token template-string graphql language-graphql">      </span><span class="token template-string graphql language-graphql property" style="color:black">color</span><span class="token template-string graphql language-graphql"></span><br></span><span class="token-line" style="color:black"><span class="token template-string graphql language-graphql">      </span><span class="token template-string graphql language-graphql object">supply</span><span class="token template-string graphql language-graphql"> </span><span class="token template-string graphql language-graphql punctuation" style="color:#b58a5e">{</span><span class="token template-string graphql language-graphql"></span><br></span><span class="token-line" style="color:black"><span class="token template-string graphql language-graphql">        </span><span class="token template-string graphql language-graphql property" style="color:black">total</span><span class="token template-string graphql language-graphql"> </span><span class="token template-string graphql language-graphql directive function" style="color:black">@required</span><span class="token template-string graphql language-graphql punctuation" style="color:#b58a5e">(</span><span class="token template-string graphql language-graphql attr-name" style="color:#2a6883">action</span><span class="token template-string graphql language-graphql punctuation" style="color:#b58a5e">:</span><span class="token template-string graphql language-graphql"> </span><span class="token template-string graphql language-graphql constant" style="color:black">LOG</span><span class="token template-string graphql language-graphql punctuation" style="color:#b58a5e">)</span><span class="token template-string graphql language-graphql"></span><br></span><span class="token-line" style="color:black"><span class="token template-string graphql language-graphql">        </span><span class="token template-string graphql language-graphql property" style="color:black">circulating</span><span class="token template-string graphql language-graphql"> </span><span class="token template-string graphql language-graphql directive function" style="color:black">@required</span><span class="token template-string graphql language-graphql punctuation" style="color:#b58a5e">(</span><span class="token template-string graphql language-graphql attr-name" style="color:#2a6883">action</span><span class="token template-string graphql language-graphql punctuation" style="color:#b58a5e">:</span><span class="token template-string graphql language-graphql"> </span><span class="token template-string graphql language-graphql constant" style="color:black">LOG</span><span class="token template-string graphql language-graphql punctuation" style="color:#b58a5e">)</span><span class="token template-string graphql language-graphql"></span><br></span><span class="token-line" style="color:black"><span class="token template-string graphql language-graphql">      </span><span class="token template-string graphql language-graphql punctuation" style="color:#b58a5e">}</span><span class="token template-string graphql language-graphql"></span><br></span><span class="token-line" style="color:black"><span class="token template-string graphql language-graphql">    </span><span class="token template-string graphql language-graphql punctuation" style="color:#b58a5e">}</span><span class="token template-string graphql language-graphql"></span><br></span><span class="token-line" style="color:black"><span class="token template-string graphql language-graphql">  </span><span class="token template-string template-punctuation string" style="color:#65822b">`</span><span class="token punctuation" style="color:#b58a5e">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">  assetRef</span><span class="token punctuation" style="color:#b58a5e">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain"></span><span class="token punctuation" style="color:#b58a5e">)</span><span class="token punctuation" style="color:#b58a5e">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:black"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// If we couldn't get the required asset name or slug fields, hide this entire UI</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain"></span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#b58a5e">(</span><span class="token plain">asset </span><span class="token operator" style="color:#b58a5e">===</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">null</span><span class="token punctuation" style="color:#b58a5e">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#b58a5e">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">null</span><span class="token punctuation" style="color:#b58a5e">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain"></span><span class="token punctuation" style="color:#b58a5e">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// Otherwise hide certain portions of the UI if data is missing</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain"></span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#b58a5e">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">  </span><span class="token tag punctuation" style="color:#b58a5e">&lt;</span><span class="token tag punctuation" style="color:#b58a5e">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:black"><span class="token plain-text">    </span><span class="token tag punctuation" style="color:#b58a5e">&lt;</span><span class="token tag class-name" style="color:#00009f">Title</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#2a6883">color</span><span class="token tag script language-javascript script-punctuation punctuation" style="color:#b58a5e">=</span><span class="token tag script language-javascript punctuation" style="color:#b58a5e">{</span><span class="token tag script language-javascript" style="color:#00009f">asset</span><span class="token tag script language-javascript punctuation" style="color:#b58a5e">.</span><span class="token tag script language-javascript property-access" style="color:#00009f">color</span><span class="token tag script language-javascript punctuation" style="color:#b58a5e">}</span><span class="token tag punctuation" style="color:#b58a5e">&gt;</span><span class="token punctuation" style="color:#b58a5e">{</span><span class="token plain">asset</span><span class="token punctuation" style="color:#b58a5e">.</span><span class="token property-access">name</span><span class="token punctuation" style="color:#b58a5e">}</span><span class="token tag punctuation" style="color:#b58a5e">&lt;/</span><span class="token tag class-name" style="color:#00009f">Title</span><span class="token tag punctuation" style="color:#b58a5e">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:black"><span class="token plain-text">    </span><span class="token tag punctuation" style="color:#b58a5e">&lt;</span><span class="token tag class-name" style="color:#00009f">Subtitle</span><span class="token tag punctuation" style="color:#b58a5e">&gt;</span><span class="token punctuation" style="color:#b58a5e">{</span><span class="token plain">asset</span><span class="token punctuation" style="color:#b58a5e">.</span><span class="token property-access">slug</span><span class="token punctuation" style="color:#b58a5e">}</span><span class="token tag punctuation" style="color:#b58a5e">&lt;/</span><span class="token tag class-name" style="color:#00009f">Subtitle</span><span class="token tag punctuation" style="color:#b58a5e">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:black"><span class="token plain-text">    </span><span class="token punctuation" style="color:#b58a5e">{</span><span class="token plain">asset</span><span class="token punctuation" style="color:#b58a5e">.</span><span class="token property-access">supply</span><span class="token plain"> </span><span class="token operator" style="color:#b58a5e">&amp;&amp;</span><span class="token plain"> </span><span class="token punctuation" style="color:#b58a5e">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">      </span><span class="token tag punctuation" style="color:#b58a5e">&lt;</span><span class="token tag class-name" style="color:#00009f">SupplyStats</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#2a6883">total</span><span class="token tag script language-javascript script-punctuation punctuation" style="color:#b58a5e">=</span><span class="token tag script language-javascript punctuation" style="color:#b58a5e">{</span><span class="token tag script language-javascript" style="color:#00009f">asset</span><span class="token tag script language-javascript punctuation" style="color:#b58a5e">.</span><span class="token tag script language-javascript property-access" style="color:#00009f">supply</span><span class="token tag script language-javascript punctuation" style="color:#b58a5e">.</span><span class="token tag script language-javascript property-access" style="color:#00009f">total</span><span class="token tag script language-javascript punctuation" style="color:#b58a5e">}</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#2a6883">circulating</span><span class="token tag script language-javascript script-punctuation punctuation" style="color:#b58a5e">=</span><span class="token tag script language-javascript punctuation" style="color:#b58a5e">{</span><span class="token tag script language-javascript" style="color:#00009f">asset</span><span class="token tag script language-javascript punctuation" style="color:#b58a5e">.</span><span class="token tag script language-javascript property-access" style="color:#00009f">supply</span><span class="token tag script language-javascript punctuation" style="color:#b58a5e">.</span><span class="token tag script language-javascript property-access" style="color:#00009f">circulating</span><span class="token tag script language-javascript punctuation" style="color:#b58a5e">}</span><span class="token tag" style="color:#00009f"> </span><span class="token tag punctuation" style="color:#b58a5e">/&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">    </span><span class="token punctuation" style="color:#b58a5e">)</span><span class="token punctuation" style="color:#b58a5e">}</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:black"><span class="token plain-text">  </span><span class="token tag punctuation" style="color:#b58a5e">&lt;/</span><span class="token tag punctuation" style="color:#b58a5e">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain"></span><span class="token punctuation" style="color:#b58a5e">)</span><span class="token punctuation" style="color:#b58a5e">;</span><br></span></code></pre></div></div>
<p>The <code>@required</code> directive really shines here because it removes complex null checks that we'd have to write otherwise. Instead of having to check whether both the <code>asset.name</code> or <code>asset.slug</code> fields are null, we can simply check if our entire fragment was nulled out and prevent rendering. The same is true when checking whether we should render the SupplyStats component. We only have to check whether the parent field is null in order to know that the two subfields are non-null.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="when-to-use-requiredaction">When to use @required(action<!-- -->:THROW<!-- -->)<a href="https://relay.dev/blog/2023/01/03/resilient-relay-apps/#when-to-use-requiredaction" class="hash-link" aria-label="Direct link to when-to-use-requiredaction" title="Direct link to when-to-use-requiredaction" translate="no">​</a></h3>
<p>Using <code>@required(action: THROW)</code> is more straightforward. This action should be used on fields that are necessary to render your Expected or Critical UI component. If these fields are returned as null from the server, your component should throw an error to the nearest ErrorBoundary and the user should see an error message.</p>
<p>How far up the tree your ErrorBoundary is depends on how much of the UI you want to remove if there's an error. For example, if we're showing the user an error instead of an asset price history graph, it doesn't make sense to keep the time series buttons still in view, that entire UI should disappear as well. But we don't want to take out the entire screen if this happens either.</p>
<p>Make sure your ErrorBoundary provides a mechanism for the user to retry the failed query to see if they can get the data on a subsequent attempt. We should always pair an error message with an actionable element to let the user recover. We shouldn't rely on the user being able (or knowing) to use the pull-to-refresh to reload the screen.</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="a-note-about-using-requiredaction-throw-on-fields-in-arrays">A note about using @required(action: THROW) on fields in arrays<a href="https://relay.dev/blog/2023/01/03/resilient-relay-apps/#a-note-about-using-requiredaction-throw-on-fields-in-arrays" class="hash-link" aria-label="Direct link to A note about using @required(action: THROW) on fields in arrays" title="Direct link to A note about using @required(action: THROW) on fields in arrays" translate="no">​</a></h4>
<p>You should almost never use the <code>THROW</code> action in a component that selects both an array field and fields of that array. As an example of what not to do:</p>
<div class="language-tsx codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:black;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-tsx codeBlock_bY9V thin-scrollbar" style="color:black;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:black"><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:black">Component</span><span class="token punctuation" style="color:#b58a5e">(</span><span class="token punctuation" style="color:#b58a5e">{</span><span class="token plain"> assetPriceRef </span><span class="token punctuation" style="color:#b58a5e">}</span><span class="token punctuation" style="color:#b58a5e">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#b58a5e">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#b58a5e">{</span><span class="token plain"> quotes </span><span class="token punctuation" style="color:#b58a5e">}</span><span class="token plain"> </span><span class="token operator" style="color:#b58a5e">=</span><span class="token plain"> </span><span class="token function" style="color:black">useFragment</span><span class="token punctuation" style="color:#b58a5e">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">    graphql</span><span class="token template-string template-punctuation string" style="color:#65822b">`</span><span class="token template-string graphql language-graphql"></span><br></span><span class="token-line" style="color:black"><span class="token template-string graphql language-graphql">      </span><span class="token template-string graphql language-graphql keyword" style="color:#00009f">fragment</span><span class="token template-string graphql language-graphql"> </span><span class="token template-string graphql language-graphql fragment function" style="color:black">ComponentFragment</span><span class="token template-string graphql language-graphql"> </span><span class="token template-string graphql language-graphql keyword" style="color:#00009f">on</span><span class="token template-string graphql language-graphql"> </span><span class="token template-string graphql language-graphql class-name">AssetPriceData</span><span class="token template-string graphql language-graphql"> </span><span class="token template-string graphql language-graphql punctuation" style="color:#b58a5e">{</span><span class="token template-string graphql language-graphql"></span><br></span><span class="token-line" style="color:black"><span class="token template-string graphql language-graphql">        </span><span class="token template-string graphql language-graphql object">quotes</span><span class="token template-string graphql language-graphql"> </span><span class="token template-string graphql language-graphql punctuation" style="color:#b58a5e">{</span><span class="token template-string graphql language-graphql"></span><br></span><span class="token-line" style="color:black"><span class="token template-string graphql language-graphql">          </span><span class="token template-string graphql language-graphql comment" style="color:#999988;font-style:italic"># Returns an array of items</span><span class="token template-string graphql language-graphql"></span><br></span><span class="token-line" style="color:black"><span class="token template-string graphql language-graphql">          </span><span class="token template-string graphql language-graphql property" style="color:black">timestamp</span><span class="token template-string graphql language-graphql"></span><br></span><span class="token-line" style="color:black"><span class="token template-string graphql language-graphql">          </span><span class="token template-string graphql language-graphql property" style="color:black">price</span><span class="token template-string graphql language-graphql"> </span><span class="token template-string graphql language-graphql directive function" style="color:black">@required</span><span class="token template-string graphql language-graphql punctuation" style="color:#b58a5e">(</span><span class="token template-string graphql language-graphql attr-name" style="color:#2a6883">action</span><span class="token template-string graphql language-graphql punctuation" style="color:#b58a5e">:</span><span class="token template-string graphql language-graphql"> </span><span class="token template-string graphql language-graphql constant" style="color:black">THROW</span><span class="token template-string graphql language-graphql punctuation" style="color:#b58a5e">)</span><span class="token template-string graphql language-graphql"></span><br></span><span class="token-line" style="color:black"><span class="token template-string graphql language-graphql">        </span><span class="token template-string graphql language-graphql punctuation" style="color:#b58a5e">}</span><span class="token template-string graphql language-graphql"></span><br></span><span class="token-line" style="color:black"><span class="token template-string graphql language-graphql">      </span><span class="token template-string graphql language-graphql punctuation" style="color:#b58a5e">}</span><span class="token template-string graphql language-graphql"></span><br></span><span class="token-line" style="color:black"><span class="token template-string graphql language-graphql">    </span><span class="token template-string template-punctuation string" style="color:#65822b">`</span><span class="token punctuation" style="color:#b58a5e">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">    assetPriceRef</span><span class="token punctuation" style="color:#b58a5e">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">  </span><span class="token punctuation" style="color:#b58a5e">)</span><span class="token punctuation" style="color:#b58a5e">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain"></span><span class="token punctuation" style="color:#b58a5e">}</span><br></span></code></pre></div></div>
<p>This component selects both the <code>quotes</code> array along with the <code>timestamp</code> and <code>price</code> fields on every item in that array. Putting <code>THROW</code> on the <code>quotes</code> field would be acceptable if we want to show the user an error if we don't get back any quotes. But, putting <code>THROW</code> on the <code>price</code> field would result in showing the user an error if even a single price field in that array was null. That's probably not the behavior we want. If we got back 23 of the 24 quotes for the past day correctly, we should probably still display the results we have and just omit the empty values instead.</p>
<p>Instead, we should use <code>action: LOG/NONE</code> so that we only invalidate a single item in the array instead of all items. We can then optionally filter out the null values in the array if needed.</p>
<div class="language-tsx codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:black;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-tsx codeBlock_bY9V thin-scrollbar" style="color:black;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:black"><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:black">Component</span><span class="token punctuation" style="color:#b58a5e">(</span><span class="token punctuation" style="color:#b58a5e">{</span><span class="token plain"> assetPriceRef </span><span class="token punctuation" style="color:#b58a5e">}</span><span class="token punctuation" style="color:#b58a5e">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#b58a5e">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#b58a5e">{</span><span class="token plain"> quotes </span><span class="token punctuation" style="color:#b58a5e">}</span><span class="token plain"> </span><span class="token operator" style="color:#b58a5e">=</span><span class="token plain"> </span><span class="token function" style="color:black">useFragment</span><span class="token punctuation" style="color:#b58a5e">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">    graphql</span><span class="token template-string template-punctuation string" style="color:#65822b">`</span><span class="token template-string graphql language-graphql"></span><br></span><span class="token-line" style="color:black"><span class="token template-string graphql language-graphql">      </span><span class="token template-string graphql language-graphql keyword" style="color:#00009f">fragment</span><span class="token template-string graphql language-graphql"> </span><span class="token template-string graphql language-graphql fragment function" style="color:black">ComponentFragment</span><span class="token template-string graphql language-graphql"> </span><span class="token template-string graphql language-graphql keyword" style="color:#00009f">on</span><span class="token template-string graphql language-graphql"> </span><span class="token template-string graphql language-graphql class-name">AssetPriceData</span><span class="token template-string graphql language-graphql"> </span><span class="token template-string graphql language-graphql punctuation" style="color:#b58a5e">{</span><span class="token template-string graphql language-graphql"></span><br></span><span class="token-line" style="color:black"><span class="token template-string graphql language-graphql">        </span><span class="token template-string graphql language-graphql object">quotes</span><span class="token template-string graphql language-graphql"> </span><span class="token template-string graphql language-graphql punctuation" style="color:#b58a5e">{</span><span class="token template-string graphql language-graphql"></span><br></span><span class="token-line" style="color:black"><span class="token template-string graphql language-graphql">          </span><span class="token template-string graphql language-graphql comment" style="color:#999988;font-style:italic"># Returns an array of items</span><span class="token template-string graphql language-graphql"></span><br></span><span class="token-line" style="color:black"><span class="token template-string graphql language-graphql">          </span><span class="token template-string graphql language-graphql property" style="color:black">timestamp</span><span class="token template-string graphql language-graphql"></span><br></span><span class="token-line" style="color:black"><span class="token template-string graphql language-graphql">          </span><span class="token template-string graphql language-graphql property" style="color:black">price</span><span class="token template-string graphql language-graphql"> </span><span class="token template-string graphql language-graphql directive function" style="color:black">@required</span><span class="token template-string graphql language-graphql punctuation" style="color:#b58a5e">(</span><span class="token template-string graphql language-graphql attr-name" style="color:#2a6883">action</span><span class="token template-string graphql language-graphql punctuation" style="color:#b58a5e">:</span><span class="token template-string graphql language-graphql"> </span><span class="token template-string graphql language-graphql constant" style="color:black">LOG</span><span class="token template-string graphql language-graphql punctuation" style="color:#b58a5e">)</span><span class="token template-string graphql language-graphql"></span><br></span><span class="token-line" style="color:black"><span class="token template-string graphql language-graphql">        </span><span class="token template-string graphql language-graphql punctuation" style="color:#b58a5e">}</span><span class="token template-string graphql language-graphql"></span><br></span><span class="token-line" style="color:black"><span class="token template-string graphql language-graphql">      </span><span class="token template-string graphql language-graphql punctuation" style="color:#b58a5e">}</span><span class="token template-string graphql language-graphql"></span><br></span><span class="token-line" style="color:black"><span class="token template-string graphql language-graphql">    </span><span class="token template-string template-punctuation string" style="color:#65822b">`</span><span class="token punctuation" style="color:#b58a5e">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">    assetPriceRef</span><span class="token punctuation" style="color:#b58a5e">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">  </span><span class="token punctuation" style="color:#b58a5e">)</span><span class="token punctuation" style="color:#b58a5e">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> validQuotes </span><span class="token operator" style="color:#b58a5e">=</span><span class="token plain"> quotes</span><span class="token punctuation" style="color:#b58a5e">.</span><span class="token method function property-access" style="color:black">filter</span><span class="token punctuation" style="color:#b58a5e">(</span><span class="token plain">removeNull</span><span class="token punctuation" style="color:#b58a5e">)</span><span class="token punctuation" style="color:#b58a5e">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain"></span><span class="token punctuation" style="color:#b58a5e">}</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="when-not-to-use-required-on-a-field">When NOT to use @required on a field<a href="https://relay.dev/blog/2023/01/03/resilient-relay-apps/#when-not-to-use-required-on-a-field" class="hash-link" aria-label="Direct link to When NOT to use @required on a field" title="Direct link to When NOT to use @required on a field" translate="no">​</a></h3>
<p>The unhelpful answer to this question would be “don't use <code>@required</code> when a field isn't required”. That answer trivializes the decision of what is required and what isn't when the answer is usually more nuanced, especially when your fragment has a dozen fields or more. However, we can follow a number of best practices to decide whether to mark a field as required or not. Again, it is important that you work with your PMs and Designers to help you with these decisions.</p>
<p>There is also a fine line between when to omit the <code>@required</code> directive vs using it with the <code>LOG/NONE</code> action. The primary difference is that you should omit the <code>@required</code> directive when the UI rendered by that field is Optional UI.</p>
<p>Some components in your application can render a combination of different classifications of UI. For example, a single component might be responsible for displaying both the current price of an asset as well as what percent of users have bought or sold the asset over some time frame. This means the component is mixing both Critical UI (asset price) and Optional UI (buy/sell stats).</p>
<p>If a field is used to render optional content which can instead be omitted from the UI entirely without causing confusion for the user (remember, that's the definition of Optional UI) then you shouldn't use the <code>@required</code> directive on that field. Instead, you should add checks to your code to omit the UI if the field is null.</p>
<div class="language-tsx codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:black;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-tsx codeBlock_bY9V thin-scrollbar" style="color:black;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:black"><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:black">SomeComponent</span><span class="token punctuation" style="color:#b58a5e">(</span><span class="token punctuation" style="color:#b58a5e">{</span><span class="token plain"> queryRef </span><span class="token punctuation" style="color:#b58a5e">}</span><span class="token punctuation" style="color:#b58a5e">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#b58a5e">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#b58a5e">{</span><span class="token plain"> asset </span><span class="token punctuation" style="color:#b58a5e">}</span><span class="token plain"> </span><span class="token operator" style="color:#b58a5e">=</span><span class="token plain"> </span><span class="token function" style="color:black">useFragment</span><span class="token punctuation" style="color:#b58a5e">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">    graphql</span><span class="token template-string template-punctuation string" style="color:#65822b">`</span><span class="token template-string graphql language-graphql"></span><br></span><span class="token-line" style="color:black"><span class="token template-string graphql language-graphql">   </span><span class="token template-string graphql language-graphql object">asset</span><span class="token template-string graphql language-graphql"> </span><span class="token template-string graphql language-graphql punctuation" style="color:#b58a5e">{</span><span class="token template-string graphql language-graphql"></span><br></span><span class="token-line" style="color:black"><span class="token template-string graphql language-graphql">     </span><span class="token template-string graphql language-graphql property" style="color:black">latestQuote</span><span class="token template-string graphql language-graphql"> </span><span class="token template-string graphql language-graphql directive function" style="color:black">@required</span><span class="token template-string graphql language-graphql punctuation" style="color:#b58a5e">(</span><span class="token template-string graphql language-graphql attr-name" style="color:#2a6883">action</span><span class="token template-string graphql language-graphql punctuation" style="color:#b58a5e">:</span><span class="token template-string graphql language-graphql"> </span><span class="token template-string graphql language-graphql constant" style="color:black">THROW</span><span class="token template-string graphql language-graphql punctuation" style="color:#b58a5e">)</span><span class="token template-string graphql language-graphql"> </span><span class="token template-string graphql language-graphql comment" style="color:#999988;font-style:italic"># Required data</span><span class="token template-string graphql language-graphql"></span><br></span><span class="token-line" style="color:black"><span class="token template-string graphql language-graphql">     </span><span class="token template-string graphql language-graphql property" style="color:black">buyPercent</span><span class="token template-string graphql language-graphql">  </span><span class="token template-string graphql language-graphql comment" style="color:#999988;font-style:italic"># Optional data</span><span class="token template-string graphql language-graphql"></span><br></span><span class="token-line" style="color:black"><span class="token template-string graphql language-graphql">   </span><span class="token template-string graphql language-graphql punctuation" style="color:#b58a5e">}</span><span class="token template-string template-punctuation string" style="color:#65822b">`</span><span class="token punctuation" style="color:#b58a5e">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">    queryRef</span><span class="token punctuation" style="color:#b58a5e">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">  </span><span class="token punctuation" style="color:#b58a5e">)</span><span class="token punctuation" style="color:#b58a5e">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:black"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#b58a5e">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">    </span><span class="token tag punctuation" style="color:#b58a5e">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#b58a5e">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:black"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:#b58a5e">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#b58a5e">&gt;</span><span class="token plain-text">Price: </span><span class="token punctuation" style="color:#b58a5e">{</span><span class="token plain">asset</span><span class="token punctuation" style="color:#b58a5e">.</span><span class="token property-access">latestQuote</span><span class="token punctuation" style="color:#b58a5e">}</span><span class="token tag punctuation" style="color:#b58a5e">&lt;/</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#b58a5e">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:black"><span class="token plain-text">      </span><span class="token punctuation" style="color:#b58a5e">{</span><span class="token plain">asset</span><span class="token punctuation" style="color:#b58a5e">.</span><span class="token property-access">buyPercent</span><span class="token plain"> </span><span class="token operator" style="color:#b58a5e">!==</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">null</span><span class="token plain"> </span><span class="token operator" style="color:#b58a5e">&amp;&amp;</span><span class="token plain"> </span><span class="token punctuation" style="color:#b58a5e">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">        </span><span class="token tag punctuation" style="color:#b58a5e">&lt;</span><span class="token tag punctuation" style="color:#b58a5e">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:black"><span class="token plain-text">          </span><span class="token tag punctuation" style="color:#b58a5e">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#b58a5e">&gt;</span><span class="token plain-text">Buy Percent: </span><span class="token punctuation" style="color:#b58a5e">{</span><span class="token plain">asset</span><span class="token punctuation" style="color:#b58a5e">.</span><span class="token property-access">buyPercent</span><span class="token punctuation" style="color:#b58a5e">}</span><span class="token tag punctuation" style="color:#b58a5e">&lt;/</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#b58a5e">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:black"><span class="token plain-text">          </span><span class="token tag punctuation" style="color:#b58a5e">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#b58a5e">&gt;</span><span class="token plain-text">Sell Percent: </span><span class="token punctuation" style="color:#b58a5e">{</span><span class="token number" style="color:black">1</span><span class="token plain"> </span><span class="token operator" style="color:#b58a5e">-</span><span class="token plain"> asset</span><span class="token punctuation" style="color:#b58a5e">.</span><span class="token property-access">buyPercent</span><span class="token punctuation" style="color:#b58a5e">}</span><span class="token tag punctuation" style="color:#b58a5e">&lt;/</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#b58a5e">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:black"><span class="token plain-text">        </span><span class="token tag punctuation" style="color:#b58a5e">&lt;/</span><span class="token tag punctuation" style="color:#b58a5e">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">      </span><span class="token punctuation" style="color:#b58a5e">)</span><span class="token punctuation" style="color:#b58a5e">}</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:black"><span class="token plain-text">    </span><span class="token tag punctuation" style="color:#b58a5e">&lt;/</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#b58a5e">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">  </span><span class="token punctuation" style="color:#b58a5e">)</span><span class="token punctuation" style="color:#b58a5e">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain"></span><span class="token punctuation" style="color:#b58a5e">}</span><br></span></code></pre></div></div>
<p>In this example it would be incorrect to use <code>@required(action: LOG/NONE)</code> on the <code>buyPercent</code> field because that would invalidate the entire fragment which isn't the behavior we want.</p>
<p>Another less common use case of when to omit the <code>@required</code> directive is when you can provide a safe fallback value. Providing a fallback/default value for a field can be very dangerous if done incorrectly. While there are a few cases where it's potentially safe to fall back to a default value, it's generally pretty rare and should be avoided. However, if you can provide a safe fallback value, you should avoid adding <code>@required</code> to that field and instead use a fallback value.</p>
<p>A couple of guidelines of when to provide a fallback value:</p>
<ul>
<li class="">Fallback values for numeric fields (numbers or strings that represent numbers) should not be used.<!-- -->
<ul>
<li class="">Using a 0 in place of a missing value will always create more confusion for the user. Coinbase is a financial company and if we can't display accurate values to users, we shouldn't be displaying them at all. Showing a user that their account balance is $0.00 is clearly much worse than showing them an error message. That's an obvious use case, but even places such as the price change percent for an asset, APY% for Coinbase Card, or the amount a user can make via Coinbase Earn should never show 0 if we don't have the actual value.</li>
</ul>
</li>
<li class="">Fallback values for boolean fields should be used with caution.<!-- -->
<ul>
<li class="">The first choice for a fallback for boolean fields is usually to set the field to false. Depending on what the boolean field represents, falling back to false can create a worse customer experience than showing the user an error. Falling back to false for a field like <code>isEligibleForOffer</code> is probably acceptable because that is likely showing Optional content. Falling back to false for a field like <code>hasCoinbaseOneSubscription</code> would not be acceptable because for a user who is a CoinbaseOne subscriber the content is Expected and the user is going to be confused about why that UI is missing in the app</li>
</ul>
</li>
<li class="">Falling back to an empty array for array fields should be used with caution.<!-- -->
<ul>
<li class="">If you're showing the user their list of Coinbase Card transactions, falling back to an empty array is a bad idea, but if you're showing the user a list of recently added assets, it's probably okay to fallback to an empty array to omit the UI from displaying since the component is already going to have to deal with the case of the array being empty.</li>
</ul>
</li>
<li class="">String fields should usually just deal with null instead.<!-- -->
<ul>
<li class="">In some cases, you might want to fallback to an empty string for string fields that are returned as null, but usually this creates the same code path if you just leave the field as null. Most string fields in a schema aren't expected to be empty so falling back to an empty string can create negative user experiences where the user will be shown an empty string instead of actual content.</li>
</ul>
</li>
</ul>
<div class="language-tsx codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:black;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-tsx codeBlock_bY9V thin-scrollbar" style="color:black;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:black"><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:black">SomeComponent</span><span class="token punctuation" style="color:#b58a5e">(</span><span class="token punctuation" style="color:#b58a5e">{</span><span class="token plain"> queryRef </span><span class="token punctuation" style="color:#b58a5e">}</span><span class="token punctuation" style="color:#b58a5e">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#b58a5e">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> asset </span><span class="token operator" style="color:#b58a5e">=</span><span class="token plain"> </span><span class="token function" style="color:black">useFragment</span><span class="token punctuation" style="color:#b58a5e">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">    graphql</span><span class="token template-string template-punctuation string" style="color:#65822b">`</span><span class="token template-string graphql language-graphql"></span><br></span><span class="token-line" style="color:black"><span class="token template-string graphql language-graphql">      </span><span class="token template-string graphql language-graphql keyword" style="color:#00009f">fragment</span><span class="token template-string graphql language-graphql"> </span><span class="token template-string graphql language-graphql fragment function" style="color:black">MyFragment</span><span class="token template-string graphql language-graphql"> </span><span class="token template-string graphql language-graphql keyword" style="color:#00009f">on</span><span class="token template-string graphql language-graphql"> </span><span class="token template-string graphql language-graphql class-name">Asset</span><span class="token template-string graphql language-graphql"> </span><span class="token template-string graphql language-graphql punctuation" style="color:#b58a5e">{</span><span class="token template-string graphql language-graphql"></span><br></span><span class="token-line" style="color:black"><span class="token template-string graphql language-graphql">        </span><span class="token template-string graphql language-graphql property" style="color:black">canTrade</span><span class="token template-string graphql language-graphql"> </span><span class="token template-string graphql language-graphql directive function" style="color:black">@required</span><span class="token template-string graphql language-graphql punctuation" style="color:#b58a5e">(</span><span class="token template-string graphql language-graphql attr-name" style="color:#2a6883">action</span><span class="token template-string graphql language-graphql punctuation" style="color:#b58a5e">:</span><span class="token template-string graphql language-graphql"> </span><span class="token template-string graphql language-graphql constant" style="color:black">THROW</span><span class="token template-string graphql language-graphql punctuation" style="color:#b58a5e">)</span><span class="token template-string graphql language-graphql"> </span><span class="token template-string graphql language-graphql comment" style="color:#999988;font-style:italic"># Required data</span><span class="token template-string graphql language-graphql"></span><br></span><span class="token-line" style="color:black"><span class="token template-string graphql language-graphql">        </span><span class="token template-string graphql language-graphql property" style="color:black">hasOfferToStake</span><span class="token template-string graphql language-graphql"> </span><span class="token template-string graphql language-graphql comment" style="color:#999988;font-style:italic"># Optional data</span><span class="token template-string graphql language-graphql"></span><br></span><span class="token-line" style="color:black"><span class="token template-string graphql language-graphql">      </span><span class="token template-string graphql language-graphql punctuation" style="color:#b58a5e">}</span><span class="token template-string graphql language-graphql"></span><br></span><span class="token-line" style="color:black"><span class="token template-string graphql language-graphql">    </span><span class="token template-string template-punctuation string" style="color:#65822b">`</span><span class="token punctuation" style="color:#b58a5e">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">    assetRef</span><span class="token punctuation" style="color:#b58a5e">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">  </span><span class="token punctuation" style="color:#b58a5e">)</span><span class="token punctuation" style="color:#b58a5e">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:black"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> showStakeOffer </span><span class="token operator" style="color:#b58a5e">=</span><span class="token plain"> asset</span><span class="token punctuation" style="color:#b58a5e">.</span><span class="token property-access">hasOfferToStake</span><span class="token plain"> </span><span class="token operator" style="color:#b58a5e">??</span><span class="token plain"> </span><span class="token boolean" style="color:black">false</span><span class="token punctuation" style="color:#b58a5e">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:black"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#b58a5e">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">    </span><span class="token tag punctuation" style="color:#b58a5e">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#b58a5e">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:black"><span class="token plain-text">      </span><span class="token punctuation" style="color:#b58a5e">{</span><span class="token plain">asset</span><span class="token punctuation" style="color:#b58a5e">.</span><span class="token property-access">canTrade</span><span class="token plain"> </span><span class="token operator" style="color:#b58a5e">&amp;&amp;</span><span class="token plain"> </span><span class="token tag punctuation" style="color:#b58a5e">&lt;</span><span class="token tag class-name" style="color:#00009f">Button</span><span class="token tag punctuation" style="color:#b58a5e">&gt;</span><span class="token plain-text">Trade</span><span class="token tag punctuation" style="color:#b58a5e">&lt;/</span><span class="token tag class-name" style="color:#00009f">Button</span><span class="token tag punctuation" style="color:#b58a5e">&gt;</span><span class="token punctuation" style="color:#b58a5e">}</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:black"><span class="token plain-text">      </span><span class="token punctuation" style="color:#b58a5e">{</span><span class="token plain">showStakeOffer </span><span class="token operator" style="color:#b58a5e">&amp;&amp;</span><span class="token plain"> </span><span class="token tag punctuation" style="color:#b58a5e">&lt;</span><span class="token tag class-name" style="color:#00009f">Button</span><span class="token tag punctuation" style="color:#b58a5e">&gt;</span><span class="token plain-text">Stake your currency</span><span class="token tag punctuation" style="color:#b58a5e">&lt;/</span><span class="token tag class-name" style="color:#00009f">Button</span><span class="token tag punctuation" style="color:#b58a5e">&gt;</span><span class="token punctuation" style="color:#b58a5e">}</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:black"><span class="token plain-text">    </span><span class="token tag punctuation" style="color:#b58a5e">&lt;/</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#b58a5e">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">  </span><span class="token punctuation" style="color:#b58a5e">)</span><span class="token punctuation" style="color:#b58a5e">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain"></span><span class="token punctuation" style="color:#b58a5e">}</span><br></span></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="summary">Summary<a href="https://relay.dev/blog/2023/01/03/resilient-relay-apps/#summary" class="hash-link" aria-label="Direct link to Summary" title="Direct link to Summary" translate="no">​</a></h2>
<p>If you've taken anything away from this document, hopefully, it's that a lot of thought needs to go into how to handle downtime and service interruptions. Handling failure states is an important part of building world-class applications. Make sure your design and PM team are on the same page with your team when scoping out new features. If they don't give you advice on what to show the user when data is missing, push back to come to a consensus as a team on these decisions.</p>
<p>Relay can be a powerful tool in helping deal with application failures. Its granular ability to help you decide how to deal with failure might involve more work than you're used to. However, this extra effort pays off in the long run and goes a long way to improving customer experience with your applications.</p>]]></content>
        <author>
            <name>Ernie Turner</name>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Introducing the new Relay compiler]]></title>
        <id>https://relay.dev/blog/2021/12/08/introducing-the-new-relay-compiler/</id>
        <link href="https://relay.dev/blog/2021/12/08/introducing-the-new-relay-compiler/"/>
        <updated>2021-12-08T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Introducing the new Relay compiler]]></summary>
        <content type="html"><![CDATA[<p>We're extremely excited to release a preview of the new, Rust-based Relay compiler to open source today (as <a href="https://github.com/facebook/relay/releases/tag/v13.0.0-rc.1" target="_blank" rel="noopener noreferrer" class=""><code>v13.0.0-rc.1</code></a>)! This new compiler is faster, supports new runtime features, and provides a strong foundation for additional growth in the future.</p>
<p>Leading up to this release, Meta's codebase had been growing without signs of stopping. At our scale, the time it took to compile all of the queries in our codebase was increasing at the direct expense of developer productivity. Though we tried a number of strategies to optimize our JavaScript-based compiler (discussed below), our ability to incrementally eke out performance gains could not keep up with the growth in the number of queries in our codebase.</p>
<p>So, we decided to rewrite the compiler in Rust. We chose Rust because it is fast, memory-safe, and makes it easy to safely share large data structures across threads. Development began in early 2020, and the compiler shipped internally at the end of that year. The rollout was smooth, with no interruptions to application development. Initial internal benchmarks indicated that the compiler performed nearly 5x better on average, and nearly 7x better at P95. We've further improved the performance of the compiler since then.</p>
<p>This post will explore why Relay has a compiler, what we hope to unlock with the new compiler, its new features, and why we chose to use the Rust language. If you're in a hurry to get started using the new compiler, check out <a href="https://github.com/facebook/relay/tree/main/packages/relay-compiler" target="_blank" rel="noopener noreferrer" class="">the compiler package README</a> or the <a href="https://github.com/facebook/relay/releases/tag/v13.0.0-rc.1" target="_blank" rel="noopener noreferrer" class="">release notes</a> instead!</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="why-does-relay-have-a-compiler">Why does Relay have a compiler?<a href="https://relay.dev/blog/2021/12/08/introducing-the-new-relay-compiler/#why-does-relay-have-a-compiler" class="hash-link" aria-label="Direct link to Why does Relay have a compiler?" title="Direct link to Why does Relay have a compiler?" translate="no">​</a></h2>
<p>Relay has a compiler in order to provide stability guarantees and achieve great runtime performance.</p>
<p>To understand why, consider the workflow of using the framework. With Relay, developers use a declarative language called GraphQL to specify what data each component needs, but not how to get it. The compiler then stitches these components' data dependencies into queries that fetch all of the data for a given page and precomputes artifacts that give Relay applications such a high level of performance and stability.</p>
<p>In this workflow, the compiler</p>
<ul>
<li class="">allows components to be reasoned about in isolation, making large classes of bugs impossible, and</li>
<li class="">shifts as much work as possible to build time, significantly improving the runtime performance of applications that use Relay.</li>
</ul>
<p>Let's interrogate each of these in turn.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="supporting-local-reasoning">Supporting local reasoning<a href="https://relay.dev/blog/2021/12/08/introducing-the-new-relay-compiler/#supporting-local-reasoning" class="hash-link" aria-label="Direct link to Supporting local reasoning" title="Direct link to Supporting local reasoning" translate="no">​</a></h3>
<p>With Relay, a component specifies only its own data requirements through the use of GraphQL fragments. The compiler then stitches these components data dependencies into queries that fetch all of the data for a given page. Developers can focus on writing a component without worrying how its data dependencies fit into a larger query.</p>
<p>However, Relay takes this local reasoning a step further. The compiler also generates files that are used by the Relay runtime to read out just the data selected by a given component's fragment (we call this <a href="https://relay.dev/docs/principles-and-architecture/thinking-in-relay/#data-masking" target="_blank" rel="noopener noreferrer" class="">data masking</a>). So a component never accesses (in practice, not just at the type level!) any data that it didn't explicitly request.</p>
<p>Thus, modifying one component's data dependencies cannot affect the data another component sees, meaning that <strong>developers can reason about components in isolation.</strong> This gives Relay apps an unparalleled level of stability and makes large classes of bugs impossible, and is a key part of why Relay can scale to many developers touching the same codebase.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="improved-runtime-performance">Improved runtime performance<a href="https://relay.dev/blog/2021/12/08/introducing-the-new-relay-compiler/#improved-runtime-performance" class="hash-link" aria-label="Direct link to Improved runtime performance" title="Direct link to Improved runtime performance" translate="no">​</a></h3>
<p>Relay also makes use of the compiler to shift as much work as possible to build time, improving the performance of Relay apps.</p>
<p>Because the Relay compiler has global knowledge of all components' data dependencies, it is able to write queries that are as good — and generally even better — than they would be if they had been written by hand. It's able to do this by optimizing queries in ways that would be impractically slow at runtime. For example, it prunes branches that can never be accessed from the generated queries and flattens identical sections of queries.</p>
<p>And because these queries are generated at build time, Relay applications never generate abstract syntax trees (ASTs) from GraphQL fragments, manipulate those ASTs, or generate query text at runtime. Instead, the Relay compiler replaces an application's GraphQL fragments with precomputed, optimized instructions (as plain ol' Javascript data structures) that describe how to write network data to the store and read it back out.</p>
<p>An added benefit of this arrangement is that a Relay application bundle includes neither the schema nor — when using persisted queries — the string representation of the GraphQL fragments. This helps to reduce application size, saving users' bandwidth and improving application performance.</p>
<p>In fact, the new compiler goes further and saves users' bandwidth in another way — Relay can inform an application's server about each query text at build time and generate a unique query ID, meaning that the application never needs to send the potentially very long query string over users' slow networks. When using such persisted queries, the only things that must be sent over the wire to make a network request are the query ID and the query variables!</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-does-the-new-compiler-enable">What does the new compiler enable?<a href="https://relay.dev/blog/2021/12/08/introducing-the-new-relay-compiler/#what-does-the-new-compiler-enable" class="hash-link" aria-label="Direct link to What does the new compiler enable?" title="Direct link to What does the new compiler enable?" translate="no">​</a></h2>
<p>Compiled languages are sometimes perceived as introducing friction and slowing developers down when compared to dynamic languages. However, Relay takes advantage of the compiler to reduce friction and make common developer tasks easier. For example, Relay exposes high-level primitives for common interactions that are easy to get subtly wrong, such as pagination and refetching a query with new variables.</p>
<p>What these interactions have in common is that they require generating a new query from an old one, and thus involve boilerplate and duplication — an ideal target for automation. Relay takes advantage of the compiler's global knowledge to empower developers to enable pagination and refetching by adding one directive and changing one function call. That's it.</p>
<p><strong>But giving developers the ability to easily add pagination is just the tip of the iceberg.</strong> Our vision for the compiler is that it provides even more high-level tools for shipping features and avoiding boilerplate, gives developers real-time assistance and insights, and is made up of parts that can be used by other tools for working with GraphQL.</p>
<p>A primary goal of this project was that the rewritten compiler's architecture should set us up to achieve this vision over the coming years.</p>
<p>And while we're not there yet, we have made significant achievements on each of the criteria.</p>
<p>For example, the new compiler ships with support for the new <code>@required</code> directive, which will nullify the parent linked field or throw an error if a given subfield is null when read out. This may sound like a trivial quality-of-life improvement, but if half of your component's code is null checks, <code>@required</code> starts to look pretty good!</p>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>A component without <code>@required</code></div><div class="admonitionContent_BuS1"><div style="text-align:center;padding-top:1rem;padding-bottom:1rem"><img src="https://relay.dev/assets/images/2021-12-08-introducing-the-new-relay-compiler-pre-required-34d7c573b373c37510cb92f5d5bac681.png" width="90%" alt="" title="A component with null-checking boilerplate"></div></div></div>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>And with <code>@required</code>:</div><div class="admonitionContent_BuS1"><div style="text-align:center;padding-top:1rem;padding-bottom:1rem"><img src="https://relay.dev/assets/images/2021-12-08-introducing-the-new-relay-compiler-post-required-5d8885970f034d798e022fa46fb6508a.png" width="90%" alt="" title="A component with less null-checking boilerplate, due to the use of the `@required` directive"></div></div></div>
<p>Next, the compiler powers an internal-only VSCode extension that autocompletes field names when you type and shows type information on hover, among many other features. We haven't made it public, yet, but we hope to at some point! Our experience is that this VSCode extension makes working with GraphQL data much easier and more intuitive.</p>
<p>Lastly, the new compiler was written as a series of independent modules that can be reused by other GraphQL tools. We call this the Relay compiler platform. Internally, these modules are being reused for other code generation tools and for other GraphQL clients for different platforms.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="compiler-performance">Compiler performance<a href="https://relay.dev/blog/2021/12/08/introducing-the-new-relay-compiler/#compiler-performance" class="hash-link" aria-label="Direct link to Compiler performance" title="Direct link to Compiler performance" translate="no">​</a></h2>
<p>So far, we've discussed why Relay has a compiler and what we hope the rewrite enables. But we haven't discussed why we decided to rewrite the compiler in 2020: performance.</p>
<p>Prior to the decision to rewrite the compiler, the time it took to compile all of the queries in our codebase was gradually, but unrelentingly, slowing as our codebase grew. Our ability to eke out performance gains could not keep up with the growth in the number of queries in our codebase, and we saw no incremental way out of this predicament.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="reaching-the-end-of-javascript">Reaching the end of JavaScript<a href="https://relay.dev/blog/2021/12/08/introducing-the-new-relay-compiler/#reaching-the-end-of-javascript" class="hash-link" aria-label="Direct link to Reaching the end of JavaScript" title="Direct link to Reaching the end of JavaScript" translate="no">​</a></h3>
<p>The previous compiler was written in JavaScript. This was a natural choice of language for several reasons: it was the language with which our team had the most experience, the language in which the Relay runtime was written (allowing us to share code between the compiler and runtime), and the language in which the GraphQL reference implementation and our mobile GraphQL tools were written.</p>
<p>The compiler's performance remained reasonable for quite some time: Node/V8 comes with a heavily-optimized JIT compiler and garbage collector, and can be quite fast if you're careful (we were). But compilation times were growing.</p>
<p>We tried a number of strategies to keep up:</p>
<ul>
<li class="">We had made the compiler incremental. In response to a change, it only recompiled the dependencies that were affected by that change.</li>
<li class="">We had identified which transforms were slow (namely, flatten), and made the algorithmic improvements we could (such as adding memoization).</li>
<li class="">The official <code>graphql</code> npm package's GraphQL schema representation took multiple gigabytes of memory to represent our schema, so we replaced it with a custom fork.</li>
<li class="">We made profiler-guided micro-optimizations in our hottest code paths. For example, we stopped using the <code>...</code> operator to clone and modify objects, instead preferring to explicitly list out the properties of objects when copying them. This preserved the object's hidden class, and enabled the code to be better JIT-optimized.</li>
<li class="">We restructured the compiler to shell out to multiple workers, with each worker handling a single schema. Projects with multiple schemas are uncommon outside of Meta, so even with this, most users would have been using a single-threaded compiler.</li>
</ul>
<p>These optimizations weren't enough to keep pace with the rapid internal adoption of Relay.</p>
<p>The biggest challenge was that NodeJS does not support multithreaded programs with shared memory. The best one can do is to start multiple workers that communicate by passing messages.</p>
<p>This works well in some scenarios. For example, Jest employs this pattern and makes use of all cores when running tests of transforming files. This is a good fit because Jest doesn't need to share much data or memory between processes.</p>
<p>On the other hand, our schema is simply too large to have multiple instances in memory, so there was simply no good way to efficiently parallelize the Relay compiler with more than one thread per schema in JavaScript.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="deciding-on-rust">Deciding on Rust<a href="https://relay.dev/blog/2021/12/08/introducing-the-new-relay-compiler/#deciding-on-rust" class="hash-link" aria-label="Direct link to Deciding on Rust" title="Direct link to Deciding on Rust" translate="no">​</a></h3>
<p>After we decided to rewrite the compiler, we evaluated many languages to see which would meet the needs of our project. We wanted a language that was fast, memory-safe and supported concurrency — preferably with concurrency bugs caught at build time, not at runtime. At the same time we wanted a language that was well-supported internally. This narrowed it down to a few choices:</p>
<ul>
<li class="">C++ met most of the criteria, but felt difficult to learn. And, the compiler doesn't assist with safety as much as we'd like.</li>
<li class="">Java was probably also a decent choice. It can be fast and is multi-core, but provides less low-level control.</li>
<li class="">OCaml is a proven choice in the compiler space, but multi-threading is challenging.</li>
<li class="">Rust is fast, memory-safe, and supports concurrency. It makes it easy to safely share large data structures across threads. With the general excitement around Rust, some previous experience on our team, and usage by other teams at Facebook, this was our clear top choice.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="internal-rollout">Internal rollout<a href="https://relay.dev/blog/2021/12/08/introducing-the-new-relay-compiler/#internal-rollout" class="hash-link" aria-label="Direct link to Internal rollout" title="Direct link to Internal rollout" translate="no">​</a></h2>
<p>Rust turned out to be a great fit! The team of mostly JavaScript developers found Rust easy to adopt. And, Rust's advanced type system caught many errors at build time, helping us maintain a high velocity.</p>
<p>We began development in early 2020, and rolled out the compiler internally at the end of that year. Initial internal benchmarks indicated that the compiler performed nearly 5x better on average, and nearly 7x better at P95. We've further improved the performance of the compiler since then.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="release-in-oss">Release in OSS<a href="https://relay.dev/blog/2021/12/08/introducing-the-new-relay-compiler/#release-in-oss" class="hash-link" aria-label="Direct link to Release in OSS" title="Direct link to Release in OSS" translate="no">​</a></h2>
<p>Today, we're excited to publish the new version of the compiler, as part of the Relay v13. New compiler features include:</p>
<ul>
<li class=""><a href="https://relay.dev/docs/guides/required-directive/" target="_blank" rel="noopener noreferrer" class="">The <code>@required</code> directive.</a></li>
<li class="">The <code>@no_inline</code> directive, which can be used to prevent common fragments from being inlined, resulting in smaller generated files.</li>
<li class="">Validation for conflicting GraphQL fields, arguments and directives</li>
<li class=""><a href="https://github.com/facebook/relay/pull/3182" target="_blank" rel="noopener noreferrer" class="">Support for TypeScript type generation</a></li>
<li class="">Support for remote query persisting.</li>
</ul>
<p>You can find more information about the compiler in the <a href="https://github.com/facebook/relay/tree/main/packages/relay-compiler" target="_blank" rel="noopener noreferrer" class="">README</a> and in the <a href="https://github.com/facebook/relay/releases/tag/v13.0.0-rc.1" target="_blank" rel="noopener noreferrer" class="">release notes</a>!</p>
<p>We're continuing to develop features within the compiler, such as giving developers the ability to access derived values on the graph, adding support for a more ergonomic syntax for updating local data, and fully fleshing out our VSCode extension, all of which we hope to release to open source. We're proud of this release, but there's still a lot more to come!</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="thanks">Thanks<a href="https://relay.dev/blog/2021/12/08/introducing-the-new-relay-compiler/#thanks" class="hash-link" aria-label="Direct link to Thanks" title="Direct link to Thanks" translate="no">​</a></h2>
<p>Thank you Joe Savona, Lauren Tan, Jason Bonta and Jordan Eldredge for providing amazing feedback on this blog post. Thank you ch1ffa, robrichard, orta and sync for filing issues related to compiler bugs. Thank you to MaartenStaa for adding TypeScript support. Thank you @andrewingram for pointing out how difficult it is to enable the <code>@required</code> directive, which is now enabled by default. There are many others that contributed — this was truly a community effort!</p>]]></content>
        <author>
            <name>Robert Balicki, Tianyu Yao &amp; Andrey Lunyov</name>
        </author>
        <category label="relay-compiler" term="relay-compiler"/>
        <category label="rust" term="rust"/>
        <category label="required" term="required"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Introducing Relay Hooks]]></title>
        <id>https://relay.dev/blog/2021/03/09/introducing-relay-hooks/</id>
        <link href="https://relay.dev/blog/2021/03/09/introducing-relay-hooks/"/>
        <updated>2021-03-09T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Introducing Relay Hooks]]></summary>
        <content type="html"><![CDATA[<p>We are extremely excited to release <a href="https://github.com/facebook/relay/releases/tag/v11.0.0" target="_blank" rel="noopener noreferrer" class="">Relay Hooks</a>, the most developer-friendly version of Relay yet, and <a href="https://developers.facebook.com/blog/post/2021/03/09/introducing-relay-hooks-improved-react-apis-relay/" target="_blank" rel="noopener noreferrer" class="">make it available to the OSS community</a> today! Relay Hooks is a set of new, rethought APIs for fetching and managing GraphQL data using React Hooks.</p>
<p>The new APIs are fully compatible with the existing, container-based APIs. Though we recommend writing any new code using Relay Hooks, <em>migrating existing containers to the new APIs is optional and container-based code will continue to work</em>.</p>
<p>Although these APIs are newly released, they are not untested: the rewritten <a href="https://www.facebook.com/" target="_blank" rel="noopener noreferrer" class="">Facebook.com</a> is entirely powered by Relay Hooks and these APIs have been the recommended way to use Relay at Facebook since mid-2019.</p>
<p>In addition, we are also releasing a rewritten <a href="https://relay.dev/docs/guided-tour/">guided tour</a> and <a href="https://relay.dev/docs/">updated documentation</a> that distill the best practices for building maintainable, data-driven applications that we have learned since first developing Relay.</p>
<p>Though we still have a ways to go before getting started with Relay is as easy as we’d like, we believe these steps will make the Relay developer experience substantially better.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-was-released">What was released?<a href="https://relay.dev/blog/2021/03/09/introducing-relay-hooks/#what-was-released" class="hash-link" aria-label="Direct link to What was released?" title="Direct link to What was released?" translate="no">​</a></h2>
<p>We released Relay Hooks, a set of React Hooks-based APIs for working with GraphQL data. We also took the opportunity to ship other improvements, like a more stable version of <a href="https://relay.dev/docs/api-reference/fetch-query/"><code>fetchQuery</code></a> and the ability to customize object identifiers in Relay using <code>getDataID</code> (which is useful if your server does not have globally unique IDs.)</p>
<p>See the <a href="https://github.com/facebook/relay/releases/tag/v11.0.0" target="_blank" rel="noopener noreferrer" class="">release notes</a> for a complete list of what was released.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-are-the-advantages-of-the-hooks-apis">What are the advantages of the Hooks APIs?<a href="https://relay.dev/blog/2021/03/09/introducing-relay-hooks/#what-are-the-advantages-of-the-hooks-apis" class="hash-link" aria-label="Direct link to What are the advantages of the Hooks APIs?" title="Direct link to What are the advantages of the Hooks APIs?" translate="no">​</a></h2>
<p>The newly released APIs improve the developer experience in at least the following ways:</p>
<ul>
<li class="">The Hooks-based APIs for fetching queries, loading data with fragments, pagination, refetching, mutations and subscriptions generally require fewer lines of code and have less indirection than the equivalent container-based solution.</li>
<li class="">These APIs have more complete Flow and Typescript coverage.</li>
<li class="">These APIs take advantage of compiler features to automate error-prone tasks, such as the generation of refetch and pagination queries.</li>
<li class="">These APIs come with the ability to configure fetch policies, which let you determine the conditions in which a query should be fulfilled from the store and in which a network request will be made.</li>
<li class="">These APIs give you the ability to start fetching data before a component renders, something that could not be achieved with the container-based solutions. This allows data to be shown to users sooner.</li>
</ul>
<p>The following examples demonstrate some of the advantages of the new APIs.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="refetching-a-fragment-with-different-variables">Refetching a fragment with different variables<a href="https://relay.dev/blog/2021/03/09/introducing-relay-hooks/#refetching-a-fragment-with-different-variables" class="hash-link" aria-label="Direct link to Refetching a fragment with different variables" title="Direct link to Refetching a fragment with different variables" translate="no">​</a></h2>
<p>First, let’s take a look at how we might refetch a fragment with different variables using the Hooks APIs:</p>
<div class="language-js codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:black;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-js codeBlock_bY9V thin-scrollbar" style="color:black;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:black"><span class="token plain">type </span><span class="token maybe-class-name">Props</span><span class="token plain"> </span><span class="token operator" style="color:#b58a5e">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#b58a5e">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">  </span><span class="token literal-property property" style="color:black">comment</span><span class="token operator" style="color:#b58a5e">:</span><span class="token plain"> </span><span class="token maybe-class-name">CommentBody_comment$key</span><span class="token punctuation" style="color:#b58a5e">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain"></span><span class="token punctuation" style="color:#b58a5e">}</span><span class="token punctuation" style="color:#b58a5e">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:black"><span class="token plain"></span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function maybe-class-name" style="color:black">CommentBody</span><span class="token punctuation" style="color:#b58a5e">(</span><span class="token parameter literal-property property" style="color:black">props</span><span class="token parameter operator" style="color:#b58a5e">:</span><span class="token parameter"> </span><span class="token parameter maybe-class-name">Props</span><span class="token punctuation" style="color:#b58a5e">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#b58a5e">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#b58a5e">[</span><span class="token plain">data</span><span class="token punctuation" style="color:#b58a5e">,</span><span class="token plain"> refetch</span><span class="token punctuation" style="color:#b58a5e">]</span><span class="token plain"> </span><span class="token operator" style="color:#b58a5e">=</span><span class="token plain"> useRefetchableFragment</span><span class="token operator" style="color:#b58a5e">&lt;</span><span class="token maybe-class-name">CommentBodyRefetchQuery</span><span class="token punctuation" style="color:#b58a5e">,</span><span class="token plain"> _</span><span class="token operator" style="color:#b58a5e">&gt;</span><span class="token punctuation" style="color:#b58a5e">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">    graphql</span><span class="token template-string template-punctuation string" style="color:#65822b">`</span><span class="token template-string graphql language-graphql"></span><br></span><span class="token-line" style="color:black"><span class="token template-string graphql language-graphql">      </span><span class="token template-string graphql language-graphql keyword" style="color:#00009f">fragment</span><span class="token template-string graphql language-graphql"> </span><span class="token template-string graphql language-graphql fragment function" style="color:black">CommentBody_comment</span><span class="token template-string graphql language-graphql"> </span><span class="token template-string graphql language-graphql keyword" style="color:#00009f">on</span><span class="token template-string graphql language-graphql"> </span><span class="token template-string graphql language-graphql class-name">Comment</span><span class="token template-string graphql language-graphql"></span><br></span><span class="token-line" style="color:black"><span class="token template-string graphql language-graphql">      </span><span class="token template-string graphql language-graphql directive function" style="color:black">@refetchable</span><span class="token template-string graphql language-graphql punctuation" style="color:#b58a5e">(</span><span class="token template-string graphql language-graphql attr-name" style="color:#2a6883">queryName</span><span class="token template-string graphql language-graphql punctuation" style="color:#b58a5e">:</span><span class="token template-string graphql language-graphql"> </span><span class="token template-string graphql language-graphql string" style="color:#65822b">"CommentBodyRefetchQuery"</span><span class="token template-string graphql language-graphql punctuation" style="color:#b58a5e">)</span><span class="token template-string graphql language-graphql"> </span><span class="token template-string graphql language-graphql punctuation" style="color:#b58a5e">{</span><span class="token template-string graphql language-graphql"></span><br></span><span class="token-line" style="color:black"><span class="token template-string graphql language-graphql">        </span><span class="token template-string graphql language-graphql property-query">body</span><span class="token template-string graphql language-graphql punctuation" style="color:#b58a5e">(</span><span class="token template-string graphql language-graphql attr-name" style="color:#2a6883">lang</span><span class="token template-string graphql language-graphql punctuation" style="color:#b58a5e">:</span><span class="token template-string graphql language-graphql"> </span><span class="token template-string graphql language-graphql variable" style="color:black">$lang</span><span class="token template-string graphql language-graphql punctuation" style="color:#b58a5e">)</span><span class="token template-string graphql language-graphql"> </span><span class="token template-string graphql language-graphql punctuation" style="color:#b58a5e">{</span><span class="token template-string graphql language-graphql"></span><br></span><span class="token-line" style="color:black"><span class="token template-string graphql language-graphql">          </span><span class="token template-string graphql language-graphql property" style="color:black">text</span><span class="token template-string graphql language-graphql"></span><br></span><span class="token-line" style="color:black"><span class="token template-string graphql language-graphql">        </span><span class="token template-string graphql language-graphql punctuation" style="color:#b58a5e">}</span><span class="token template-string graphql language-graphql"></span><br></span><span class="token-line" style="color:black"><span class="token template-string graphql language-graphql">      </span><span class="token template-string graphql language-graphql punctuation" style="color:#b58a5e">}</span><span class="token template-string graphql language-graphql"></span><br></span><span class="token-line" style="color:black"><span class="token template-string graphql language-graphql">    </span><span class="token template-string template-punctuation string" style="color:#65822b">`</span><span class="token punctuation" style="color:#b58a5e">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">    props</span><span class="token punctuation" style="color:#b58a5e">.</span><span class="token property-access">comment</span><span class="token punctuation" style="color:#b58a5e">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">  </span><span class="token punctuation" style="color:#b58a5e">)</span><span class="token punctuation" style="color:#b58a5e">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:black"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token operator" style="color:#b58a5e">&lt;</span><span class="token operator" style="color:#b58a5e">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">    </span><span class="token operator" style="color:#b58a5e">&lt;</span><span class="token maybe-class-name">CommentText</span><span class="token plain"> text</span><span class="token operator" style="color:#b58a5e">=</span><span class="token punctuation" style="color:#b58a5e">{</span><span class="token plain">data</span><span class="token operator" style="color:#b58a5e">?.</span><span class="token plain">text</span><span class="token punctuation" style="color:#b58a5e">}</span><span class="token plain"> </span><span class="token operator" style="color:#b58a5e">/</span><span class="token operator" style="color:#b58a5e">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">    </span><span class="token operator" style="color:#b58a5e">&lt;</span><span class="token maybe-class-name">Button</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">      onClick</span><span class="token operator" style="color:#b58a5e">=</span><span class="token punctuation" style="color:#b58a5e">{</span><span class="token punctuation" style="color:#b58a5e">(</span><span class="token punctuation" style="color:#b58a5e">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#b58a5e">=&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">        </span><span class="token function" style="color:black">refetch</span><span class="token punctuation" style="color:#b58a5e">(</span><span class="token punctuation" style="color:#b58a5e">{</span><span class="token plain"> </span><span class="token literal-property property" style="color:black">lang</span><span class="token operator" style="color:#b58a5e">:</span><span class="token plain"> </span><span class="token string" style="color:#65822b">'SPANISH'</span><span class="token plain"> </span><span class="token punctuation" style="color:#b58a5e">}</span><span class="token punctuation" style="color:#b58a5e">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#b58a5e">{</span><span class="token plain"> </span><span class="token literal-property property" style="color:black">fetchPolicy</span><span class="token operator" style="color:#b58a5e">:</span><span class="token plain"> </span><span class="token string" style="color:#65822b">'store-or-network'</span><span class="token plain"> </span><span class="token punctuation" style="color:#b58a5e">}</span><span class="token punctuation" style="color:#b58a5e">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">      </span><span class="token punctuation" style="color:#b58a5e">}</span><span class="token operator" style="color:#b58a5e">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">    </span><span class="token operator" style="color:#b58a5e">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">      </span><span class="token maybe-class-name">Translate</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">    </span><span class="token operator" style="color:#b58a5e">&lt;</span><span class="token operator" style="color:#b58a5e">/</span><span class="token maybe-class-name">Button</span><span class="token operator" style="color:#b58a5e">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">  </span><span class="token operator" style="color:#b58a5e">&lt;</span><span class="token operator" style="color:#b58a5e">/</span><span class="token operator" style="color:#b58a5e">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain"></span><span class="token punctuation" style="color:#b58a5e">}</span><br></span></code></pre></div></div>
<p>Compare this to the equivalent <a href="https://gist.github.com/rbalicki2/2ac2829aefd8d032e8cb32cd0066bd4e" target="_blank" rel="noopener noreferrer" class="">container-based example</a>. The Hooks-based example takes fewer lines, does not require the developer to manually write a refetch query, has the refetch variables type-checked and explicitly states that a network request should not be issued if the query can be fulfilled from data in the store.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="starting-to-fetch-data-before-rendering-a-component">Starting to fetch data before rendering a component<a href="https://relay.dev/blog/2021/03/09/introducing-relay-hooks/#starting-to-fetch-data-before-rendering-a-component" class="hash-link" aria-label="Direct link to Starting to fetch data before rendering a component" title="Direct link to Starting to fetch data before rendering a component" translate="no">​</a></h2>
<p>The new APIs allow developers to more quickly show content to users by starting to fetch data before a component renders. Prefetching data in this way is not possible with the container-based APIs. Consider the following example:</p>
<div class="language-js codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:black;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-js codeBlock_bY9V thin-scrollbar" style="color:black;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:black"><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token maybe-class-name">UserQuery</span><span class="token plain"> </span><span class="token operator" style="color:#b58a5e">=</span><span class="token plain"> graphql</span><span class="token template-string template-punctuation string" style="color:#65822b">`</span><span class="token template-string graphql language-graphql"></span><br></span><span class="token-line" style="color:black"><span class="token template-string graphql language-graphql">  </span><span class="token template-string graphql language-graphql keyword" style="color:#00009f">query</span><span class="token template-string graphql language-graphql"> </span><span class="token template-string graphql language-graphql definition-query function" style="color:black">UserLinkQuery</span><span class="token template-string graphql language-graphql punctuation" style="color:#b58a5e">(</span><span class="token template-string graphql language-graphql variable" style="color:black">$userId</span><span class="token template-string graphql language-graphql punctuation" style="color:#b58a5e">:</span><span class="token template-string graphql language-graphql"> </span><span class="token template-string graphql language-graphql scalar">ID</span><span class="token template-string graphql language-graphql operator" style="color:#b58a5e">!</span><span class="token template-string graphql language-graphql punctuation" style="color:#b58a5e">)</span><span class="token template-string graphql language-graphql"> </span><span class="token template-string graphql language-graphql punctuation" style="color:#b58a5e">{</span><span class="token template-string graphql language-graphql"></span><br></span><span class="token-line" style="color:black"><span class="token template-string graphql language-graphql">    </span><span class="token template-string graphql language-graphql property-query">user</span><span class="token template-string graphql language-graphql punctuation" style="color:#b58a5e">(</span><span class="token template-string graphql language-graphql attr-name" style="color:#2a6883">id</span><span class="token template-string graphql language-graphql punctuation" style="color:#b58a5e">:</span><span class="token template-string graphql language-graphql"> </span><span class="token template-string graphql language-graphql variable" style="color:black">$userId</span><span class="token template-string graphql language-graphql punctuation" style="color:#b58a5e">)</span><span class="token template-string graphql language-graphql"> </span><span class="token template-string graphql language-graphql punctuation" style="color:#b58a5e">{</span><span class="token template-string graphql language-graphql"></span><br></span><span class="token-line" style="color:black"><span class="token template-string graphql language-graphql">      </span><span class="token template-string graphql language-graphql property" style="color:black">user_details_blurb</span><span class="token template-string graphql language-graphql"></span><br></span><span class="token-line" style="color:black"><span class="token template-string graphql language-graphql">    </span><span class="token template-string graphql language-graphql punctuation" style="color:#b58a5e">}</span><span class="token template-string graphql language-graphql"></span><br></span><span class="token-line" style="color:black"><span class="token template-string graphql language-graphql">  </span><span class="token template-string graphql language-graphql punctuation" style="color:#b58a5e">}</span><span class="token template-string graphql language-graphql"></span><br></span><span class="token-line" style="color:black"><span class="token template-string graphql language-graphql"></span><span class="token template-string template-punctuation string" style="color:#65822b">`</span><span class="token punctuation" style="color:#b58a5e">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:black"><span class="token plain"></span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function maybe-class-name" style="color:black">UserLink</span><span class="token punctuation" style="color:#b58a5e">(</span><span class="token parameter punctuation" style="color:#b58a5e">{</span><span class="token parameter"> userId</span><span class="token parameter punctuation" style="color:#b58a5e">,</span><span class="token parameter"> userName </span><span class="token parameter punctuation" style="color:#b58a5e">}</span><span class="token punctuation" style="color:#b58a5e">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#b58a5e">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#b58a5e">[</span><span class="token plain">queryReference</span><span class="token punctuation" style="color:#b58a5e">,</span><span class="token plain"> loadQuery</span><span class="token punctuation" style="color:#b58a5e">]</span><span class="token plain"> </span><span class="token operator" style="color:#b58a5e">=</span><span class="token plain"> </span><span class="token function" style="color:black">useQueryLoader</span><span class="token punctuation" style="color:#b58a5e">(</span><span class="token maybe-class-name">UserQuery</span><span class="token punctuation" style="color:#b58a5e">)</span><span class="token punctuation" style="color:#b58a5e">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:black"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#b58a5e">[</span><span class="token plain">isPopoverVisible</span><span class="token punctuation" style="color:#b58a5e">,</span><span class="token plain"> setIsPopoverVisible</span><span class="token punctuation" style="color:#b58a5e">]</span><span class="token plain"> </span><span class="token operator" style="color:#b58a5e">=</span><span class="token plain"> </span><span class="token function" style="color:black">useState</span><span class="token punctuation" style="color:#b58a5e">(</span><span class="token boolean" style="color:black">false</span><span class="token punctuation" style="color:#b58a5e">)</span><span class="token punctuation" style="color:#b58a5e">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:black"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> maybePrefetchUserData </span><span class="token operator" style="color:#b58a5e">=</span><span class="token plain"> </span><span class="token function" style="color:black">useCallback</span><span class="token punctuation" style="color:#b58a5e">(</span><span class="token punctuation" style="color:#b58a5e">(</span><span class="token punctuation" style="color:#b58a5e">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#b58a5e">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#b58a5e">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#b58a5e">(</span><span class="token operator" style="color:#b58a5e">!</span><span class="token plain">queryReference</span><span class="token punctuation" style="color:#b58a5e">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#b58a5e">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic">// calling loadQuery will cause this component to re-render.</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic">// During that re-render, queryReference will be defined.</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">      </span><span class="token function" style="color:black">loadQuery</span><span class="token punctuation" style="color:#b58a5e">(</span><span class="token punctuation" style="color:#b58a5e">{</span><span class="token plain"> userId </span><span class="token punctuation" style="color:#b58a5e">}</span><span class="token punctuation" style="color:#b58a5e">)</span><span class="token punctuation" style="color:#b58a5e">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">    </span><span class="token punctuation" style="color:#b58a5e">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">  </span><span class="token punctuation" style="color:#b58a5e">}</span><span class="token punctuation" style="color:#b58a5e">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#b58a5e">[</span><span class="token plain">queryReference</span><span class="token punctuation" style="color:#b58a5e">,</span><span class="token plain"> loadQuery</span><span class="token punctuation" style="color:#b58a5e">]</span><span class="token punctuation" style="color:#b58a5e">)</span><span class="token punctuation" style="color:#b58a5e">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:black"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> showPopover </span><span class="token operator" style="color:#b58a5e">=</span><span class="token plain"> </span><span class="token function" style="color:black">useCallback</span><span class="token punctuation" style="color:#b58a5e">(</span><span class="token punctuation" style="color:#b58a5e">(</span><span class="token punctuation" style="color:#b58a5e">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#b58a5e">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#b58a5e">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">    </span><span class="token function" style="color:black">maybePrefetchUserData</span><span class="token punctuation" style="color:#b58a5e">(</span><span class="token punctuation" style="color:#b58a5e">)</span><span class="token punctuation" style="color:#b58a5e">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">    </span><span class="token function" style="color:black">setIsPopoverVisible</span><span class="token punctuation" style="color:#b58a5e">(</span><span class="token boolean" style="color:black">true</span><span class="token punctuation" style="color:#b58a5e">)</span><span class="token punctuation" style="color:#b58a5e">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">  </span><span class="token punctuation" style="color:#b58a5e">}</span><span class="token punctuation" style="color:#b58a5e">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#b58a5e">[</span><span class="token plain">maybePrefetchUserData</span><span class="token punctuation" style="color:#b58a5e">,</span><span class="token plain"> setIsPopoverVisible</span><span class="token punctuation" style="color:#b58a5e">]</span><span class="token punctuation" style="color:#b58a5e">)</span><span class="token punctuation" style="color:#b58a5e">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:black"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token operator" style="color:#b58a5e">&lt;</span><span class="token operator" style="color:#b58a5e">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">    </span><span class="token operator" style="color:#b58a5e">&lt;</span><span class="token maybe-class-name">Button</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">      onMouseOver</span><span class="token operator" style="color:#b58a5e">=</span><span class="token punctuation" style="color:#b58a5e">{</span><span class="token plain">maybePrefetchUserData</span><span class="token punctuation" style="color:#b58a5e">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">      onPress</span><span class="token operator" style="color:#b58a5e">=</span><span class="token punctuation" style="color:#b58a5e">{</span><span class="token plain">showPopover</span><span class="token punctuation" style="color:#b58a5e">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">    </span><span class="token operator" style="color:#b58a5e">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">      </span><span class="token punctuation" style="color:#b58a5e">{</span><span class="token plain">userName</span><span class="token punctuation" style="color:#b58a5e">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">    </span><span class="token operator" style="color:#b58a5e">&lt;</span><span class="token operator" style="color:#b58a5e">/</span><span class="token maybe-class-name">Button</span><span class="token operator" style="color:#b58a5e">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">    </span><span class="token punctuation" style="color:#b58a5e">{</span><span class="token plain">isPopoverVisible </span><span class="token operator" style="color:#b58a5e">&amp;&amp;</span><span class="token plain"> queryReference </span><span class="token operator" style="color:#b58a5e">&amp;&amp;</span><span class="token plain"> </span><span class="token punctuation" style="color:#b58a5e">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">      </span><span class="token operator" style="color:#b58a5e">&lt;</span><span class="token maybe-class-name">Popover</span><span class="token operator" style="color:#b58a5e">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">        </span><span class="token operator" style="color:#b58a5e">&lt;</span><span class="token maybe-class-name">React</span><span class="token punctuation" style="color:#b58a5e">.</span><span class="token property-access maybe-class-name">Suspense</span><span class="token plain"> fallback</span><span class="token operator" style="color:#b58a5e">=</span><span class="token punctuation" style="color:#b58a5e">{</span><span class="token operator" style="color:#b58a5e">&lt;</span><span class="token maybe-class-name">Glimmer</span><span class="token plain"> </span><span class="token operator" style="color:#b58a5e">/</span><span class="token operator" style="color:#b58a5e">&gt;</span><span class="token punctuation" style="color:#b58a5e">}</span><span class="token operator" style="color:#b58a5e">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">          </span><span class="token operator" style="color:#b58a5e">&lt;</span><span class="token maybe-class-name">UserPopoverContent</span><span class="token plain"> queryRef</span><span class="token operator" style="color:#b58a5e">=</span><span class="token punctuation" style="color:#b58a5e">{</span><span class="token plain">queryReference</span><span class="token punctuation" style="color:#b58a5e">}</span><span class="token plain"> </span><span class="token operator" style="color:#b58a5e">/</span><span class="token operator" style="color:#b58a5e">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">        </span><span class="token operator" style="color:#b58a5e">&lt;</span><span class="token operator" style="color:#b58a5e">/</span><span class="token maybe-class-name">React</span><span class="token punctuation" style="color:#b58a5e">.</span><span class="token property-access maybe-class-name">Suspense</span><span class="token operator" style="color:#b58a5e">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">      </span><span class="token operator" style="color:#b58a5e">&lt;</span><span class="token operator" style="color:#b58a5e">/</span><span class="token maybe-class-name">Popover</span><span class="token operator" style="color:#b58a5e">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">    </span><span class="token punctuation" style="color:#b58a5e">)</span><span class="token punctuation" style="color:#b58a5e">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">  </span><span class="token operator" style="color:#b58a5e">&lt;</span><span class="token operator" style="color:#b58a5e">/</span><span class="token operator" style="color:#b58a5e">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain"></span><span class="token punctuation" style="color:#b58a5e">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:black"><span class="token plain"></span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function maybe-class-name" style="color:black">UserPopoverContent</span><span class="token punctuation" style="color:#b58a5e">(</span><span class="token parameter punctuation" style="color:#b58a5e">{</span><span class="token parameter">queryRef</span><span class="token parameter punctuation" style="color:#b58a5e">}</span><span class="token punctuation" style="color:#b58a5e">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#b58a5e">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// The following call will Suspend if the request for the data is still</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// in flight:</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> data </span><span class="token operator" style="color:#b58a5e">=</span><span class="token plain"> </span><span class="token function" style="color:black">usePreloadedQuery</span><span class="token punctuation" style="color:#b58a5e">(</span><span class="token maybe-class-name">UserQuery</span><span class="token punctuation" style="color:#b58a5e">,</span><span class="token plain"> queryRef</span><span class="token punctuation" style="color:#b58a5e">)</span><span class="token punctuation" style="color:#b58a5e">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// ...</span><span class="token plain"></span><br></span><span class="token-line" style="color:black"><span class="token plain"></span><span class="token punctuation" style="color:#b58a5e">}</span><br></span></code></pre></div></div>
<p>In this example, if the query cannot be fulfilled from data in the local cache, a network request is initiated when the user hovers over a button. When the button is finally pressed, the user will thus see content sooner.</p>
<p>By contrast, the container-based APIs initiate network requests when the component renders.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="hooks-and-suspense-for-data-fetching">Hooks and Suspense for Data Fetching<a href="https://relay.dev/blog/2021/03/09/introducing-relay-hooks/#hooks-and-suspense-for-data-fetching" class="hash-link" aria-label="Direct link to Hooks and Suspense for Data Fetching" title="Direct link to Hooks and Suspense for Data Fetching" translate="no">​</a></h3>
<p>You may have noticed that both of the examples use Suspense.</p>
<p>Although Relay Hooks uses Suspense for some of its APIs, <em>support, general guidance, and requirements for usage of Suspense for Data Fetching in React are still</em> <em>not ready</em>, and the React team is still defining what this guidance will be for upcoming releases. There are some limitations when Suspense is used with React 17.</p>
<p>Nonetheless, we released Relay Hooks now because we know these APIs are on the right trajectory for supporting upcoming releases of React. Even though parts of Relay’s Suspense implementation may still change, the Relay Hooks APIs themselves are stable; they have been widely adopted internally, and have been in use in production for over a year.</p>
<p>See <a href="https://relay.dev/docs/v13.0.0/migration-and-compatibility/suspense-compatibility/" target="_blank" rel="noopener noreferrer" class="">Suspense Compatibility</a> and <a href="https://relay.dev/docs/guided-tour/rendering/loading-states/">Loading States with Suspense</a> for deeper treatments of this topic.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="where-to-go-from-here">Where to go from here<a href="https://relay.dev/blog/2021/03/09/introducing-relay-hooks/#where-to-go-from-here" class="hash-link" aria-label="Direct link to Where to go from here" title="Direct link to Where to go from here" translate="no">​</a></h3>
<p>Please check out the <a href="https://relay.dev/docs/">getting started guide</a>, the <a href="https://relay.dev/docs/v13.0.0/migration-and-compatibility/" target="_blank" rel="noopener noreferrer" class="">migration guide</a> and the <a href="https://relay.dev/docs/guided-tour/">guided tour</a>.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="thanks">Thanks<a href="https://relay.dev/blog/2021/03/09/introducing-relay-hooks/#thanks" class="hash-link" aria-label="Direct link to Thanks" title="Direct link to Thanks" translate="no">​</a></h3>
<p>Releasing Relay Hooks was not just the work of the React Data team. We'd like to thank the contributors that helped make it possible:</p>
<p>@0xflotus, @AbdouMoumen, @ahmadrasyidsalim, @alexdunne, @alloy, @andrehsu, @andrewkfiedler, @anikethsaha, @babangsund, @bart88, @bbenoist, @bigfootjon, @bondz, @BorisTB, @captbaritone, @cgriego, @chaytanyasinha, @ckknight, @clucasalcantara, @damassi, @Daniel15, @daniloab, @earvinLi, @EgorShum, @eliperkins, @enisdenjo, @etcinit, @fabriziocucci, @HeroicHitesh, @jaburx, @jamesgeorge007, @janicduplessis, @jaroslav-kubicek, @jaycenhorton, @jaylattice, @JonathanUsername, @jopara94, @jquense, @juffalow, @kafinsalim, @kyarik, @larsonjj, @leoasis, @leonardodino, @levibuzolic, @liamross, @lilianammmatos, @luansantosti, @MaartenStaa, @MahdiAbdi, @MajorBreakfast, @maraisr, @mariusschulz, @martinbooth, @merrywhether, @milosa, @mjm, @morrys, @morwalz, @mrtnzlml, @n1ru4l, @Nilomiranda, @omerzach, @orta, @pauloedurezende, @RDIL, @RicCu, @robrichard, @rsmelo92, @SeshanPillay25, @sibelius, @SiddharthSham, @stefanprobst, @sugarshin, @taion, @thedanielforum, @theill, @thicodes, @tmus, @TrySound, @VinceOPS, @visshaljagtap, @Vrq, @w01fgang, @wincent, @wongmjane, @wyattanderson, @xamgore, @yangshun, @ymittal, @zeyap, @zpao and @zth.</p>
<p>The open source project <a href="https://github.com/relay-tools/relay-hooks" target="_blank" rel="noopener noreferrer" class=""><code>relay-hooks</code></a> allowed the community to experiment with Relay and React Hooks, and was a source of valuable feedback for us. The idea for the <code>useSubscription</code> hook originated in <a href="https://github.com/relay-tools/relay-hooks/issues/5#issuecomment-603930570" target="_blank" rel="noopener noreferrer" class="">an issue</a> on that repo. Thank you @morrys for driving this project and for playing such an important role in our open source community.</p>
<p>Thank you for helping make this possible!</p>]]></content>
        <author>
            <name>Robert Balicki &amp; Juan Tejada</name>
        </author>
        <category label="relay-hooks" term="relay-hooks"/>
    </entry>
</feed>