<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[nakamoriのblog]]></title><description><![CDATA[日々のことを書きます]]></description><link>https://kazekyo.com</link><generator>GatsbyJS</generator><lastBuildDate>Thu, 09 Jun 2022 08:11:13 GMT</lastBuildDate><item><title><![CDATA[Apollo Clientにいろいろ足しながらRelayと比較する]]></title><description><![CDATA[GraphQLクライアントのApollo Clientは単独で使用することは少ないと思います。今回はApollo Clientにいろいろなツールを足しながらRelayと比較してみます。]]></description><link>https://kazekyo.com/posts/20220605-apollo-client-vs-relay-2</link><guid isPermaLink="false">https://kazekyo.com/posts/20220605-apollo-client-vs-relay-2</guid><pubDate>Thu, 09 Jun 2022 07:06:10 GMT</pubDate><content:encoded>&lt;p&gt;以前に&lt;a href=&quot;https://www.apollographql.com/docs/react/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Apollo Client&lt;/a&gt;と&lt;a href=&quot;https://relay.dev/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Relay&lt;/a&gt;の&lt;a href=&quot;https://kazekyo.com/posts/20200925&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;比較記事&lt;/a&gt;を書きました。
当時はApollo Clientを使い込んでいませんでしたが、1年以上Apollo Clientを使ってきたので適切な比較ができる頃合いだと思います。&lt;/p&gt;
&lt;p&gt;今回はApollo ClientとRelayとの比較ではありますが、Apollo Clientにいろいろなツールを足しつつ比較してみたいと思います。
Apollo Clientはそのまま使うことはおそらく稀なので、こうした比較をすることでより良い比較ができる + 自作のOSSの&lt;a href=&quot;https://www.naugraphql.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Nau&lt;/a&gt;をちょっと紹介する😎ということが目的です。&lt;/p&gt;
&lt;p&gt;Apollo ClientとRelayのどちらのツールも使用したことがない方には少し難しい内容になっている可能性があります。容赦ください。&lt;/p&gt;
&lt;h2 id=&quot;私の状況&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E7%A7%81%E3%81%AE%E7%8A%B6%E6%B3%81&quot; aria-label=&quot;私の状況 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;私の状況&lt;/h2&gt;
&lt;p&gt;比較の前に私がどの程度Apollo ClientとRelayを理解しているかについて書きます。&lt;/p&gt;
&lt;p&gt;まずRelayですが、使用歴1年半ほどです。Relayの設計方針の翻訳記事をこのブログに置いています。本番で使用していました。
Apollo Clientについても使用歴1年以上、本番で使用しています。Apollo Client用のツールであるNauを1年以上作っています。&lt;/p&gt;
&lt;h2 id=&quot;apollo-client-vs-relay-の概況&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#apollo-client-vs-relay-%E3%81%AE%E6%A6%82%E6%B3%81&quot; aria-label=&quot;apollo client vs relay の概況 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;code class=&quot;language-text&quot;&gt;Apollo Client&lt;/code&gt; vs &lt;code class=&quot;language-text&quot;&gt;Relay&lt;/code&gt; の概況&lt;/h2&gt;
&lt;p&gt;ざっくりとした話として、&lt;code class=&quot;language-text&quot;&gt;Apollo Client&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;Relay&lt;/code&gt;の技術選択について、簡単に触れていきたいと思います。&lt;/p&gt;
&lt;p&gt;まず周りに配慮せず使用技術を決められるならRelayがおすすめです。
Relayの方が効率的で効果的で保守性が高い形でプログラムを書くことができます（というよりそのような形でしか書けません）。
ただし、チームメイトが使いこなせなかったり、Relayの想定する使い方と違うやり方をすると、途端に大変になります。ある程度自分で責任取れる人向けです。&lt;/p&gt;
&lt;p&gt;一方Apollo Clientはプログラムを書いて動かすまでの敷居がとても低いです。また柔軟性・拡張性が高いのも特徴ですね。
ただし、保守性の高いプログラムを書こうとすると&lt;a href=&quot;https://www.graphql-code-generator.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;GraphQL Code Generator&lt;/a&gt;などの別プロダクトを併用しないといけなかったり、キャッシュの仕組みを詳細に理解しなければなりません。&lt;/p&gt;
&lt;p&gt;※ 今回は触れませんが、どちらを使用する場合でもバックエンドのGraphQL APIは&lt;a href=&quot;https://relay.dev/docs/guides/graphql-server-specification/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;RelayのGraphQL Server Specification&lt;/a&gt;に準拠しておいた方が後々幸せになれます。Apollo Clientを使う場合でも準拠したバックエンドを構築したほうが良いです。&lt;/p&gt;
&lt;h2 id=&quot;apollo-clientにいろいろ足しながらrelayと比較する&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#apollo-client%E3%81%AB%E3%81%84%E3%82%8D%E3%81%84%E3%82%8D%E8%B6%B3%E3%81%97%E3%81%AA%E3%81%8C%E3%82%89relay%E3%81%A8%E6%AF%94%E8%BC%83%E3%81%99%E3%82%8B&quot; aria-label=&quot;apollo clientにいろいろ足しながらrelayと比較する permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Apollo Clientにいろいろ足しながらRelayと比較する&lt;/h2&gt;
&lt;p&gt;ここからはApollo Clientに様々なツールを足していきながらRelayと比較していきたいと思います。
実際Apollo Clientを使う時にそれだけで使用するということはほぼないので、「何を使ったらApollo ClientはRelayと同じぐらいの保守性や利便性が手に入れられるのか？」という観点で比較していきましょう。&lt;/p&gt;
&lt;h3 id=&quot;apollo-clientだけ-vs-relay&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#apollo-client%E3%81%A0%E3%81%91-vs-relay&quot; aria-label=&quot;apollo clientだけ vs relay permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;code class=&quot;language-text&quot;&gt;Apollo Clientだけ&lt;/code&gt; vs &lt;code class=&quot;language-text&quot;&gt;Relay&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;最初にそのままのApollo ClientとRelayとの比較について考えます。&lt;/p&gt;
&lt;p&gt;残念ながらそのままApollo Clientを使うだけでは、TypeScriptの型も生成できませんし、書いたQueryが正しいのかも分かりません。すぐ壊れるので正直厳しい世界です。
Apollo Clientは簡単に始められるという利点はありますが、RelayとApolloの両方で本番運用をした経験がある人なら（この状況では）間違いなくRelayを推奨すると思います。
それほどApollo Clientだけで書いたプログラムは脆弱です。&lt;/p&gt;
&lt;h3 id=&quot;apollo-client--graphql-code-generator-vs-relay&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#apollo-client--graphql-code-generator-vs-relay&quot; aria-label=&quot;apollo client  graphql code generator vs relay permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;code class=&quot;language-text&quot;&gt;Apollo Client + GraphQL Code Generator&lt;/code&gt; vs &lt;code class=&quot;language-text&quot;&gt;Relay&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Apollo Clientに&lt;a href=&quot;https://www.graphql-code-generator.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;GraphQL Code Generator&lt;/a&gt;を併用することで、かなりまともな環境が手に入れられます。&lt;/p&gt;
&lt;p&gt;GraphQL Code GeneratorはRelayのコンパイラと似たようなことをしてくれて、TypeScriptの型を出力したりQueryの静的解析をしてくれます。
若干GraphQL Code Generatorのプラグインに精通しておかないと何をどう使えばよいのか分からないこともありますが、1日かけてドキュメントを読み込めばなんとかなる範囲なので頑張りましょう。&lt;/p&gt;
&lt;p&gt;この状況でApollo ClientがRelayに劣っている主な点は次のとおりです（Reactの最新機能への追随などは除きます。GraphQLクライアントとしての観点のみで考えます）。&lt;/p&gt;
&lt;p&gt;保守性：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;フラグメントに閉じた変数などが使えないため、コロケーション（コンポーネントとフラグメントを近い場所に置いて運用すること）がうまくいかないです。コロケーションせずにスケーラブルなアプリケーションを作るのは中々難しいところがあります。&lt;/li&gt;
&lt;li&gt;キャッシュのバグは本当に解明しにくいのに、ちょっとリッチな機能を作ろうとするとキャッシュを直接弄る必要があるのでバグりやすいです。ページネーションやSubscriptionを使う時はキャッシュを弄りがちで、バグりがちです。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;効率：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Queryをリフェッチする時にフラグメント単位でリフェッチできないため、たびたびページのQuery全体を再実行する必要がありバックエンドから見て効率が悪いです。&lt;/li&gt;
&lt;li&gt;仕組み上、Queryからデータを得ると全てのコンポーネントが再レンダリングされるためパフォーマンスチューニングが難しいです。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;apollo-client--graphql-code-generator--nau-vs-relay&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#apollo-client--graphql-code-generator--nau-vs-relay&quot; aria-label=&quot;apollo client  graphql code generator  nau vs relay permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;code class=&quot;language-text&quot;&gt;Apollo Client + GraphQL Code Generator + Nau&lt;/code&gt; vs &lt;code class=&quot;language-text&quot;&gt;Relay&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Apollo Client陣営に&lt;a href=&quot;https://github.com/kazekyo/nau&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Nau&lt;/a&gt;を参加させてみます。&lt;/p&gt;
&lt;p&gt;NauはRelayにインスパイアされたApollo Client用のツール（GraphQL Code Generatorと一緒に使います）で、コロケーションを行うための各種ディレクティブや、フラグメント単位でのRefetch機能、バグりやすいキャッシュ制御のためのツールなどを提供しています。
Nauを使う場合はバックエンドが&lt;a href=&quot;https://relay.dev/docs/guides/graphql-server-specification/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Relay GraphQL Server Specification&lt;/a&gt;に準拠している必要があります。&lt;/p&gt;
&lt;p&gt;NauはRelayの機能をGraphQL Code Generatorの上でApollo Client流に再現しているものと言えます。
Nauを使うことで先に述べたほとんどの課題は解決されます。残ったのは「Queryからデータを得ると全てのコンポーネントが再レンダリングされるためパフォーマンスチューニングが難しい」だけです。これもNauに入れたいところではありますが、まだ実装できていません。&lt;/p&gt;
&lt;h2 id=&quot;まとめ&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E3%81%BE%E3%81%A8%E3%82%81&quot; aria-label=&quot;まとめ permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;まとめ&lt;/h2&gt;
&lt;p&gt;いろいろなツールをApollo Clientと一緒に使用することである程度Relayの機能をカバーできること、そしてそれらを標準搭載しているRelayのオールインワン感がお分かりいただけたと思います。&lt;/p&gt;
&lt;p&gt;ちなみに、これを書いている私は、お客様のところで技術選定をする場合はRelayでのリスクを引き受けられないのでApollo Clientを最初に提案します 😇
自分でやる場合はRelay一択なのですが…。人間だもの。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Apollo Clientの強みと弱みを考える]]></title><description><![CDATA[多くの利用者を持つGraphQLクライアントのApollo Client。今回はその強みと弱みについて、彼らなりの理由や戦略についても触れながら考えていきたいと思います。]]></description><link>https://kazekyo.com/posts/20220605-strengths-and-weaknesses-of-apollo-client</link><guid isPermaLink="false">https://kazekyo.com/posts/20220605-strengths-and-weaknesses-of-apollo-client</guid><pubDate>Sun, 05 Jun 2022 08:02:09 GMT</pubDate><content:encoded>&lt;p&gt;突然ですが、Apollo Clientの開発を支援するツールである &lt;a href=&quot;https://github.com/kazekyo/nau&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Nau&lt;/a&gt; を作り始めて1年以上が経過しました。&lt;/p&gt;
&lt;p&gt;この間、さすがにApollo Clientについて多少は詳しくなりました。せっかくなのでこの1年の開発から得られた学びなどを、今後いくつかに分けて話していけたらなと思います。
今回はNauの開発を始めるキッカケになったApollo Clientの強みと弱み、特に生産性や開発者体験に目を向けて話していきましょう。&lt;/p&gt;
&lt;p&gt;なお、ここではApollo Clientに加えてReactとTypeScriptの使用を前提として話します。
その他の環境についてはあまり詳しくないので、私の考えが妥当でない可能性があります。ご注意ください。&lt;/p&gt;
&lt;h2 id=&quot;apollo-clientの良いところ&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#apollo-client%E3%81%AE%E8%89%AF%E3%81%84%E3%81%A8%E3%81%93%E3%82%8D&quot; aria-label=&quot;apollo clientの良いところ permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Apollo Clientの良いところ&lt;/h2&gt;
&lt;p&gt;Reactで使うGraphQLクライアントと言えば、Apollo Client・Relay・urqlあたりが有名です。
私はもともとRelayを使っていましたが、2020年末頃からお手伝いしているお客様の案件でApollo Clientを使い始めました。&lt;/p&gt;
&lt;p&gt;Apollo Clientを始めてまず感じた良さは、開発者に対して手厚い支援があるところでした。ドキュメントやサンプルは豊富にありますし、&lt;a href=&quot;https://studio.apollographql.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Apollo Studio&lt;/a&gt;などのクエリの実行環境から&lt;a href=&quot;https://www.apollographql.com/docs/devtools/editor-plugins/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;VS Codeのエクステンション&lt;/a&gt;や&lt;a href=&quot;https://github.com/apollographql/apollo-client-devtools&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;ブラウザのエクステンション&lt;/a&gt;まで、一通りあります。
対して、当時のRelayは（現在は改善されていますが）ドキュメントがほぼなくソースコードを見て細かい挙動を解析するような生活を強いられていたので、それと比較すると体験は当然良かったです。&lt;/p&gt;
&lt;p&gt;また、Apollo Clientは柔軟で高い拡張性があり、実行時に行われるほぼ全てに干渉できます。それによりサードーパーティー文化が盛り上がっているかはさておき、困った時に自分で簡単な処理を書いてハックできるところは技術選定では安心感がありますね。&lt;/p&gt;
&lt;p&gt;最後に、単純に使用している人が多いので、調べたらGitHubのIssue含め答えが出てきやすいところも技術選定観点では安心です。&lt;/p&gt;
&lt;h2 id=&quot;apollo-clientの良くないところ&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#apollo-client%E3%81%AE%E8%89%AF%E3%81%8F%E3%81%AA%E3%81%84%E3%81%A8%E3%81%93%E3%82%8D&quot; aria-label=&quot;apollo clientの良くないところ permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Apollo Clientの良くないところ&lt;/h2&gt;
&lt;p&gt;良くないところは、ハッキリ言ってRelayと比較すると大規模なアプリケーションではバグりやすいことですね。悲しいですがそう言わざるをえません。
Apollo Clientのバグが多いという意味ではなく、Apollo Clientで作ったアプリケーションにバグを仕込んでしまう可能性が高いです。&lt;/p&gt;
&lt;p&gt;まず、そのまま使うとQueryからTypeScriptの型を生成できませんし、書いたQueryが正しいのか実行前に静的に検証できません。普段TypeScriptを使っているユーザーからするとJavaScript時代に戻ったのかのような感覚になります。
なので、ほとんどのユーザーは&lt;a href=&quot;https://www.graphql-code-generator.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;GraphQL Code Generator&lt;/a&gt;と一緒に使って、TypeScriptの型やコードを自動生成したりQueryの検証をしたりしています。&lt;/p&gt;
&lt;p&gt;ここまではGraphQL Code Generatorがあるので良いでしょう。本当の辛いところはここからです。&lt;/p&gt;
&lt;p&gt;キャッシュ関連は酷いです。バグの温床です。「下手に弄るとすぐバグるから、とりあえずキャッシュ使わずにリフェッチして正しい状態にしよう」みたいなのはあるあるなバッドノウハウだと思います。
Apollo ClientはRelayと異なりバックエンドのスキーマがどうなっているか事前に定めないので、フレームワーク側でキャッシュ関連の自動化が困難です。
できることと言えば、様々なケースに対応した柔軟性の高いキャッシュの仕組みを提供するということになりますが、その柔軟性、もとい複雑性が人類が使いこなすにはまだ早すぎるように感じます。&lt;/p&gt;
&lt;p&gt;また、Relayでは常識となっているコロケーションが困難なのも大規模なアプリケーションでは辛いところです。
ここで言うコロケーションというのは、フラグメントとコンポーネントを同じところに配置し、コンポーネントとコンポーネントで必要なデータの定義をセット運用することです。こうすることで、コンポーネントに必要なデータがコンポーネントの外部に依存しないのでリファクタリングが容易になります。
しかしながらApollo Clientでコロケーションしようとすると、フラグメント内に閉じる変数を作れなかったり、フラグメント単位でリフェッチできなかったりするので、なかなか上手くいきません。&lt;/p&gt;
&lt;p&gt;総じて、アプリケーションが大きくなるにつれてRelayは（慣れもあり）開発者体験が良くなっていくのに対して、Apollo Clientでは全く逆のことが起きると感じます。始めるのは簡単ですが大きくするのが難しいです。&lt;/p&gt;
&lt;h2 id=&quot;対応策&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E5%AF%BE%E5%BF%9C%E7%AD%96&quot; aria-label=&quot;対応策 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;対応策&lt;/h2&gt;
&lt;p&gt;そんなこんなでApollo Clientの開発者体験は若干厳しいのですが、当然ながら様々な努力が過去に、または現在進行形で存在しています。&lt;/p&gt;
&lt;p&gt;日本語で参照できる情報としては、QuramyさんがApollo Clientでコロケーションが行える仕組み作りにチャレンジされています。
&lt;a href=&quot;https://qiita.com/Quramy/items/b4d42f5b1c52d5cc171d&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://qiita.com/Quramy/items/b4d42f5b1c52d5cc171d&lt;/a&gt;
&lt;a href=&quot;https://github.com/Quramy/apollo-link-fragment-argument&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://github.com/Quramy/apollo-link-fragment-argument&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;GraphQL Code Generatorでコロケーションを行えるようにするプラグインも存在しています。これは内部的にRelayのコンパイラを使用しています。
&lt;a href=&quot;https://www.the-guild.dev/blog/graphql-codegen-relay-compiler&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://www.the-guild.dev/blog/graphql-codegen-relay-compiler&lt;/a&gt;
&lt;a href=&quot;https://www.graphql-code-generator.com/plugins/relay-operation-optimizer&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://www.graphql-code-generator.com/plugins/relay-operation-optimizer&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;異色なところでは、用途は違うのですがApollo Client上にRelayのAPIを完全に作ってしまうものなんかもあります。当然コロケーション等ができます。こちらも内部的にはRelayのコンパイラを使っていたと思います。
&lt;a href=&quot;https://github.com/microsoft/graphitation&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://github.com/microsoft/graphitation&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;そして私は &lt;a href=&quot;https://github.com/kazekyo/nau&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Nau&lt;/a&gt; というツールを作っています。
&lt;a href=&quot;https://github.com/kazekyo/nau&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://github.com/kazekyo/nau&lt;/a&gt;
Relayへの依存なしで、コロケーションをサポートしたり、ディレクティブ等でキャッシュの処理を宣言的かつ簡易に書くことができるようなツールです。&lt;/p&gt;
&lt;h2 id=&quot;apollo-clientの弱みと向き合う&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#apollo-client%E3%81%AE%E5%BC%B1%E3%81%BF%E3%81%A8%E5%90%91%E3%81%8D%E5%90%88%E3%81%86&quot; aria-label=&quot;apollo clientの弱みと向き合う permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Apollo Clientの弱みと向き合う&lt;/h2&gt;
&lt;p&gt;Apollo Clientの根本的な弱みは「静的解析が苦手」「前提条件が少なく自動化できる領域が狭い」「一緒に使用するツールを限定できないため連携を極められない」みたいなところにあります。
これらの弱みはApollo Clientが簡単さや間口の広さを重視しているからと言えるので、今後劇的に改善されるような未来はあまり見えません。
Apollo Clientはその代わりに拡張性を高めているので、なんとかGraphQL Code Generatorのようなサードパーティーのツールでうまく補完しながら付き合っていきたいですね。
みなさんも、業務で作った仕組みがあれば是非OSS化していただけると良いと思います。人類が幸せになれる余地はまだたくさんあるのではないでしょうか。&lt;/p&gt;
&lt;p&gt;ちなみに、私が作っているツールの目標はこれらの弱みを踏まえて、「&lt;a href=&quot;https://relay.dev/docs/guides/graphql-server-specification/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Relayのサーバー仕様&lt;/a&gt;に準拠しているバックエンドがある」環境に限定して「Apollo Clientのやり方に沿った形で開発者の体験を向上させる」としています。
Apollo Clientユーザー全員は救えませんが、利用環境をある程度限定した状態で、その中で体験を良くするということですね。&lt;/p&gt;
&lt;p&gt;またNauの紹介は後日書きたいと思いますので、今日はこの辺りで。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Relay Meetup #2 レポート]]></title><description><![CDATA[今回はRelay Meetup #2のレポートをお届けします。@defer/@streamなど面白い話がいろいろと聞けた今回のmeetup、さっそく見ていきましょう。]]></description><link>https://kazekyo.com/posts/20210202-relay-meetup-2</link><guid isPermaLink="false">https://kazekyo.com/posts/20210202-relay-meetup-2</guid><pubDate>Fri, 05 Feb 2021 03:35:31 GMT</pubDate><content:encoded>&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/7ab6d344aeb107193cb7be82188c25e5/97a96/20210202-relay-meetup-2-cover.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsSAAALEgHS3X78AAABvElEQVQoz4VSS0sCURSef9S2P9EmKHLROpAKpEUEEdimqKAIQloIkkJFi1r0A8ryka/yNb4dHzNqPog0HzXjfN0705hJ1oHv3nMP53z3vBgQkWVZQb9P0f+hD4tq7w9uDVo8FUZTxkmz1UKhyKNWr6Pd6fzpS7kYLYjnS8hkOcQTKUIgIMvlFZ3LFVB5riIaixNbDslUhpA3wMYSiLJxsPGk8v74ENUM6fHabCIcYREMRxWnu3s3vL5H3NgdsN85CWket+SOkWD3g0/x9XgDCDyFEAxFFEJRFMeXzOUzsDsdCEdjcHn8KJLs/YEgfARe/yMcLg+qtcb4kgdNltQhPNzySIYqpAVpCMUiclwaETaMdvsN9PuywIPLJBXfX4eiTZVKKi1CN7EP3eQ2dpYMWJybxvrKMuZnp2Dc0OPwwAijQY+1hRnUXmpfmX2TMvIgQ0UjWUjYWvVgb9MHx/UFbGYzbOdXMFvOYLWYYDraxeXpCazHB3gX3zG8dmrJ/6zNfzI6A0Zq8CgJAvhSGeVyBZ1O96s3al8lSYI8WGAVo32jthZZvW63S0qWRPR6PfJQQQlGyxiH4SxpHP3oE9x/NLSvizZHAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
        &lt;source
          srcset=&quot;/static/7ab6d344aeb107193cb7be82188c25e5/8ac56/20210202-relay-meetup-2-cover.webp 240w,
/static/7ab6d344aeb107193cb7be82188c25e5/d3be9/20210202-relay-meetup-2-cover.webp 480w,
/static/7ab6d344aeb107193cb7be82188c25e5/e46b2/20210202-relay-meetup-2-cover.webp 960w,
/static/7ab6d344aeb107193cb7be82188c25e5/f992d/20210202-relay-meetup-2-cover.webp 1440w,
/static/7ab6d344aeb107193cb7be82188c25e5/882b9/20210202-relay-meetup-2-cover.webp 1920w,
/static/7ab6d344aeb107193cb7be82188c25e5/46261/20210202-relay-meetup-2-cover.webp 2400w&quot;
          sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
          type=&quot;image/webp&quot;
        /&gt;
        &lt;source
          srcset=&quot;/static/7ab6d344aeb107193cb7be82188c25e5/8ff5a/20210202-relay-meetup-2-cover.png 240w,
/static/7ab6d344aeb107193cb7be82188c25e5/e85cb/20210202-relay-meetup-2-cover.png 480w,
/static/7ab6d344aeb107193cb7be82188c25e5/d9199/20210202-relay-meetup-2-cover.png 960w,
/static/7ab6d344aeb107193cb7be82188c25e5/07a9c/20210202-relay-meetup-2-cover.png 1440w,
/static/7ab6d344aeb107193cb7be82188c25e5/29114/20210202-relay-meetup-2-cover.png 1920w,
/static/7ab6d344aeb107193cb7be82188c25e5/97a96/20210202-relay-meetup-2-cover.png 2400w&quot;
          sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
          type=&quot;image/png&quot;
        /&gt;
        &lt;img
          class=&quot;gatsby-resp-image-image&quot;
          src=&quot;/static/7ab6d344aeb107193cb7be82188c25e5/d9199/20210202-relay-meetup-2-cover.png&quot;
          alt=&quot;image&quot;
          title=&quot;image&quot;
          loading=&quot;lazy&quot;
          style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        /&gt;
      &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;今回は2021年12月3日開催の&lt;a href=&quot;https://relaymeetup.com/meetups/2&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Relay Meetup #2&lt;/a&gt;のレポートをお届けします。
ここ最近は忙しくて記事が書けていませんが、遅れてもRelay Meetupの記事投稿は続けられたらと思っています。&lt;/p&gt;
&lt;p&gt;今回は主催者の&lt;a href=&quot;https://github.com/zth&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Gabriel Nordeborn&lt;/a&gt;、1stDibsから&lt;a href=&quot;https://github.com/lilianammmatos&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Liliana Matos&lt;/a&gt;と
&lt;a href=&quot;https://github.com/robrichard&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Rob Richard&lt;/a&gt;、Entria Techから&lt;a href=&quot;https://github.com/sibelius&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Sibelius Seraphini&lt;/a&gt;、そしてFacebookのRelayコアチームから&lt;a href=&quot;https://github.com/josephsavona&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Joseph Savona&lt;/a&gt;が登壇してくれました。&lt;/p&gt;
&lt;h2 id=&quot;relay-meetupについて&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#relay-meetup%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6&quot; aria-label=&quot;relay meetupについて permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Relay Meetupについて&lt;/h2&gt;
&lt;p&gt;Relay Meetupは、GraphQLクライアントである&lt;a href=&quot;https://relay.dev/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Relay&lt;/a&gt;のミートアップです。
今回もYoutube上でのオンラインミートアップとして開催され、参加者はYoutube上のチャットやDiscordでの交流となりました。
全て英語となりますが、当日の模様は&lt;a href=&quot;https://www.youtube.com/watch?v=5E5KdSU7Exs&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Youtubeのアーカイブ&lt;/a&gt;に残っていますので是非ご覧ください。&lt;/p&gt;
&lt;p&gt;正直なところ、私は英語のヒアリングが下手なので字幕なしだと分からないところもありますね…。このレポートは雰囲気で書いています（笑）&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;それではイベントのレポートに入っていきましょう！&lt;/p&gt;
&lt;h2 id=&quot;1-relay関連のニュース&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-relay%E9%96%A2%E9%80%A3%E3%81%AE%E3%83%8B%E3%83%A5%E3%83%BC%E3%82%B9&quot; aria-label=&quot;1 relay関連のニュース permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. Relay関連のニュース&lt;/h2&gt;
&lt;p&gt;イベントの最初はRelayの関連ニュースの紹介からとなりました。&lt;/p&gt;
&lt;h3 id=&quot;relay-1010リリース&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#relay-1010%E3%83%AA%E3%83%AA%E3%83%BC%E3%82%B9&quot; aria-label=&quot;relay 1010リリース permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Relay 10.1.0リリース&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/facebook/relay/releases/tag/v10.1.0&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Relay 10.1.0&lt;/a&gt;がリリースされました。本記事を書いている時点で10.1.3までリリースされています。&lt;/p&gt;
&lt;p&gt;主なトピックで言うと、&lt;a href=&quot;https://kazekyo.com/posts/20201110-relay-meetup-1#3-mutation%E3%81%A7%E3%81%AE%E3%82%AD%E3%83%A3%E3%83%83%E3%82%B7%E3%83%A5%E3%82%A2%E3%83%83%E3%83%97%E3%83%87%E3%83%BC%E3%83%88%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;前回のMeetupでも話していたStore  updater directive&lt;/a&gt;の種類が増えました。具体的には&lt;code class=&quot;language-text&quot;&gt;@appendNode&lt;/code&gt;や&lt;code class=&quot;language-text&quot;&gt;prependNode&lt;/code&gt;、&lt;code class=&quot;language-text&quot;&gt;deleteEdge&lt;/code&gt;などですね。
Mutationでデータに変更を加えても直接キャッシュを弄るコードを書かなくて良いので本当に便利です。&lt;/p&gt;
&lt;p&gt;他にもcacheConfigの拡張サポートだったりReact17対応が入ったようです。&lt;/p&gt;
&lt;h3 id=&quot;meros&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#meros&quot; aria-label=&quot;meros permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;meros&lt;/h3&gt;
&lt;p&gt;multipart responseの読み込みをサポートしてくれるライブラリである&lt;a href=&quot;https://github.com/maraisr/meros/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;meros&lt;/a&gt;の紹介もありました。
Relay界隈でよく活動している&lt;a href=&quot;https://github.com/maraisr&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Marais Rossouw&lt;/a&gt;が作ってます。&lt;/p&gt;
&lt;p&gt;本イベントでも話される@deferや@streamは&lt;a href=&quot;https://github.com/graphql/graphql-over-http/blob/master/rfcs/IncrementalDelivery.md&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;GraphQLサーバーがレスポンスをmultipartで返す&lt;/a&gt;仕様で、クライアントはそれを受け取って処理できないといけません。
merosはそのあたりの処理をやってくれるライブラリのようです。&lt;/p&gt;
&lt;p&gt;ちなみに、@deferのmultipartなレスポンスを受けるプログラムについて、日本語で解説している方がいますので気になる方は読んでみると良いかもしれません。
⇒ &lt;a href=&quot;https://zenn.dev/adwd/articles/53c991e373f84d&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;GraphQL の @defer, @stream ディレクティブを試してみる&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;graphql-helix&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#graphql-helix&quot; aria-label=&quot;graphql helix permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Graphql Helix&lt;/h3&gt;
&lt;p&gt;Graphql HelixはGraphQLサーバーを構築するためのライブラリの紹介です。こちらも@defer/@streamの文脈での紹介となります。
Graphql HelixはExpressなどのNode.jsのWebフレームワークをGraphQL対応させるためのシンプルなユーティリティ関数集といったところです。シンプルでミニマルなところを特徴としています。
このライブラリは@defer/@streamに対応済みなので、これを使って簡単に@defer/@streamを動かすことができるようです。&lt;/p&gt;
&lt;h2 id=&quot;2-relay-workshop&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-relay-workshop&quot; aria-label=&quot;2 relay workshop permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. Relay Workshop&lt;/h2&gt;
&lt;p&gt;メインセッションの前半パートはRelay Workshopについて。
&lt;a href=&quot;https://youtu.be/5E5KdSU7Exs?t=938&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://youtu.be/5E5KdSU7Exs?t=938&lt;/a&gt;
これを読んでいる方にRelayの初学者がいるかは分かりませんが、初学者の方にとってはSibeliusが作っている&lt;a href=&quot;https://github.com/sibelius/relay-workshop&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Relay Workshop&lt;/a&gt;はRelayを学び始める良い手段になると思います。&lt;/p&gt;
&lt;p&gt;Relayは学習曲線が急な上に公式ドキュメントが薄く、私も学び始めは「このAPIどうやって動かすんだろう？」と思うことがしばしばありました。
そして分からないのでRelayのソースコードを読んで調べるという…。&lt;/p&gt;
&lt;p&gt;そこで&lt;a href=&quot;https://github.com/sibelius/relay-workshop&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Relay Workshop&lt;/a&gt;です。
Relay Workshopでは豊富な実例と共にRelayを学ぶことができます。
サンプルはHook対応で書かれているので、これからのRelayのアップデートで忘れ去られる知識を覚えてなくて良いのも素晴らしいです。&lt;/p&gt;
&lt;p&gt;今回のイベントでは、Relay Workshopがどんなワークショップなのか、どうやって使うのかも含めて説明されていますので、気になる方は動画も見てみてください。&lt;/p&gt;
&lt;p&gt;Sibeliusも話していましたが、Relay初学者が心折れるところは「なんでか分からないけどRelayコンパイラが動かない」という時なんですよね。
初学者じゃなくなるとそのコンパイルがあるからこそ安全に素早く書ける実感が得られるのですが、そこまでが長い。
そうした障害に対して、動作するサンプルコードを豊富に用意することで解決するアプローチは素敵だと思います。&lt;/p&gt;
&lt;h2 id=&quot;3-deferとstream&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-defer%E3%81%A8stream&quot; aria-label=&quot;3 deferとstream permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. @deferと@stream&lt;/h2&gt;
&lt;p&gt;メインセッションの後半パートは@defer/@streamについて。
&lt;a href=&quot;https://youtu.be/5E5KdSU7Exs?t=2807&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://youtu.be/5E5KdSU7Exs?t=2807&lt;/a&gt;
@deferと@streamの話を1stDibsのLiliana MatosとRob Richard、そしてRelayコアチームのJoseph Savonaも参加していました。
1stDibsの2人は@defer/@stremを2020年に一気に実現に近づけた中心人物で、多くの仕様や実装案を作っています。彼らから直接話を聞けるのはとても良い機会でした。&lt;/p&gt;
&lt;p&gt;@defer/@stream自体の解説も当然ありましたが、ここで説明すると長くなってしまうので割愛させてください。
&lt;a href=&quot;https://kazekyo.com/posts/20201005&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;私も以前に解説を書いてますが&lt;/a&gt;、他にも日本語で素晴らしい記事を書いている方がいらっしゃいますのでそちらを見てみると良いと思います。
⇒ &lt;a href=&quot;https://quramy.medium.com/incremental-data-delivery-with-graphql-defer-and-stream-d779b5e38833&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Incremental Data Delivery with GraphQL defer and stream&lt;/a&gt;
⇒ &lt;a href=&quot;https://zenn.dev/adwd/articles/53c991e373f84d&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;GraphQL の @defer, @stream ディレクティブを試してみる&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;さて、イベントに話を戻します。
冒頭のニュースでmerosの話がありましたが、同様のクライアントサイドのライブラリである&lt;a href=&quot;https://github.com/relay-tools/fetch-multipart-graphql&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;fetch-multipart-graphql&lt;/a&gt;をRobが作っているようです。
merosの方がよりコアで汎用的に使える印象ですが、@defer/@streamのためにRelayで使うならこちらの方がサッと実装できそうな気配がしました。&lt;/p&gt;
&lt;p&gt;@defer/@streamをRelayのHookなどと使う実例も見せてくれて、Relayと合わせて使うのはめちゃくちゃ便利そうです。
早くクエリ分割をする世界から開放されたい…。&lt;/p&gt;
&lt;p&gt;今回はチャットから質問を受けて様々なトピックについても話していたのですが、それも面白かったですね。
例えば、なぜクエリ分割ではダメなのか？ フィールドに@deferディレクティブはつけられないのは何故か？ などなど…。
おそらく日本語の記事で知れることが8割以上だと思いますが、仕様を深く理解している人達の解説は面白かったです。
英語が未熟で一部何言ってるか分からないところもありましたが…。主催者のGabrielにYoutubeアーカイブに字幕つけてくれと言ってはいるので、後日字幕ついてるかもしれません。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;まとめ&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E3%81%BE%E3%81%A8%E3%82%81&quot; aria-label=&quot;まとめ permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;まとめ&lt;/h2&gt;
&lt;p&gt;Relay Meetup #2のレポートでした。今回もRelayユーザーにとってはためになる、または今後が楽しみになる会だったのではないでしょうか。&lt;/p&gt;
&lt;p&gt;次回の&lt;a href=&quot;https://relaymeetup.com/meetups/3&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Relay Meetup #3&lt;/a&gt;は2月10日19:00 GMT+2からです。
内容はRelayコアチームによるQ&amp;#x26;A！
とても面白そうな内容で、今年のRelayの計画・スケジュールについても聞けそうです。
今回のミートアップでもRelayコアチームのJosephが少し触れてましたが、experimentalの（少なくともいくつかは）2021年内のリリースを計画しているようです。
ReactのConcurrent Modeに依存してるところはReact待ちになると思いますが、Hooksのリリースなどは早めに見たいところですね。&lt;/p&gt;
&lt;p&gt;それでは、次回もお楽しみに！&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Relay Meetup #1 レポート]]></title><description><![CDATA[2020年になって、Relayが盛り上がってきましたね！ 少し遅くなりましたが、今回は2020年10月21日にオンラインで初開催されたRelayのグローバルミートアップのレポートをお届けします。]]></description><link>https://kazekyo.com/posts/20201110-relay-meetup-1</link><guid isPermaLink="false">https://kazekyo.com/posts/20201110-relay-meetup-1</guid><pubDate>Tue, 10 Nov 2020 06:27:35 GMT</pubDate><content:encoded>&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/7d67671bbde830b70ca8c755c0063142/f2763/201110-relay-meetup-1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 52.916666666666664%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsSAAALEgHS3X78AAAC6klEQVQoz02SW0yTZxjHv3hCseqygqCWfkA59IAtMmCKYgRlmC1o4o2HC292b+KdOqWIU5wXW7ItWcY8YCRUTmZTDoVpiSbLWm0pVDHRKVBgVCWeIG08TH4+fHCxi1++L8//eX/f+7zvp4wNenk79YjY5GNgQnglTApTQlSI/Y/oXP31XN8Eb4Z+pbIik84T+7n23V6USPgOvB/j5cQDTp85wxFnFZXHv+WYUFl9im+qajjsrOHo8Rqc1Sc5Wi2ZcOhYFT/9/CPD3h849fVGus866ak9gDIe9sN0hH/DIRKMuSiL1qAsTUWJN6LEqcSvMJCoX82iZSkoi9XZuk7yeclYPythfNDHeDiA76821uflzAh74cMTImP3sORt4RODnaS0PD41rsNqzuHQ9hQObFM5WKZizHCgV9dp+bJkG5u27eRtdFRGf85Afw9qqnwoMiLC6aeMjw2QaS9Gl2RFn2InLslBRX46uwrTWJ+TiXN7EsX2TBYnO2QSB0sSstlQUkH09RD894S7wRtkmEyyw5Gg7HBWmOXYzPJVNm3B0tW5IsjiSPkaeWazZ0Mq+TYzOqknqrnEJ5opKt1BdHJY29DdoAfTjDAyKsLpZyK8T6q1iDh9FrpkK7qVZhYmWCixpbC7YBWWdBML9BaZwKKNO3+FiYLiL4lNhbX19/rmhMP/eHkXCxMe6tNGyLBv0s7SVrCVtYVlmAvKyc4vJ6fwC+yfl2HNL5W8hDRbEV/t2seLiYe8j40Q8LlJT09DCd52Mzrk5/EDH329N+n13yLU7+PqHy1cdtXTUH+RugtnhXM0NzVw43qHlmt9wVsMPpRblmPzdDeiqvIXeLpc9Pm7CXi7CAV6CIl0IPQ3HW0tIjtPS9MlLtbViryO1uZ6bnratbxf+voDHvxet3YhrY21GAwGlGbXL1x3u3C3NwguujqahGahFXfHFbrdv9PZNvve2d4q9Ra6OmfyRum/TPs1F3+6mzj/2/eoRpWPVnU29rn1sjsAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
        &lt;source
          srcset=&quot;/static/7d67671bbde830b70ca8c755c0063142/8ac56/201110-relay-meetup-1.webp 240w,
/static/7d67671bbde830b70ca8c755c0063142/d3be9/201110-relay-meetup-1.webp 480w,
/static/7d67671bbde830b70ca8c755c0063142/e46b2/201110-relay-meetup-1.webp 960w,
/static/7d67671bbde830b70ca8c755c0063142/b21f7/201110-relay-meetup-1.webp 1291w&quot;
          sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
          type=&quot;image/webp&quot;
        /&gt;
        &lt;source
          srcset=&quot;/static/7d67671bbde830b70ca8c755c0063142/8ff5a/201110-relay-meetup-1.png 240w,
/static/7d67671bbde830b70ca8c755c0063142/e85cb/201110-relay-meetup-1.png 480w,
/static/7d67671bbde830b70ca8c755c0063142/d9199/201110-relay-meetup-1.png 960w,
/static/7d67671bbde830b70ca8c755c0063142/f2763/201110-relay-meetup-1.png 1291w&quot;
          sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
          type=&quot;image/png&quot;
        /&gt;
        &lt;img
          class=&quot;gatsby-resp-image-image&quot;
          src=&quot;/static/7d67671bbde830b70ca8c755c0063142/d9199/201110-relay-meetup-1.png&quot;
          alt=&quot;image&quot;
          title=&quot;image&quot;
          loading=&quot;lazy&quot;
          style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        /&gt;
      &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;2020年になって、Relayが盛り上がってきましたね！
少し遅くなりましたが、今回は2020年10月21日にオンラインで初開催された&lt;a href=&quot;https://relaymeetup.com/meetups/1&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Relayのグローバルミートアップ&lt;/a&gt;のレポートをお届けします。&lt;/p&gt;
&lt;p&gt;今回は主催者の&lt;a href=&quot;https://twitter.com/___zth___&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Gabriel Nordeborn&lt;/a&gt;、元Artsyで現Microsoftの&lt;a href=&quot;https://twitter.com/alloy&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Eloy Durán&lt;/a&gt;、FacebookのRelayコアチームの&lt;a href=&quot;https://twitter.com/kassens&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Jan Kassens&lt;/a&gt;の3名が登壇してくれました。&lt;/p&gt;
&lt;h2 id=&quot;オンラインでの開催&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E3%82%AA%E3%83%B3%E3%83%A9%E3%82%A4%E3%83%B3%E3%81%A7%E3%81%AE%E9%96%8B%E5%82%AC&quot; aria-label=&quot;オンラインでの開催 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;オンラインでの開催&lt;/h2&gt;
&lt;p&gt;今回はYoutube上でのオンラインミートアップとして開催されました。参加者はYoutubeのチャットやDiscordでの交流となりました。
開始時間が日本時間で深夜2時ぐらいだったので私もリアルタイムでは見てなかったのですが、&lt;a href=&quot;https://www.youtube.com/watch?v=wLrityvveSQ&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;当日の模様は動画で残ってる&lt;/a&gt;ので今からでも見ることができます。&lt;/p&gt;
&lt;p&gt;ちなみに全て英語です。Youtubeの自動翻訳はついてますが、厳しい場合はこのレポートを読みながらご覧ください。&lt;/p&gt;
&lt;h2 id=&quot;1-relayコミュニティからのニュース&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-relay%E3%82%B3%E3%83%9F%E3%83%A5%E3%83%8B%E3%83%86%E3%82%A3%E3%81%8B%E3%82%89%E3%81%AE%E3%83%8B%E3%83%A5%E3%83%BC%E3%82%B9&quot; aria-label=&quot;1 relayコミュニティからのニュース permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. Relayコミュニティからのニュース&lt;/h2&gt;
&lt;p&gt;それでは内容に入っていきましょう。
最初はFacebook外のRelayのコミュニティのニュースから。&lt;/p&gt;
&lt;p&gt;Relayは長らくFacebook所属のコアチームが作っており、コミュニティとは距離を置いていると思われてきましたし、実際そういった面も否めません。
しかしFacebook外のコミュニティメンバーからのコミットは継続的にあり、特に最近はインパクトのあるPRが外部から出されてマージされています。
Relay Meetupでコミュニティの活動が紹介され、目に見える動きがあるのはとても嬉しいことですね。&lt;/p&gt;
&lt;p&gt;今回は下記が紹介されました。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Rustで作成中のRelayの本家コンパイラで、基本的なTypeScript連携が含まれました &lt;a href=&quot;https://github.com/facebook/relay/issues/3182&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://github.com/facebook/relay/issues/3182&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;DefinitelyTypedにあるRelayの型にexperimentalの機能を含む多くのアップデートが行われました &lt;a href=&quot;https://github.com/DefinitelyTyped/DefinitelyTyped/pull/48638&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://github.com/DefinitelyTyped/DefinitelyTyped/pull/48638&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;webpackを使用したエントリーポイントのOSS実装が進行しています &lt;a href=&quot;https://github.com/relay-tools/typescript-relayjs-examples/issues/1&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://github.com/relay-tools/typescript-relayjs-examples/issues/1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;react-relay-offlineというオフラインでrelayを使用できるようにするためのパッケージで、render-as-you-fetchがサポートされました &lt;a href=&quot;https://github.com/morrys/react-relay-offline/pull/57&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://github.com/morrys/react-relay-offline/pull/57&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;1と2はTypeScriptとRelayがより親密になるという話で、3と4はReactのrender-as-you-fetchを（OSS版での）Relayで実現する話です。&lt;/p&gt;
&lt;p&gt;1のTypeScript連携も素晴らしいですが、3のエントリーポイントは今後がかなり気になります。
ここで詳細は解説しませんがrender-as-you-fetchではJSを1ファイルではなく分割して後で追加でダウンロードします。今後、コンポーネントとデータの遅延ダウンロードの実装方法についてOSSでのガイドが出てきそうです。
SPAでJSのサイズが大きい問題から逃れられる日も近いかもしれません。&lt;/p&gt;
&lt;h2 id=&quot;2-artsyでのrelayについて&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-artsy%E3%81%A7%E3%81%AErelay%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6&quot; aria-label=&quot;2 artsyでのrelayについて permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. ArtsyでのRelayについて&lt;/h2&gt;
&lt;p&gt;Relayを使っている方はArtsyという会社を知っているかもしれません。Relayコミュニティに対して貴重なブログやgithubリポジトリを公開しており（プロダクションで使用されているコードが公開されています）、コミュニティ内では一定の認知度があります。
今回はArtsyでのRelayの経験をalloy（&lt;a href=&quot;https://twitter.com/alloy&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Eloy Durán&lt;/a&gt;）が語ってくれています。&lt;/p&gt;
&lt;p&gt;alloyは現Microsoftのエンジニアで、以前はArtsyで数年働いていました。
彼はReactNative、TypeScript、RelayでArtsyのモバイルのアプリケーションを構築することになります。なぜそれらを選んだのか、どういった過程になり、Webやモバイルのチームがどう反応してくれたのかについて話してくれました。
また、Relayの素晴らしさと学習の難点、OSSの貢献についても議論されました。&lt;/p&gt;
&lt;h2 id=&quot;3-mutationでのキャッシュアップデートについて&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-mutation%E3%81%A7%E3%81%AE%E3%82%AD%E3%83%A3%E3%83%83%E3%82%B7%E3%83%A5%E3%82%A2%E3%83%83%E3%83%97%E3%83%87%E3%83%BC%E3%83%88%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6&quot; aria-label=&quot;3 mutationでのキャッシュアップデートについて permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. Mutationでのキャッシュアップデートについて&lt;/h2&gt;
&lt;p&gt;最後のパネルディスカッションで、Mutationでのキャッシュアップデートの戦略について議論されました。
少し話が入り組んでるので、このパートは分かりやすいように内容をまとめ直します。彼らが話している順番で記載していないのでご注意ください。&lt;/p&gt;
&lt;p&gt;まず、Mutation使用時の原始的なクライアントキャッシュの更新方法は、updaterを使ってストアの中身を自分で命令的に書き換えていくことです。
&lt;a href=&quot;https://relay.dev/docs/en/mutations#using-updater-and-optimisticupdater&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://relay.dev/docs/en/mutations#using-updater-and-optimisticupdater&lt;/a&gt;
これはまずまず大変です。型もなく、自分であれこれ操作しなければなりません。
Relayを使い始めた人は最初にupdaterを試すと思うのですが、結構難しくてどこが間違って動かないのか分からないですよね。
まぁ、プロダクションコードではほぼ後述する方法を使うことになるので最初はあまり気にする必要もないと思いますが…。&lt;/p&gt;
&lt;p&gt;updaterよりも簡単な方法として、updater configsでの宣言的な更新があります。
&lt;a href=&quot;https://relay.dev/docs/en/mutations#updater-configs&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://relay.dev/docs/en/mutations#updater-configs&lt;/a&gt;
ストアを直接弄るよりも圧倒的に簡単でバグも少なく更新できます。例えばキャッシュからデータを削除する時は&lt;code class=&quot;language-text&quot;&gt;NODE_DELETE&lt;/code&gt;を使いますが、使い方さえ分かれば&lt;a href=&quot;https://relay.dev/docs/en/mutations#node_delete&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;圧倒的に簡単です&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;そして、最近RelayにマージされたStore updater directivesはさらに簡単です。具体的には&lt;code class=&quot;language-text&quot;&gt;@deleteRecord&lt;/code&gt;、&lt;code class=&quot;language-text&quot;&gt;@appendEdge&lt;/code&gt;、&lt;code class=&quot;language-text&quot;&gt;@prependEdge&lt;/code&gt;、&lt;code class=&quot;language-text&quot;&gt;@appendNode&lt;/code&gt;、&lt;code class=&quot;language-text&quot;&gt;@prependNode&lt;/code&gt;のディレクティブです。
名前からなんとなく分かるかもしれませんが、例えば削除するオブジェクトのidに&lt;code class=&quot;language-text&quot;&gt;＠deleteRecord&lt;/code&gt;をつけるだけで、Mutation実行時にキャッシュから削除できます。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;graphql&quot;&gt;&lt;pre class=&quot;language-graphql&quot;&gt;&lt;code class=&quot;language-graphql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;mutation&lt;/span&gt; CommentDeleteMutation&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;token variable&quot;&gt;$input&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; CommentDeleteInput
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  commentDelete&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$input&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    deletedCommentId &lt;span class=&quot;token directive function&quot;&gt;@deleteRecord&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;@appendEdge&lt;/code&gt;などを使えばConnectionにEdgeを簡単に追加することも可能になります。&lt;/p&gt;
&lt;p&gt;さて、updater configsやStore updater directivesは便利ですが、全てをこのように宣言的に記述することは難しいのが現実で、ストアを直接弄る低レベルなAPI（= updater）が必要なシーンもあります。
特に、他のオブジェクトにも副作用が発生するようなケースは宣言的に記述するのが難しいです。例えばFacebookでは友達をブロックすると、友達リストの他にフィードなど異なるidをもつオブジェクトを消さなければなりません。こうした副作用で様々な処理をするケースでは上記のように宣言的なキャッシュの更新だけでは対処が難しいので注意が必要です。&lt;/p&gt;
&lt;p&gt;ちなみに、Relayのexperimental版にはInvalidation APIというものがあり、Mutationの実行時にキャッシュの古いデータ（一般的にはMutationで更新したことで影響されたであろうデータ）をマークしてRelayに教えることができるようです。
これを使うことで、次に古いデータを評価するタイミングでキャッシュを使用せずに再フェッチできるんだとか。すごい。
&lt;a href=&quot;https://relay.dev/docs/en/experimental/a-guided-tour-of-relay#staleness-of-data&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://relay.dev/docs/en/experimental/a-guided-tour-of-relay#staleness-of-data&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;まとめ&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E3%81%BE%E3%81%A8%E3%82%81&quot; aria-label=&quot;まとめ permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;まとめ&lt;/h2&gt;
&lt;p&gt;Relay Meetupの記念すべき初回のレポートをお届けしました。かなりディープで参考になる話題が盛りだくさんだったのではないでしょうか。
最近はRelayの記事をいくつか書いたり主催者のGabrielとはRelayの翻訳関係でやり取りしたりしていたので、今回のミートアップの開催はとても楽しみにしていました。
Relayはコミュニティでの繋がりって世界的にもほぼないので、ここからコミュニティの活動も広まっていくと良いですね。&lt;/p&gt;
&lt;p&gt;次回の&lt;a href=&quot;https://relaymeetup.com/meetups/2&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Relay Meetup #2&lt;/a&gt;は12月2日19:00 GMT+2からです。
前半パートでは、初心者向けにRelayのワークショップを作っている方が、Relayの始め方について教えてくれます。
後半パートでは@deferと@streamについて、GraphQLの仕様にこれらのディレクティブを追加したメンバーが直接教えてくれます。少し前に@deferと@streamについて&lt;a href=&quot;posts/20201005&quot;&gt;日本語で書いたので&lt;/a&gt;、そちらを見ておくと英語が苦手でも理解できるかも…？（私が嘘書いてなければ）&lt;/p&gt;
&lt;p&gt;それでは次回をお楽しみに！&lt;/p&gt;</content:encoded></item><item><title><![CDATA[インフラをコード化するPulumiを実運用している話]]></title><description><![CDATA[Webサービスのインフラは年々複雑化しており、コード化なしにインフラを構築するのはどんどん難しくなっています。最近はIaC（Infrastructure as Code）のPulumiを使ってインフラをコード化しているので、使用感を書いてみようかと思います。]]></description><link>https://kazekyo.com/posts/20201102-pulumi</link><guid isPermaLink="false">https://kazekyo.com/posts/20201102-pulumi</guid><pubDate>Mon, 02 Nov 2020 11:15:00 GMT</pubDate><content:encoded>&lt;p&gt;Webサービスのインフラは年々複雑化しており、コード化なしにインフラを構築するのはどんどん難しくなっています。最近はIaC（Infrastructure as Code）のPulumiを使ってインフラをコード化しているので、使用感を書いてみようかと思います。&lt;/p&gt;
&lt;h2 id=&quot;むかし話&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E3%82%80%E3%81%8B%E3%81%97%E8%A9%B1&quot; aria-label=&quot;むかし話 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;むかし話&lt;/h2&gt;
&lt;p&gt;私がまだ新卒だった2011年〜2012年あたりでは、&lt;a href=&quot;https://github.com/chef/chef&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Chef&lt;/a&gt;と&lt;a href=&quot;https://github.com/capistrano/capistrano&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Capistrano&lt;/a&gt;を使ってAWSのEC2のサーバーの構築を自動化していたような気がします。私がメインでやっていたわけではないのですが、Capistranoあたりは少し触っていました。
その後は&lt;a href=&quot;https://github.com/ansible/ansible&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Ansible&lt;/a&gt;も少し扱ったことがありますが、このあたりまでの記憶はほぼないので省略します。&lt;/p&gt;
&lt;p&gt;明確に記憶があるのはAWSのリソースをYAMLで宣言的に記述できる&lt;a href=&quot;https://aws.amazon.com/jp/cloudformation/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;AWS CloudFormation&lt;/a&gt;です。
CloudFormationはEC2の中身をセットアップをするようなものではなく、AWS上のリソースを用意するためのものです。EC2のサーバーを立てる、RDSを立てる、WAFを立てる、といったAWS上でのリソース全体を構築します。&lt;/p&gt;
&lt;p&gt;この時代は既にサーバーの中身の構築はDockerにトレンドが移行していました。CloudFormationでAWS上のリソースを用意して、サーバーの中はDockerのImageを動かすということをしていました。&lt;/p&gt;
&lt;h2 id=&quot;yamlでリソースを作るの大変すぎ問題&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#yaml%E3%81%A7%E3%83%AA%E3%82%BD%E3%83%BC%E3%82%B9%E3%82%92%E4%BD%9C%E3%82%8B%E3%81%AE%E5%A4%A7%E5%A4%89%E3%81%99%E3%81%8E%E5%95%8F%E9%A1%8C&quot; aria-label=&quot;yamlでリソースを作るの大変すぎ問題 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;YAMLでリソースを作るの大変すぎ問題&lt;/h2&gt;
&lt;p&gt;CloudFormationはYAMLで書きます。これがなかなか辛いです。
当たり前ですがYAMLには型がないので、ドキュメント見ながら書いてても普通に間違えちゃうんですよね。タイポというより、オプション等のネストの指定の仕方などYAMLでの構成を間違えてしまいます。&lt;/p&gt;
&lt;p&gt;悪いことにAWS側のYAMLの検証が微妙で、記述が間違ってても普通にデプロイが走ってしまうことがままあり…というか今は分かりませんが当時は間違っててもほぼ通ってしまい、&lt;strong&gt;実行時にミスが分かってロールバックされる&lt;/strong&gt;のが辛いです。もっと酷いとロールバックできなくなって手動で戻さないといけない時もあります。&lt;/p&gt;
&lt;p&gt;顕著に悪い部分が出るのは、DBやCloudFrontなど作るのに時間がかかるリソースの作成や更新の時ですね。デプロイ始めてから30分後にロールバックされてしまうと、1日8時間勤務だとして16回しか試せないという…。&lt;/p&gt;
&lt;p&gt;また、プログラミング言語で書けば一瞬で終わるようなものを地道に書かなければならなかったり、YAML上で特殊な記法を使って保守性を高める必要があったりする点も微妙でした。&lt;/p&gt;
&lt;h2 id=&quot;そしてpulumiへ&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E3%81%9D%E3%81%97%E3%81%A6pulumi%E3%81%B8&quot; aria-label=&quot;そしてpulumiへ permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;そしてPulumiへ&lt;/h2&gt;
&lt;p&gt;とにかくYAMLが嫌で、型のあるモダンなプログラミング言語でAWSを構築したいという思いで&lt;a href=&quot;https://www.pulumi.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Pulumi&lt;/a&gt;に飛び込んでいきました。
今であればAWS CDKも良いと思うのですが、当時はまだ生まれたてだったので選択肢には入りませんでした。&lt;/p&gt;
&lt;p&gt;PulumiはモダンなIaCです。簡単に特徴をまとめると、次の通りです。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;TypeScript、Python、Go、.Net（C#など）といったプログラミング言語でインフラの構成を宣言的に記述できます&lt;/li&gt;
&lt;li&gt;AWSやGCPなどマルチクラウドのリソースを構築できます&lt;/li&gt;
&lt;li&gt;Kubernetesのマニュフェストなどもプログラミング言語で書けます&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;私はTypeScriptで書いていますが、とても生産性が高いです。&lt;/p&gt;
&lt;h2 id=&quot;pulumiを実運用した感想&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#pulumi%E3%82%92%E5%AE%9F%E9%81%8B%E7%94%A8%E3%81%97%E3%81%9F%E6%84%9F%E6%83%B3&quot; aria-label=&quot;pulumiを実運用した感想 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Pulumiを実運用した感想&lt;/h2&gt;
&lt;p&gt;しばらく運用しているので簡単に感想でも。&lt;/p&gt;
&lt;h3 id=&quot;良いところ&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E8%89%AF%E3%81%84%E3%81%A8%E3%81%93%E3%82%8D&quot; aria-label=&quot;良いところ permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;良いところ&lt;/h3&gt;
&lt;p&gt;TypeScriptで書けるのはとても便利です。
型がある点だけでなく、インフラでのコンテキストスイッチが小さくて済む点もお気に入りですね。私はフロントもバックエンドもTypeScriptで書くので、いざインフラのコードを書く時も違和感なく書けます。&lt;/p&gt;
&lt;p&gt;もう1つお気に入りなのは、PulumiではDevOpsで必要なワークフロー全体をシームレスに統合できるところです。
例えば、k8sのサポートがかなり入ってるので、PulumiのコードとしてDockerのimageを作って、レジストリに上げて、k8sのマニュフェスト更新するところまでワンストップで短い行数で記述できます。あとはGithubのPull Requestのマージ時にそのコードをCI/CDでキックするだけで、自動的にk8sのPodを入れ替えることができます。
他にも、アプリケーションやインフラで使用する各種シークレットの管理もできます。DBのパスワードをコード上で作ってから、k8sのPodの環境変数に埋め込んだりできるので、パスワード管理しなくて良い部分も多いです。&lt;/p&gt;
&lt;p&gt;他にもインフラの構築を便利にするもの（例えばDBの移行スクリプト的なものを作れるAPIなど）がたくさんあり、痒いところに手が届きます。&lt;/p&gt;
&lt;h3 id=&quot;微妙なところ&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E5%BE%AE%E5%A6%99%E3%81%AA%E3%81%A8%E3%81%93%E3%82%8D&quot; aria-label=&quot;微妙なところ permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;微妙なところ&lt;/h3&gt;
&lt;p&gt;Pulumiはマルチクラウドに対応しており、AWSやGCPなど様々なところでリソースを作れます。AWSやGCP以外にも、変わったところでは認証基盤である&lt;a href=&quot;https://auth0.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Auth0&lt;/a&gt;でテナントを立てたりできます。&lt;/p&gt;
&lt;p&gt;これはProviderという、雑に言うとプラグインシステムのようなものを本体と一緒に使うことで実現されています。AWSのProviderやGCPのProviderがあり、それ経由でリソースを構築するということです。&lt;/p&gt;
&lt;p&gt;そのPulumiのAWSのProviderなんですが、なんと競合のTerraformがOSSとして作っている&lt;a href=&quot;https://github.com/terraform-providers/terraform-provider-aws&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;terraform-provider-aws&lt;/a&gt;をベースに、型を導入するなど手を加えて使っているんですね。ドキュメントにもしっかりと&lt;a href=&quot;https://www.pulumi.com/docs/reference/pkg/aws/provider/#package-details&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;This Pulumi package is based on the aws Terraform Provider.&lt;/a&gt;と書かれています。&lt;/p&gt;
&lt;p&gt;それ自体は良いのですが、terraform-provider-awsで対応が遅れているリソースは当然Pulumiにもないんですよね。例えばAWSにWAFv2のリソースが作れるようになってから半年以上は、Terraformが対応できてなかったのでPulumiも同時に対応していないという状況だったと思います。&lt;/p&gt;
&lt;p&gt;また、terraform-provider-awsから生成した型が微妙なところがちらほら。インスタンスタイプなど、明らかに型をUnion（またはEnum）で表現できるところをstringにまとめちゃっていて、タイポできる余地が残っているところがあります。&lt;/p&gt;
&lt;h2 id=&quot;まとめ&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E3%81%BE%E3%81%A8%E3%82%81&quot; aria-label=&quot;まとめ permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;まとめ&lt;/h2&gt;
&lt;p&gt;正直、Pulumiにはかなり満足しています。今までのインフラ構築の体験の中でベストな体験です。
AWSのリソースを作るということ以上に、アプリケーションとインフラの構築プロセスの全てがちゃんと便利になっている実感があります。&lt;/p&gt;
&lt;p&gt;しかし不満がないわけではなく、不満の一部はAWS CDKに分があるように思うので、時間があればAWS CDKも見てみようかなと思っています。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[リッチテキストエディタを作れるSlate、2020年の状況]]></title><description><![CDATA[Webサービスにリッチテキストエディタの機能をつける時、自分でフルスクラッチで書くのは大変です。
Draft.jsを筆頭にOSSで使えるものはいくつかあり、私はSlateを使っています。そんなSlateの現状について、使用者の一人としてざっとまとめていきたいと思います。]]></description><link>https://kazekyo.com/posts/20201101-slate</link><guid isPermaLink="false">https://kazekyo.com/posts/20201101-slate</guid><pubDate>Sun, 01 Nov 2020 06:00:22 GMT</pubDate><content:encoded>&lt;p&gt;Webサービスにリッチテキストエディタの機能をつける時、自分でフルスクラッチで書くのは大変です。
&lt;a href=&quot;https://draftjs.org/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Draft.js&lt;/a&gt;を筆頭にOSSで使えるものはいくつかあり、私は&lt;a href=&quot;https://github.com/ianstormtaylor/slate&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Slate&lt;/a&gt;を使っています。そんなSlateの現状について、使用者の一人としてざっとまとめていきたいと思います。&lt;/p&gt;
&lt;p&gt;なお、2020年11月1日現在の話であり、あなたがこれを見ている時には状況が変わってる部分も多くあると思います。&lt;/p&gt;
&lt;h2 id=&quot;私がslateを使い始めた理由&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E7%A7%81%E3%81%8Cslate%E3%82%92%E4%BD%BF%E3%81%84%E5%A7%8B%E3%82%81%E3%81%9F%E7%90%86%E7%94%B1&quot; aria-label=&quot;私がslateを使い始めた理由 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;私がSlateを使い始めた理由&lt;/h2&gt;
&lt;p&gt;2018年頃、当初はFacebook製のDraft.jsを利用していました。
しかしながらDraft.jsを使って複雑なプラグインを書くのが大変だったのと、何よりDraft.jsで共同編集の実装に必要なノウハウをインターネット上で発見できなかったので、代わりを探し始めました。
これはDraft.jsで共同編集の実装が不可能であるというわけではなく、当時の自分にガイドなく実装できるだけの知識がなかったというだけです。もしDraft.jsで共同編集を実装したい人は&lt;a href=&quot;https://github.com/facebook/draft-js/issues/93&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;GithubのIssue&lt;/a&gt;を覗いてみると良いかもしれません。&lt;/p&gt;
&lt;p&gt;さて、当時から（今も）SlateはBeta版です。プロダクションでの利用は推奨されていませんでしたが、共同編集へのサポートとプラグインの書きやすさ（内部状態の可読性とAPIの扱いやすさ）から移行を決定しました。
Slateへの移行で共同編集を含む様々なプラグインを実装でき、当時はSlateに満足していました。&lt;/p&gt;
&lt;h2 id=&quot;現在のslateの状況&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E7%8F%BE%E5%9C%A8%E3%81%AEslate%E3%81%AE%E7%8A%B6%E6%B3%81&quot; aria-label=&quot;現在のslateの状況 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;現在のSlateの状況&lt;/h2&gt;
&lt;p&gt;しかしながら2020年末現在、Slateは少し雲行きの怪しい状況にあります。
もちろん悪いニュースだけでなく良いニュースもあるのでそれも含めて紹介していきたいと思います。&lt;/p&gt;
&lt;h3 id=&quot;作者不在&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E4%BD%9C%E8%80%85%E4%B8%8D%E5%9C%A8&quot; aria-label=&quot;作者不在 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;作者不在&lt;/h3&gt;
&lt;p&gt;まず、作者であるIanは&lt;a href=&quot;https://github.com/ianstormtaylor/slate/commits?author=ianstormtaylor&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;2020年になってほとんどSlateに参加していません&lt;/a&gt;。Slackはもちろん、GithubのIssueやPull Request（以下PR）にも出てきません。
READMEには作者から「改善が必要な場合は（報告するだけでなく）自分でコントリビュートしてください」というような、まぁ至極当然ではあるのですが、そういう記載が加えられました。&lt;/p&gt;
&lt;p&gt;作者はもともと関与している時期とそうでない時期の差が大きかった気がしますが、今年は本当に全然参加していないので、Slateは数人のメンテナが保守的にPRをマージしています。
しかしAPIの変更を伴うような”判断”が必要な議論はあまり進んでおらず、コミュニティ全体の活動が低下しています。バグフィックスのPRでさえ全ては処理できない状況です。&lt;/p&gt;
&lt;p&gt;Slackでは「最悪、リポジトリをフォークして使うのが良いよ」というアドバイスが飛び交っております。来年は状況変わるんでしょうか。&lt;/p&gt;
&lt;h3 id=&quot;日本語入力のサポートはまだ厳しい&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E6%97%A5%E6%9C%AC%E8%AA%9E%E5%85%A5%E5%8A%9B%E3%81%AE%E3%82%B5%E3%83%9D%E3%83%BC%E3%83%88%E3%81%AF%E3%81%BE%E3%81%A0%E5%8E%B3%E3%81%97%E3%81%84&quot; aria-label=&quot;日本語入力のサポートはまだ厳しい permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;日本語入力のサポートはまだ厳しい&lt;/h3&gt;
&lt;p&gt;以前より良くなっていますが、日本語入力のサポートはまだ厳しい状況が続いています。
厳密に言えば日本語というよりもIMEを使っての入力がよくバグります。例えばSafariではIMEを使って入力すると、常にカーソルが行頭に飛ぶなどの致命的なバグがあります。&lt;/p&gt;
&lt;p&gt;バグ自体は良いのですが、問題は修正PRが全くマージされないことです。&lt;a href=&quot;https://github.com/ianstormtaylor/slate/pull/3626&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;このPR&lt;/a&gt;とか&lt;a href=&quot;https://github.com/ianstormtaylor/slate/pull/3342&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;このPR&lt;/a&gt;とか。
Slackでも何度も話題に上がってますが、アクティブなメンテナの中にIMEを使っている人がいないみたいなので致し方ないです。変えたければ自分がメンテナになれって話なので。
何人かはリポジトリをフォークしてIME関連のパッチを当てて使ってるようです。&lt;/p&gt;
&lt;h3 id=&quot;どうなるandroidサポート&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E3%81%A9%E3%81%86%E3%81%AA%E3%82%8Bandroid%E3%82%B5%E3%83%9D%E3%83%BC%E3%83%88&quot; aria-label=&quot;どうなるandroidサポート permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;どうなるAndroidサポート&lt;/h3&gt;
&lt;p&gt;モバイル対応ですが、iOSはともかくAndroidは現時点で動きません。ちなみにネイティブの話ではなくモバイルのブラウザの話で、ネイティブはターゲット外です。&lt;/p&gt;
&lt;p&gt;Androidで動かすためには複雑なハックが必要なようです。そのようなハックを作者Ianが前のバージョンアップ時に&lt;strong&gt;全部消しちゃった&lt;/strong&gt;ので、Androidは動かなくなりました。&lt;/p&gt;
&lt;p&gt;そこで再度入れようという話になったのですが、Androidサポートは元々Ianではなく別のメンテナが担当していて、その人が&lt;a href=&quot;https://github.com/ianstormtaylor/slate/issues/3786&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;金銭的な寄付を求める&lt;/a&gt;という事態になり、コミュニティが若干ざわつきます。
個人的には「お金を払えば作ってもらえるなら最高じゃん」と考えているので、このプロジェクトに&lt;a href=&quot;https://www.kickstarter.com/projects/sunnyhirai/add-android-support-for-the-slate-wysiwyg-editor&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;寄付しました&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;お金は無事集まって完成に向かって走っているようですが、今後どれぐらいメンテされるんでしょうか。&lt;/p&gt;
&lt;h3 id=&quot;プラグインは超前進&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E3%83%97%E3%83%A9%E3%82%B0%E3%82%A4%E3%83%B3%E3%81%AF%E8%B6%85%E5%89%8D%E9%80%B2&quot; aria-label=&quot;プラグインは超前進 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;プラグインは超前進&lt;/h3&gt;
&lt;p&gt;プラグインに関してはとても良いニュースがありました。
SlateはAPIが秀逸でプラグインが作りやすいのですが、Slateに残ってるバグを踏むこともままあり、それなりに詳しくないと品質の高いプラグインは作れません。
そんななか、今年活発に開発されているプラグイン集の&lt;a href=&quot;https://github.com/udecode/slate-plugins&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;slate-plugins&lt;/a&gt;は質と量ともに大変素晴らしいです。&lt;/p&gt;
&lt;p&gt;Slateで何ができるかはslate-pluginsのStorybookを見たら分かるといっても過言じゃないぐらいのプラグインの見本市です。
コードも綺麗なのでちょっとソースコードを見れば自分でも拡張できます。
私は自分で書いていたプラグインのほとんど（共同編集など以外）をslate-pluginsにリプレースしました。&lt;/p&gt;
&lt;p&gt;slate-pluginsでは今のところGithubでIssueやPRを投げたら反応早いですし、Slackでのチャンネルも活発です。&lt;/p&gt;
&lt;h2 id=&quot;私のこれから&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E7%A7%81%E3%81%AE%E3%81%93%E3%82%8C%E3%81%8B%E3%82%89&quot; aria-label=&quot;私のこれから permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;私のこれから&lt;/h2&gt;
&lt;p&gt;slate-pluginsは素晴らしいものの、本体の開発状況を考えると雲行きは怪しいと言わざるをえません。申し訳ないですがメンテナになるほどの気力はなく、移行を考えています。&lt;/p&gt;
&lt;p&gt;移行先として考えているのは&lt;a href=&quot;https://prosemirror.net/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;ProseMirror&lt;/a&gt;ですね。IMEがちゃんと動いてくれて、プラグインが満足に組めればそれで良いので、そこだけ祈ってます。
共同編集については別で書きたいと思うのですが、自分で作らなくてもProseMirrorには&lt;a href=&quot;https://github.com/yjs/y-prosemirror&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;y-prosemirror&lt;/a&gt;というCRDTで動くプラグインがあるので良さそうです。
一応Slateにも似たようなものはありますが、今のところ&lt;a href=&quot;https://github.com/cudr/slate-collaborative/issues/28&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;自分でほとんど書き直すぐらいの気力が必要&lt;/a&gt;です。私は実際書き直しました。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[ブログを（ここに）移行しました]]></title><description><![CDATA[ブログを移行しました。移行というか、Qiitaなど他サイトにあったものを集約しました。移行した理由や技術スタックについて話します。]]></description><link>https://kazekyo.com/posts/20201027-blog</link><guid isPermaLink="false">https://kazekyo.com/posts/20201027-blog</guid><pubDate>Tue, 27 Oct 2020 06:45:04 GMT</pubDate><content:encoded>&lt;p&gt;ブログを移行しました。移行というか、Qiitaなど他サイトにあったものを集約しました。&lt;/p&gt;
&lt;h2 id=&quot;移行した理由&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E7%A7%BB%E8%A1%8C%E3%81%97%E3%81%9F%E7%90%86%E7%94%B1&quot; aria-label=&quot;移行した理由 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;移行した理由&lt;/h2&gt;
&lt;p&gt;理由の1つは、技術をQiitaで書いて、それ以外を別のサイトに書くのが面倒で集約したかったからです。
Markdownの解釈がサイトによって違ったり、そもそもWYSIWYGでしか書けなかったりすると、差異を考慮して記事を書くのがなかなか厳しい。
仕事柄、マネジメントやビジネスについても書きたいことがいろいろあるので、それも多く書いていきたいとなるとブログを1つにまとめた方が早いなと。
もちろん、読者層としては全然違うと思うので読む方にとっては不便だと思うのですが許してやってください。&lt;/p&gt;
&lt;p&gt;もう1つの理由は、Qiitaと自分があまり相性が良くないと思うようになったことです。
昔はRailsの記事とかを書いていて、あそこには一定Railsユーザーがいたのでそれでも良かったのですが、最近書きたいものはQiitaではマイナーなものばかりです。
どちらかと言うとタイトルがキャッチーではない、外部から検索でたどり着くような詳細な技術トピックを書くことが多いので、であれば無理してQiitaで書くほどでもないかと思うようになりました。&lt;/p&gt;
&lt;h2 id=&quot;何でできているか&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E4%BD%95%E3%81%A7%E3%81%A7%E3%81%8D%E3%81%A6%E3%81%84%E3%82%8B%E3%81%8B&quot; aria-label=&quot;何でできているか permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;何でできているか&lt;/h2&gt;
&lt;p&gt;Gatsbyのスターターを使っています。&lt;a href=&quot;https://github.com/alxshelepenok/gatsby-starter-lumen&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Leum&lt;/a&gt;というスターターです。今回はテーマではなくスターターを使いました。既にゴリゴリに改造してます。&lt;/p&gt;
&lt;p&gt;ホスティングはNetlifyで行っています。記事の編集はNetlify CMSで、ドメインはNetlify DNSで管理しています。
Netlifyはall-in-oneさがすごいですね。別でVercelも使っていますが、ブログを作る程度で全部お任せしたい場合はNetlifyの方が好みです。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[最近のお仕事]]></title><description><![CDATA[最近の仕事状況や業務委託の再開などについて。]]></description><link>https://kazekyo.com/posts/20201027-job</link><guid isPermaLink="false">https://kazekyo.com/posts/20201027-job</guid><pubDate>Tue, 27 Oct 2020 05:14:29 GMT</pubDate><content:encoded>&lt;p&gt;ここ半年ほどは、自社開発で1on1のクラウドサービスである&lt;a href=&quot;https://www.diaroup.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;ダイアループ&lt;/a&gt;を作っていました。
ただ、半年やった手応えとしてはそのままスケールさせるのは難しいという印象です。もう少しテコ入れが必要。
なので少し企画を練り直しつつ、来年あたりからまた刷新しようと思っています。&lt;/p&gt;
&lt;p&gt;今はガッツリ開発している感じでもないので、その間に開発業務の仕事を業務委託で受けて来年のサービス開発に投資しようと思い、仕事の受け付けを再開しました。&lt;/p&gt;
&lt;p&gt;この1年でCOVID-19により世界は大きく変わりました。まるで映画のような1年でした。
幸い自分はそれほど影響を受けていませんが、自分も幅を広げないとこれから何が起こるか分かりません。&lt;/p&gt;
&lt;p&gt;なので、これまですべての仕事を知人経由だけで受けてきましたが、今回は関わりのなかった方から仕事を受けようかと思っています。&lt;/p&gt;
&lt;p&gt;そんなこんなで最近流行り（？）の副業系やフリーランス系のサービスに登録して受けてみようかと思ってます。
お客様も私のことなんて知るわけないので単価は安そうですが、そのあたりはやってみてから考えようかなと。&lt;/p&gt;
&lt;p&gt;思っているだけでそれほど進んでないのですが…。11月はちゃんと動きます。
初めての方とやる案件は正直ドキドキしますね。でも楽しみです。&lt;/p&gt;
&lt;p&gt;受けられるかは分かりませんが、&lt;a href=&quot;/pages/job&quot;&gt;このブログからも仕事の相談は受けられるようにした&lt;/a&gt;ので、ご興味のある方はお声がけくださいませ。
1度お仕事をご一緒させていただいたことのある会社様は、すみませんが今回はご遠慮ください。&lt;/p&gt;
&lt;p&gt;「パパー！」と「ねんね」が言えるようになった子供の成長を見ながら、自分も頑張ろうと思う10月でした。子供の声量でかい。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[React.Suspenseの力を最大化する、GraphQLの次世代ディレクティブ @ defer と @ stream]]></title><description><![CDATA[ReactのSuspense、素敵ですよね。この記事ではGraphQLのワーキンググループで仕様の策定が進んでいる@deferと@streamについて解説します。@deferと@streamはReactのSuspenseの力を引き出す、注目すべきディレクティブです。]]></description><link>https://kazekyo.com/posts/20201005</link><guid isPermaLink="false">https://kazekyo.com/posts/20201005</guid><pubDate>Mon, 05 Oct 2020 08:47:00 GMT</pubDate><content:encoded>&lt;p&gt;ReactのSuspense、素敵ですよね。
この記事ではGraphQLのワーキンググループで仕様の策定が進んでいる&lt;code class=&quot;language-text&quot;&gt;@defer&lt;/code&gt;と&lt;code class=&quot;language-text&quot;&gt;@stream&lt;/code&gt;について解説します。&lt;code class=&quot;language-text&quot;&gt;@defer&lt;/code&gt;と&lt;code class=&quot;language-text&quot;&gt;@stream&lt;/code&gt;はReactのSuspenseの力を引き出す、注目すべきディレクティブです。&lt;/p&gt;
&lt;p&gt;なお、これらのディレクティブの仕様はワーキンググループでほぼ合意できていますが確定した仕様ではないことに注意してください。&lt;/p&gt;
&lt;h1 id=&quot;suspenseは最高だ！&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#suspense%E3%81%AF%E6%9C%80%E9%AB%98%E3%81%A0%EF%BC%81&quot; aria-label=&quot;suspenseは最高だ！ permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Suspenseは最高だ！&lt;/h1&gt;
&lt;p&gt;あるブログの記事の一覧ページを例に見てみます。
記事一覧ページを表示するためのGraphQLのクエリは次のようになります。&lt;sup id=&quot;fnref-1&quot;&gt;&lt;a href=&quot;#fn-1&quot; class=&quot;footnote-ref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;graphql&quot;&gt;&lt;pre class=&quot;language-graphql&quot;&gt;&lt;code class=&quot;language-graphql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;query&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  articles&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;page&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# 記事一覧（1ページ目）&lt;/span&gt;
    title &lt;span class=&quot;token comment&quot;&gt;# タイトル&lt;/span&gt;
    body &lt;span class=&quot;token comment&quot;&gt;# 文章（先頭の少しだけ表示）&lt;/span&gt;
    author &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      name &lt;span class=&quot;token comment&quot;&gt;# 著者名&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    like &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      count &lt;span class=&quot;token comment&quot;&gt;# イイねの数&lt;/span&gt;
      avatars &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        image &lt;span class=&quot;token comment&quot;&gt;# イイねしている人のアバター画像&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;このクエリでは、まず記事の一覧（&lt;code class=&quot;language-text&quot;&gt;articles&lt;/code&gt;）があり、記事の中に著者（&lt;code class=&quot;language-text&quot;&gt;author&lt;/code&gt;）とイイね（&lt;code class=&quot;language-text&quot;&gt;like&lt;/code&gt;）があります。&lt;/p&gt;
&lt;p&gt;ここでReactのSuspenseの典型的な利用例について簡単におさらいしておきましょう。
Suspenseを使うと、ページを一気に表示するのではなく、&lt;strong&gt;データが揃ったコンポーネントから段階的に表示していく処理&lt;/strong&gt;を簡単に書くことができます。&lt;/p&gt;
&lt;p&gt;Webサイトでページの一部に&lt;a href=&quot;https://material-ui.com/ja/components/skeleton/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;スケルトン&lt;/a&gt;（プレースホルダ）が表示されるのを見たことがありませんか？
データがまだ表示できない時に、代わりに表示されるアレです。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/710f84c43bd83668dd6924610b257820/20201005-image.gif&quot; alt=&quot;スケルトン.gif&quot;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://material-ui.com/components/skeleton/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://material-ui.com/components/skeleton/&lt;/a&gt; より&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Suspenseはこういったコンポーネントの「待っている」状態の管理や表示の切り替えを担当します。
Suspenseを使うと、バックエンドに要求したデータ（またはクライアントのキャッシュにあるデータ）が揃ったコンポーネントから順に、スケルトンのUIから実際の表示に切り替えていく処理を簡単に書くことができます。&lt;/p&gt;
&lt;p&gt;今回のブログの記事一覧の例で言うと、まず記事のタイトルなどのデータを最初に表示してから、その他のデータ（著者やイイね）を後で表示できるようになります。
著者やイイねのデータがバックエンドから返ってきていない間は、そこだけスケルトンのUIを表示して読み込み中であることを表現できます。
仮にバックエンドで”イイねの数”の集計が遅くても、それが足を引っ張りページ全体のレンダリングまで遅くなるという事態を避けられます。&lt;/p&gt;
&lt;h1 id=&quot;ダメだキャッシュがない&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E3%83%80%E3%83%A1%E3%81%A0%E3%82%AD%E3%83%A3%E3%83%83%E3%82%B7%E3%83%A5%E3%81%8C%E3%81%AA%E3%81%84&quot; aria-label=&quot;ダメだキャッシュがない permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;ダメだキャッシュがない&lt;/h1&gt;
&lt;p&gt;ということで、Suspenseのおかげでユーザーを待たせずに重要なデータからいち早く表示できるようになりました。手軽にユーザー体験を向上させることができます！&lt;/p&gt;
&lt;p&gt;…ちょっと待ってください。
仮に記事一覧ページで使用できるデータのキャッシュが、ローカルに全くなかったとしましょう。&lt;sup id=&quot;fnref-2&quot;&gt;&lt;a href=&quot;#fn-2&quot; class=&quot;footnote-ref&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;
私達がページの一部分でも表示するためには、データをバックエンドから取得しなければなりません。
しかし、GraphQLのクエリをバックエンドに送ると、&lt;strong&gt;全てのデータが1回のレスポンスで返ってきます&lt;/strong&gt;。そうなると部分的にページを更新しようにも、一気に全て表示する以外できません。&lt;/p&gt;
&lt;p&gt;これは困りました。キャッシュなしで一部のデータだけを先に表示するためには、バックエンドからのレスポンスを分割する必要があるようです。&lt;/p&gt;
&lt;h1 id=&quot;restやgraphql（旧）の問題点&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#rest%E3%82%84graphql%EF%BC%88%E6%97%A7%EF%BC%89%E3%81%AE%E5%95%8F%E9%A1%8C%E7%82%B9&quot; aria-label=&quot;restやgraphql（旧）の問題点 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;RESTやGraphQL（旧）の問題点&lt;/h1&gt;
&lt;p&gt;仕方がないので、クエリを3つに分割しましょう。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;graphql&quot;&gt;&lt;pre class=&quot;language-graphql&quot;&gt;&lt;code class=&quot;language-graphql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;query&lt;/span&gt; Articles &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# 記事一覧&lt;/span&gt;
  articles&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;page&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    id
    title
    body
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;query&lt;/span&gt; ArticleAuthor &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# 記事の著者&lt;/span&gt;
  article&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    author &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      name
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;query&lt;/span&gt; ArticleLike &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# 記事のイイね&lt;/span&gt;
  article&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    like &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      count
      avatars &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        image
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;Articles&lt;/code&gt;クエリで記事一覧を取ってから、記事の&lt;code class=&quot;language-text&quot;&gt;id&lt;/code&gt;を&lt;code class=&quot;language-text&quot;&gt;ArticleAuthor&lt;/code&gt;クエリ（著者）と&lt;code class=&quot;language-text&quot;&gt;ArticleLike&lt;/code&gt;クエリ（イイね）に渡して、データを取得するようにしました。
このクエリをそれぞれバックエンドに送れば、複数のレスポンスが返ってくるのでSuspenseを使ってデータが揃った順に表示できます。
この例ではGraphQLを使用していますが、基本的にRESTでも同じように一覧を取るAPIを叩いてから詳細なデータを取得します。&lt;/p&gt;
&lt;p&gt;さて、これは動作しますが良い解決策というわけではありません。&lt;/p&gt;
&lt;p&gt;まず、明らかに最終的なページを表示するまでの速度が低下しています。
&lt;code class=&quot;language-text&quot;&gt;ArticleAuthor&lt;/code&gt;クエリと&lt;code class=&quot;language-text&quot;&gt;ArticleLike&lt;/code&gt;クエリは引数に記事の&lt;code class=&quot;language-text&quot;&gt;id&lt;/code&gt;を取るため、&lt;strong&gt;&lt;code class=&quot;language-text&quot;&gt;Articles&lt;/code&gt;クエリの結果が戻ってきてからでないとリクエストを送ることができません。&lt;/strong&gt; &lt;sup id=&quot;fnref-3&quot;&gt;&lt;a href=&quot;#fn-3&quot; class=&quot;footnote-ref&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;
そのため、&lt;code class=&quot;language-text&quot;&gt;ArticleAuthor&lt;/code&gt;クエリと&lt;code class=&quot;language-text&quot;&gt;ArticleLike&lt;/code&gt;クエリに含まれるデータを使用するコンポーネントのレンダリングは、1つのクエリで全て取得していた時よりも明らかに遅くなります。&lt;/p&gt;
&lt;p&gt;また、複数のクエリに分割したことでリクエスト数が増えたためモバイルなどのバッテリーの消費が激しくなりますし、&lt;code class=&quot;language-text&quot;&gt;Articles&lt;/code&gt;クエリでDBから読み込んだ記事データに&lt;code class=&quot;language-text&quot;&gt;ArticleAuthor&lt;/code&gt;クエリと&lt;code class=&quot;language-text&quot;&gt;ArticleLike&lt;/code&gt;クエリが再度アクセスすることになるためバックエンドの負荷も無駄に高くなります。&lt;/p&gt;
&lt;p&gt;そもそも、このようなデメリットを避けるためにRESTではなくGraphQLを使用して1回でデータを取得できるようにしたという歴史的背景があるので、これでは時代が逆行しています。
このような事態を避けるためにできることは何でしょうか。&lt;/p&gt;
&lt;h1 id=&quot;データに優先度をつけてインクリメンタル配信する&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E3%83%87%E3%83%BC%E3%82%BF%E3%81%AB%E5%84%AA%E5%85%88%E5%BA%A6%E3%82%92%E3%81%A4%E3%81%91%E3%81%A6%E3%82%A4%E3%83%B3%E3%82%AF%E3%83%AA%E3%83%A1%E3%83%B3%E3%82%BF%E3%83%AB%E9%85%8D%E4%BF%A1%E3%81%99%E3%82%8B&quot; aria-label=&quot;データに優先度をつけてインクリメンタル配信する permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;データに優先度をつけてインクリメンタル配信する&lt;/h1&gt;
&lt;p&gt;Suspenseを使うと、ページを一気に表示するのではなく段階的に表示できます。しかしReactはページ全体をSuspenseで囲むのではなく、&lt;a href=&quot;https://ja.reactjs.org/docs/concurrent-mode-patterns.html#wrap-lazy-features-in-suspense&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;遅延表示して良いものだけをSuspenseで囲む&lt;/a&gt;ように言っています。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;ある機能が次の画面に必須なものではない場合は、それを &amp;#x3C;Suspense&gt; でラップして、遅延読み込みさせてください。これにより、残りのコンテンツを可能な限り素早く表示できるようになります。逆に、あるコンポーネントがないと次の画面の表示自体が無価値であるという場合（例えば我々の例の &amp;#x3C;ProfileDetails&gt;）、そのコンポーネントを &amp;#x3C;Suspense&gt; で囲んではいけません。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;ここから得られる洞察は、ページ内のコンポーネントには&lt;strong&gt;優先順位&lt;/strong&gt;があるということです。重要なコンポーネントはすぐに表示させて、そうでないコンポーネントは遅延して表示させるべきです。
この考え方はデータの配信にも言えます。データはコンポーネントで表示するためにあるので、ページ内のコンポーネントに優先順位がつくということは、ページ内で使用されるデータにも優先順位がつきます。&lt;/p&gt;
&lt;p&gt;最初のクエリに話を戻しましょう。
結論から言うと、我々の願いは、GraphQLのクエリを&lt;strong&gt;1回でリクエスト&lt;/strong&gt;して&lt;strong&gt;優先順位に従って複数のレスポンスを返してほしい&lt;/strong&gt;ということなのです。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;graphql&quot;&gt;&lt;pre class=&quot;language-graphql&quot;&gt;&lt;code class=&quot;language-graphql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;query&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  articles&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;page&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    title &lt;span class=&quot;token comment&quot;&gt;# 優先する&lt;/span&gt;
    body &lt;span class=&quot;token comment&quot;&gt;# 優先する&lt;/span&gt;
    author &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# 優先しない&lt;/span&gt;
      name
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    like &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# 優先しない&lt;/span&gt;
      count
      avatars &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        image
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;優先順位としては、まずは&lt;code class=&quot;language-text&quot;&gt;articles&lt;/code&gt;の&lt;strong&gt;1個目の記事&lt;/strong&gt;の&lt;code class=&quot;language-text&quot;&gt;title&lt;/code&gt;と&lt;code class=&quot;language-text&quot;&gt;body&lt;/code&gt;がレスポンスとして返ってきて、その後に他の部分が遅延して返ってくると理想的ですね。&lt;/p&gt;
&lt;p&gt;このように優先順位をつけて部分的にAPIからデータを返すことをインクリメンタル配信（Incremental Delivery、Incremental Data Delivery）と言います。&lt;/p&gt;
&lt;h1 id=&quot;インクリメンタル配信を行うdeferとstream&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E3%82%A4%E3%83%B3%E3%82%AF%E3%83%AA%E3%83%A1%E3%83%B3%E3%82%BF%E3%83%AB%E9%85%8D%E4%BF%A1%E3%82%92%E8%A1%8C%E3%81%86defer%E3%81%A8stream&quot; aria-label=&quot;インクリメンタル配信を行うdeferとstream permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;インクリメンタル配信を行う&lt;code class=&quot;language-text&quot;&gt;@defer&lt;/code&gt;と&lt;code class=&quot;language-text&quot;&gt;@stream&lt;/code&gt;&lt;/h1&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;@defer&lt;/code&gt;と&lt;code class=&quot;language-text&quot;&gt;@stream&lt;/code&gt;はクエリ内でインクリメンタル配信を行う箇所を指定するためのディレクティブです。
下記の例ではあくまで説明を簡単にするための&lt;strong&gt;イメージ&lt;/strong&gt;として記述しています。実際のクエリは最後に載せています。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;graphql&quot;&gt;&lt;pre class=&quot;language-graphql&quot;&gt;&lt;code class=&quot;language-graphql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;query&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  articles&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;page&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token directive function&quot;&gt;@stream&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;initialCount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;articlesStream&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    title
    body
    author &lt;span class=&quot;token directive function&quot;&gt;@defer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;articlesAuthorDefer&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      name
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    like &lt;span class=&quot;token directive function&quot;&gt;@defer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;articlesLikeDefer&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      count
      avatars &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        image
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;@stream(initialCount: 1, label: ...)&lt;/code&gt;は、配列のようなデータ構造から1つ（&lt;code class=&quot;language-text&quot;&gt;initialCount&lt;/code&gt;で指定した数）だけはすぐに返してもらい、その他は後で配信してくれれば良いという指定になります。
&lt;code class=&quot;language-text&quot;&gt;@defer(label: ...)&lt;/code&gt;は、そのブロックを最初の応答に含めず、後で配信してくれれば良いという指定になります。&lt;/p&gt;
&lt;p&gt;2つのディレクティブに共通する&lt;code class=&quot;language-text&quot;&gt;label&lt;/code&gt;は、一意の値を指定します。これはGraphQLクライアントが後で配信されたデータの中身を識別するために使用します。&lt;/p&gt;
&lt;p&gt;これらのディレクティブがあれば、クエリが返すレスポンスを分割し、優先順位をつけて返してもらえるようになります。&lt;/p&gt;
&lt;h1 id=&quot;1つのリクエスト、複数のレスポンスの実装&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1%E3%81%A4%E3%81%AE%E3%83%AA%E3%82%AF%E3%82%A8%E3%82%B9%E3%83%88%E3%80%81%E8%A4%87%E6%95%B0%E3%81%AE%E3%83%AC%E3%82%B9%E3%83%9D%E3%83%B3%E3%82%B9%E3%81%AE%E5%AE%9F%E8%A3%85&quot; aria-label=&quot;1つのリクエスト、複数のレスポンスの実装 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1つのリクエスト、複数のレスポンスの実装&lt;/h1&gt;
&lt;p&gt;ところで、インクリメンタル配信はどうやって実装するのでしょうか。1つのリクエストを送って、複数の結果を返すようなことはHTTPで実現できるのでしょうか。&lt;/p&gt;
&lt;p&gt;HTTP1.1では&lt;code class=&quot;language-text&quot;&gt;Transfer-Encoding&lt;/code&gt;というヘッダがあり、&lt;code class=&quot;language-text&quot;&gt;Transfer-Encoding: chunked&lt;/code&gt;を指定することで複数のチャンク（塊）でデータを返すことができます。
これはもともと&lt;code class=&quot;language-text&quot;&gt;Content-Length&lt;/code&gt;で指定するデータの長さ（大きさ）の計測を避けるためのヘッダなのですが（データの大きさが分からなくてもチャンクで分割して返し始めることができます）、GraphQLではチャンクを順次返す仕組みを使ってインクリメンタル配信を実現します。&lt;/p&gt;
&lt;h1 id=&quot;現在の進捗&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E7%8F%BE%E5%9C%A8%E3%81%AE%E9%80%B2%E6%8D%97&quot; aria-label=&quot;現在の進捗 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;現在の進捗&lt;/h1&gt;
&lt;p&gt;GraphQLの仕様を検討するワーキンググループで&lt;code class=&quot;language-text&quot;&gt;@defer&lt;/code&gt;と&lt;code class=&quot;language-text&quot;&gt;@stream&lt;/code&gt;の&lt;a href=&quot;https://github.com/graphql/graphql-spec/blob/master/rfcs/DeferStream.md&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;仕様の策定&lt;/a&gt;が進められています。仕様自体はほぼ合意が取れている状況です。
バックエンドの実装が必要なタイプの仕様なので、リファレンス実装として&lt;a href=&quot;https://github.com/graphql/graphql-js&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;graphql-js&lt;/a&gt;の実装が先行しています。&lt;/p&gt;
&lt;p&gt;ただし、そもそもReactのSuspenseとデータフェッチの組み合わせがいつ実験的機能でなくなるのかも謎なので、早くても普通に使えるようになるのは2021年になってからでしょう。2022年にならないことを祈ってます。&lt;/p&gt;
&lt;h1 id=&quot;おまけ：実際のクエリ&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E3%81%8A%E3%81%BE%E3%81%91%EF%BC%9A%E5%AE%9F%E9%9A%9B%E3%81%AE%E3%82%AF%E3%82%A8%E3%83%AA&quot; aria-label=&quot;おまけ：実際のクエリ permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;おまけ：実際のクエリ&lt;/h1&gt;
&lt;p&gt;最後に、現在ワーキンググループで検討されているRFCに沿って今回使用したクエリを記述します。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;graphql&quot;&gt;&lt;pre class=&quot;language-graphql&quot;&gt;&lt;code class=&quot;language-graphql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;query&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  articles&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;page&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token directive function&quot;&gt;@stream&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;initialCount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;articlesStream&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    title
    body
    &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token fragment function&quot;&gt;ArticlesAuthor&lt;/span&gt; &lt;span class=&quot;token directive function&quot;&gt;@defer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;articlesAuthorDefer&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token fragment function&quot;&gt;ArticlesLike&lt;/span&gt; &lt;span class=&quot;token directive function&quot;&gt;@defer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;articlesLikeDefer&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;fragment&lt;/span&gt; &lt;span class=&quot;token fragment function&quot;&gt;ArticlesAuthor&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;on&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Article&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  author &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    name
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;fragment&lt;/span&gt; &lt;span class=&quot;token fragment function&quot;&gt;ArticlesLike&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;on&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Article&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  like &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    count
    avatars &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      image
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;現在策定されている仕様では、&lt;code class=&quot;language-text&quot;&gt;@defer&lt;/code&gt;はフラグメントにしか使用できません。
ReactとGraphQLを結びつけるRelayではフラグメントとコンポーネントはほぼ1対1です。コンポーネントのレンダリングを遅延させて良いのであれば（Suspenseで囲むなら）、&lt;code class=&quot;language-text&quot;&gt;@defer&lt;/code&gt;をコンポーネントのフラグメントにつけるという流れになると思います。&lt;/p&gt;
&lt;div class=&quot;footnotes&quot;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&quot;fn-1&quot;&gt;
&lt;p&gt;今回のGraphQLのスキーマは説明を簡単にするために通常ではありえないスキーマ設計になっています（&lt;code class=&quot;language-text&quot;&gt;like.count&lt;/code&gt;など）。真似しないでください。&lt;/p&gt;
&lt;a href=&quot;#fnref-1&quot; class=&quot;footnote-backref&quot;&gt;↩&lt;/a&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-2&quot;&gt;
&lt;p&gt;Relay、SWR、React Queryなどを使用することで、キャッシュに存在しているデータを先に表示するコードを簡単に書けます（ただしexperimentalのものがほとんどです）。むしろキャッシュ機構を持つデータフェッチライブラリがないとSuspenseの利用は大変だと思います。&lt;/p&gt;
&lt;a href=&quot;#fnref-2&quot; class=&quot;footnote-backref&quot;&gt;↩&lt;/a&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-3&quot;&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;author&lt;/code&gt;や&lt;code class=&quot;language-text&quot;&gt;like&lt;/code&gt;も&lt;code class=&quot;language-text&quot;&gt;articles&lt;/code&gt;で取ればウォーターフォールが発生しないのではと思ったかもしれません。確かに発生しませんが、DBからのデータ取得に無駄が多いという側面もありますし、現実問題として最初の&lt;code class=&quot;language-text&quot;&gt;articles&lt;/code&gt;を取った時と最後の&lt;code class=&quot;language-text&quot;&gt;articles&lt;/code&gt;を取った時で、記事が削除や追加されていない保証はないですから、&lt;code class=&quot;language-text&quot;&gt;author&lt;/code&gt;や&lt;code class=&quot;language-text&quot;&gt;like&lt;/code&gt;と記事一覧のズレを考慮するようなカオスなクライアント実装になると思います。&lt;/p&gt;
&lt;a href=&quot;#fnref-3&quot; class=&quot;footnote-backref&quot;&gt;↩&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</content:encoded></item><item><title><![CDATA[GraphQLを検討中のあなたに送るQ&A集]]></title><description><![CDATA[世間でGraphQLが流行っているから採用検討しているものの、いろいろ調べたけど正直実感が湧かない…という方もいますよね。
そこでフロントエンドとバックエンドの両方の観点から、いくつかの典型的な疑問とその回答をご用意しました。]]></description><link>https://kazekyo.com/posts/20201004</link><guid isPermaLink="false">https://kazekyo.com/posts/20201004</guid><pubDate>Sat, 03 Oct 2020 15:00:59 GMT</pubDate><content:encoded>&lt;p&gt;世間でGraphQLが流行っているから採用検討しているものの、いろいろ調べたけど正直実感が湧かない…という方もいますよね。
そこでフロントエンドとバックエンドの両方の観点から、いくつかの典型的な疑問とその回答をご用意しました。&lt;/p&gt;
&lt;p&gt;これは私が質問された時に答えている内容をまとめたものですが、何かの参考になれば幸いです。&lt;/p&gt;
&lt;p&gt;既にGraphQLについて何となく知っている、例えば「QueryとMutationなるものがあるらしい」ぐらいは理解していると想定して書いています。
何が書いてあるか分からない項目は読み飛ばしてください。&lt;/p&gt;
&lt;h2 id=&quot;はじめに編&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB%E7%B7%A8&quot; aria-label=&quot;はじめに編 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;はじめに編&lt;/h2&gt;
&lt;h3 id=&quot;graphqlの素晴らしいところは何ですか&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#graphql%E3%81%AE%E7%B4%A0%E6%99%B4%E3%82%89%E3%81%97%E3%81%84%E3%81%A8%E3%81%93%E3%82%8D%E3%81%AF%E4%BD%95%E3%81%A7%E3%81%99%E3%81%8B&quot; aria-label=&quot;graphqlの素晴らしいところは何ですか permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;GraphQLの素晴らしいところは何ですか&lt;/h3&gt;
&lt;p&gt;最も素晴らしいのは、GraphQLクライアントによりフロントエンドのDX（開発時の体験）が圧倒的に良くなる点です。&lt;/p&gt;
&lt;h2 id=&quot;フロントエンド編&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E3%83%95%E3%83%AD%E3%83%B3%E3%83%88%E3%82%A8%E3%83%B3%E3%83%89%E7%B7%A8&quot; aria-label=&quot;フロントエンド編 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;フロントエンド編&lt;/h2&gt;
&lt;h3 id=&quot;フロントエンドにどんなメリットがありますか&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E3%83%95%E3%83%AD%E3%83%B3%E3%83%88%E3%82%A8%E3%83%B3%E3%83%89%E3%81%AB%E3%81%A9%E3%82%93%E3%81%AA%E3%83%A1%E3%83%AA%E3%83%83%E3%83%88%E3%81%8C%E3%81%82%E3%82%8A%E3%81%BE%E3%81%99%E3%81%8B&quot; aria-label=&quot;フロントエンドにどんなメリットがありますか permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;フロントエンドにどんなメリットがありますか&lt;/h3&gt;
&lt;p&gt;GraphQLクライアントであるApolloやRelayが使えることがメリットだと思います。
どのGraphQLクライアントを使用するかによって多少変わりますが、基本的にはデータの取得や更新においてバグりやすい処理が減ります。
DOMはjQueryからReactなどに移行することで大幅に宣言的に書けるようになりましたが、同じ現象がデータの取得や更新でも起こります。宣言的なデータ管理といったところです。どこまで宣言的に書けるかはGraphQLクライアントによります。
また、データに自動的に型が付くので開発しやすいと思います。&lt;/p&gt;
&lt;p&gt;基本的にはコードも減ってバグも減るので、生産性は上がると思います。&lt;/p&gt;
&lt;h3 id=&quot;reduxなどの状態管理のソフトウェアを使う必要がありますか&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#redux%E3%81%AA%E3%81%A9%E3%81%AE%E7%8A%B6%E6%85%8B%E7%AE%A1%E7%90%86%E3%81%AE%E3%82%BD%E3%83%95%E3%83%88%E3%82%A6%E3%82%A7%E3%82%A2%E3%82%92%E4%BD%BF%E3%81%86%E5%BF%85%E8%A6%81%E3%81%8C%E3%81%82%E3%82%8A%E3%81%BE%E3%81%99%E3%81%8B&quot; aria-label=&quot;reduxなどの状態管理のソフトウェアを使う必要がありますか permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Reduxなどの状態管理のソフトウェアを使う必要がありますか&lt;/h3&gt;
&lt;p&gt;必要ありません。ApolloやRelayなどのデータ管理を行うGraphQLクライアントを使う場合は同様の処理をやってくれます。
API経由以外のローカルステートもApolloとRelayともに管理できます。&lt;/p&gt;
&lt;p&gt;とは言え、独自の観点からReduxと組み合わせて使っている人もいますので、組み合わせられないわけではないです。&lt;/p&gt;
&lt;h3 id=&quot;apolloとrelayどちらが良いですか&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#apollo%E3%81%A8relay%E3%81%A9%E3%81%A1%E3%82%89%E3%81%8C%E8%89%AF%E3%81%84%E3%81%A7%E3%81%99%E3%81%8B&quot; aria-label=&quot;apolloとrelayどちらが良いですか permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;ApolloとRelayどちらが良いですか&lt;/h3&gt;
&lt;p&gt;一長一短です。
すごく単純化すると、あなたがReactを使っていて、TypeScriptをstrictで使うような機械に極力任せたい（そのために努力できる）派閥の人なら&lt;a href=&quot;https://relay.dev/en/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Relay&lt;/a&gt;が良いかもしれません。
そうでないなら&lt;a href=&quot;https://www.apollographql.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Apollo&lt;/a&gt;一択。&lt;/p&gt;
&lt;h3 id=&quot;他に有名なgraphqlクライアントはありますか&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E4%BB%96%E3%81%AB%E6%9C%89%E5%90%8D%E3%81%AAgraphql%E3%82%AF%E3%83%A9%E3%82%A4%E3%82%A2%E3%83%B3%E3%83%88%E3%81%AF%E3%81%82%E3%82%8A%E3%81%BE%E3%81%99%E3%81%8B&quot; aria-label=&quot;他に有名なgraphqlクライアントはありますか permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;他に有名なGraphQLクライアントはありますか&lt;/h3&gt;
&lt;p&gt;Apollo、Relayに次いで有名なのは&lt;a href=&quot;https://formidable.com/open-source/urql/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;urql&lt;/a&gt;です。説明できるほど詳しくありませんが、ApolloやRelayよりシンプルに使えると思います。
Apolloのハマりやすさ、Relayの厳しすぎる要件が嫌な人はurqlを使っている印象です。&lt;/p&gt;
&lt;h3 id=&quot;typescriptと相性は良いですか&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#typescript%E3%81%A8%E7%9B%B8%E6%80%A7%E3%81%AF%E8%89%AF%E3%81%84%E3%81%A7%E3%81%99%E3%81%8B&quot; aria-label=&quot;typescriptと相性は良いですか permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;TypeScriptと相性は良いですか&lt;/h3&gt;
&lt;p&gt;はい。型がある点がGraphQLメリットの1つなので、間違いなく便利に使えます。
他に型のあるAltJS（例えばReasonML）とも相性は悪くありません。そちらの方が最高と言ってる人もいるくらいですが、入門者にとっては情報量や品質の観点からTypeScript以外を試すのは現実的ではないと思います。&lt;/p&gt;
&lt;h3 id=&quot;一回で色々なオブジェクトを取得するのでuiの表示が遅くなりませんか&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E4%B8%80%E5%9B%9E%E3%81%A7%E8%89%B2%E3%80%85%E3%81%AA%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%82%92%E5%8F%96%E5%BE%97%E3%81%99%E3%82%8B%E3%81%AE%E3%81%A7ui%E3%81%AE%E8%A1%A8%E7%A4%BA%E3%81%8C%E9%81%85%E3%81%8F%E3%81%AA%E3%82%8A%E3%81%BE%E3%81%9B%E3%82%93%E3%81%8B&quot; aria-label=&quot;一回で色々なオブジェクトを取得するのでuiの表示が遅くなりませんか permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;一回で色々なオブジェクトを取得するのでUIの表示が遅くなりませんか&lt;/h3&gt;
&lt;p&gt;たくさんの情報をクエリで一気に要求しているとAPIのレスポンスは当然遅くなります。バックエンドに魔法の技術はありません。
困ったら次のことを検討してください。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;UIとして&lt;a href=&quot;https://material-ui.com/ja/components/skeleton/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;スケルトン&lt;/a&gt;を表示してください&lt;/li&gt;
&lt;li&gt;可能な限りGraphQLクライアントのキャッシュを活用してください&lt;/li&gt;
&lt;li&gt;クエリを分割してください。可能な限りネストを浅くしてください&lt;/li&gt;
&lt;li&gt;そのうち&lt;a href=&quot;https://www.apollographql.com/docs/resources/graphql-glossary/#deferred-query&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;@defer&lt;/a&gt;が使えるようになると思うので検討してください&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;現実問題として、バックエンドでのSQLのチューニングはRESTよりやり辛くなってます。パフォーマンスによるユーザー体験の影響はREST時代よりもクライアントの責任が増えていると理解してください。&lt;/p&gt;
&lt;h2 id=&quot;バックエンド編&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E3%83%90%E3%83%83%E3%82%AF%E3%82%A8%E3%83%B3%E3%83%89%E7%B7%A8&quot; aria-label=&quot;バックエンド編 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;バックエンド編&lt;/h2&gt;
&lt;h3 id=&quot;バックエンドにどんなメリットがありますか&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E3%83%90%E3%83%83%E3%82%AF%E3%82%A8%E3%83%B3%E3%83%89%E3%81%AB%E3%81%A9%E3%82%93%E3%81%AA%E3%83%A1%E3%83%AA%E3%83%83%E3%83%88%E3%81%8C%E3%81%82%E3%82%8A%E3%81%BE%E3%81%99%E3%81%8B&quot; aria-label=&quot;バックエンドにどんなメリットがありますか permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;バックエンドにどんなメリットがありますか&lt;/h3&gt;
&lt;p&gt;エンドポイントを増やさなくて良い、マイクロサービスでBFFを組みやすい、などが代表例だと思います。
ただ、これは個人的な意見ですが、APIの要件はフロントエンドの都合に合わせる時代だからGraphQLを選択している程度にしか考えていません。
もちろん幸せになったバックエンドエンジニアの話も調べたら出てくると思うので、そちらをご覧いただければと思います。&lt;/p&gt;
&lt;h3 id=&quot;apiに型をつけるという観点で、graphqlとxxx（好きなソフトウェア）と違いはありますか&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#api%E3%81%AB%E5%9E%8B%E3%82%92%E3%81%A4%E3%81%91%E3%82%8B%E3%81%A8%E3%81%84%E3%81%86%E8%A6%B3%E7%82%B9%E3%81%A7%E3%80%81graphql%E3%81%A8xxx%EF%BC%88%E5%A5%BD%E3%81%8D%E3%81%AA%E3%82%BD%E3%83%95%E3%83%88%E3%82%A6%E3%82%A7%E3%82%A2%EF%BC%89%E3%81%A8%E9%81%95%E3%81%84%E3%81%AF%E3%81%82%E3%82%8A%E3%81%BE%E3%81%99%E3%81%8B&quot; aria-label=&quot;apiに型をつけるという観点で、graphqlとxxx（好きなソフトウェア）と違いはありますか permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;APIに型をつけるという観点で、GraphQLとXXX（好きなソフトウェア）と違いはありますか&lt;/h3&gt;
&lt;p&gt;たぶんないです。RESTのAPIに型をつけるソフトウェアを使って、GraphQLの型付けの性質と同じようなものを作ることは可能です。
これは型付けだけでなく、あらゆるGraphQLの特徴は論理的には別の技術で個別に代替できると思います。
GraphQLの良いところは、いくつかの特徴が組み合わせで固定されていて、エコシステムがその特徴を前提としたツールを構築できる点にあると思います。
FacebookのオレオレAPI仕様に名前を付けて標準化したことで、みんなが乗っかれたことが良かったと思ってます。&lt;/p&gt;
&lt;h3 id=&quot;スキーマファーストとコードファーストのどちらが良いですか&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E3%82%B9%E3%82%AD%E3%83%BC%E3%83%9E%E3%83%95%E3%82%A1%E3%83%BC%E3%82%B9%E3%83%88%E3%81%A8%E3%82%B3%E3%83%BC%E3%83%89%E3%83%95%E3%82%A1%E3%83%BC%E3%82%B9%E3%83%88%E3%81%AE%E3%81%A9%E3%81%A1%E3%82%89%E3%81%8C%E8%89%AF%E3%81%84%E3%81%A7%E3%81%99%E3%81%8B&quot; aria-label=&quot;スキーマファーストとコードファーストのどちらが良いですか permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;スキーマファーストとコードファーストのどちらが良いですか&lt;/h3&gt;
&lt;p&gt;コードファーストだと思いますが、断言できません。
GraphQLの世界で強い影響力を持つApolloはスキーマファーストを主導してきました。一方でApolloと利害関係のないフレームワークでは近年コードファーストをメインに採用する傾向が強まっています。正直、今が一番どうなるのか分からない時期です。&lt;/p&gt;
&lt;p&gt;私はコードファースト派です。実運用でスキーマだけ先に書いてもメリットを得るのは難しい、バックエンドでスキーマから生成した型を使ってモデルを書くのは最悪のDXだ、などと思ってますが、たくさんの論争があるのでここで言い切るのは難しいです。&lt;/p&gt;
&lt;h3 id=&quot;cdnでキャッシュが効かないと聞きましたが本当ですか&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#cdn%E3%81%A7%E3%82%AD%E3%83%A3%E3%83%83%E3%82%B7%E3%83%A5%E3%81%8C%E5%8A%B9%E3%81%8B%E3%81%AA%E3%81%84%E3%81%A8%E8%81%9E%E3%81%8D%E3%81%BE%E3%81%97%E3%81%9F%E3%81%8C%E6%9C%AC%E5%BD%93%E3%81%A7%E3%81%99%E3%81%8B&quot; aria-label=&quot;cdnでキャッシュが効かないと聞きましたが本当ですか permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;CDNでキャッシュが効かないと聞きましたが本当ですか&lt;/h3&gt;
&lt;p&gt;はい。これも多くの論争がありますが、要約すると「諦めて他のことをした方が生産的」になります。&lt;/p&gt;
&lt;p&gt;まず、ブログのような大部分が静的なページを運営している場合はCDNが変わらず必要だと思いますが、それはそれで（APIのレスポンスではなく）サーバーサイドでレンダリングしたページをCDNのキャッシュにのせてもらって、GraphQLとは関係なくよしなにして下さい。
以下はそれ以外のケースについて話します。&lt;/p&gt;
&lt;p&gt;この問題はGraphQLではPOSTメソッドでやり取りを行うので、（GETを対象にした一般的な）CDNではキャッシュが効かせにくいという話なのですが、一応仕様的にはGraphQLはGETを使用できます。また、GraphQL対応のCDNも存在しています。&lt;/p&gt;
&lt;p&gt;ただ、いくつかの点で考えると、やはりCDNでキャッシュを効かせられるように頑張る労力にリターンが見合うかはちょっと…。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;GraphQLではクライアントでキャッシュを効かせる世界観なので必要性は薄め&lt;/li&gt;
&lt;li&gt;RESTfulのようにリソースとリクエストが対応するわけではないので、仮に同じオブジェクトを取得していても様々なリクエスト（query）の形があってキャッシュが効きにくい&lt;/li&gt;
&lt;li&gt;CDNでキャッシュするとクライアントと2重でキャッシュのレイヤが生まれるため、複雑怪奇なバグに見舞われる可能性がある&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;もちろん、画像や動画は別のエンドポイントで引き続きCDNのキャッシュを使用してください。
APIのレスポンスを高速にしたい場合はCDNではなくサーバーに到達して以降の何かのキャッシュを頑張った方が吉。&lt;/p&gt;
&lt;h3 id=&quot;n1は発生しますか&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#n1%E3%81%AF%E7%99%BA%E7%94%9F%E3%81%97%E3%81%BE%E3%81%99%E3%81%8B&quot; aria-label=&quot;n1は発生しますか permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;N+1は発生しますか&lt;/h3&gt;
&lt;p&gt;何も考えないと発生します。
GraphQLではコンテキスト（queryの形）によって関連するテーブルを読み込んだり読み込まなかったりするので、我々が事前にJOINしたSQLを組み立てるのは容易ではありません。
RESTの決まったレスポンスのために手動でゴリゴリにチューニングしたSQLより速くなることはないと理解しつつ、&lt;a href=&quot;https://github.com/graphql/dataloader&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;dataloader&lt;/a&gt;のような仕組みを使ってください。&lt;/p&gt;
&lt;h3 id=&quot;1発であらゆるオブジェクトが取得できるって負荷やばくないですか&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1%E7%99%BA%E3%81%A7%E3%81%82%E3%82%89%E3%82%86%E3%82%8B%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%81%8C%E5%8F%96%E5%BE%97%E3%81%A7%E3%81%8D%E3%82%8B%E3%81%A3%E3%81%A6%E8%B2%A0%E8%8D%B7%E3%82%84%E3%81%B0%E3%81%8F%E3%81%AA%E3%81%84%E3%81%A7%E3%81%99%E3%81%8B&quot; aria-label=&quot;1発であらゆるオブジェクトが取得できるって負荷やばくないですか permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1発であらゆるオブジェクトが取得できるって負荷やばくないですか&lt;/h3&gt;
&lt;p&gt;やばいので、&lt;a href=&quot;https://graphql-ruby.org/queries/complexity_and_depth.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;複雑度と深さ&lt;/a&gt;で計算してクエリを制限します。
GraphQLは1発であらゆるオブジェクトを取得するためのものと勘違いされてる節がありますが、1発でアプリケーションに必要な全てのデータを取得するのはさすがに非現実的です。
適切にクエリは分けてください。まずはページ単位で分ける感じで。&lt;/p&gt;
&lt;h3 id=&quot;エラーが発生したのに200を返すのはおかしいと思います&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E3%82%A8%E3%83%A9%E3%83%BC%E3%81%8C%E7%99%BA%E7%94%9F%E3%81%97%E3%81%9F%E3%81%AE%E3%81%AB200%E3%82%92%E8%BF%94%E3%81%99%E3%81%AE%E3%81%AF%E3%81%8A%E3%81%8B%E3%81%97%E3%81%84%E3%81%A8%E6%80%9D%E3%81%84%E3%81%BE%E3%81%99&quot; aria-label=&quot;エラーが発生したのに200を返すのはおかしいと思います permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;エラーが発生したのに200を返すのはおかしいと思います&lt;/h3&gt;
&lt;p&gt;GraphQLではレスポンスコードは常に200で、一般的にはエラーはレスポンスボディ（のJSON）の&lt;code class=&quot;language-text&quot;&gt;errors&lt;/code&gt;に入れて返します。&lt;/p&gt;
&lt;p&gt;まずこの仕組みの利点から説明すると、利点はエラーが一部発生しても一部のデータは返すことができる点です。
GraphQLでは1回のリクエストで多くのデータを取得できますが、一部でエラーが発生しても返ってきているデータがあるならそれをレンダリングできます。
例えばBFFでまとめているマイクロサービスで一部がダウンしている状況でも、動いている部分だけ返すことができますし、逆にこれができないとクエリは丸ごと失敗するのでフロントエンドから見ると全体がダウンしているように見えてしまいます。&lt;/p&gt;
&lt;p&gt;とは言え、個人的にはサーバーサイドの実装を手抜きしてMutationのバリデーションエラー以外は普通に401とか500で全体を失敗させて返すこともありますし、それでハンドリングできます。&lt;/p&gt;
&lt;h3 id=&quot;どうやって認証しますか&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E3%81%A9%E3%81%86%E3%82%84%E3%81%A3%E3%81%A6%E8%AA%8D%E8%A8%BC%E3%81%97%E3%81%BE%E3%81%99%E3%81%8B&quot; aria-label=&quot;どうやって認証しますか permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;どうやって認証しますか&lt;/h3&gt;
&lt;p&gt;特にRESTと変わりません（異なる方法でも実装できますが）。
ヘッダの&lt;code class=&quot;language-text&quot;&gt;Authorization&lt;/code&gt;に認証情報をのせて、それをいつも通りに処理してください。&lt;/p&gt;
&lt;h3 id=&quot;restのapiと共存できますか&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#rest%E3%81%AEapi%E3%81%A8%E5%85%B1%E5%AD%98%E3%81%A7%E3%81%8D%E3%81%BE%E3%81%99%E3%81%8B&quot; aria-label=&quot;restのapiと共存できますか permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;RESTのAPIと共存できますか&lt;/h3&gt;
&lt;p&gt;はい。色々やり方があると思います。&lt;/p&gt;
&lt;p&gt;BFFでAPIをまとめてGraphQLだけにすることもできますし、GraphQLとRESTの両方のAPIを持つサーバーにクライアントからアクセスすることもできます。
GraphQLクライアントからRESTのAPIが叩けるかは使用しているGraphQLクライアントによりますが、使えないなら普通にRESTのAPIが叩けるライブラリ（axiosとか）を使ってください。&lt;/p&gt;
&lt;p&gt;既存の外部システムと連携する部分などは移行が難しいでしょうし、GraphQLだけで作れないこともあるでしょう。
例えば、私はフロントエンドで画像を処理するライブラリを使用していて、その中にサーバーに画像をアップロードする機能があるのですが、GraphQLに対応していないのでその部分のバックエンドは普通にRESTで書いてます。
ただし、当該処理がDBと関係ないので許されるという側面が多大にあり、通常はGraphQLで書けるだけ書くのが無難です。&lt;/p&gt;
&lt;h3 id=&quot;relay-server-specificationに準拠する必要がありますか&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#relay-server-specification%E3%81%AB%E6%BA%96%E6%8B%A0%E3%81%99%E3%82%8B%E5%BF%85%E8%A6%81%E3%81%8C%E3%81%82%E3%82%8A%E3%81%BE%E3%81%99%E3%81%8B&quot; aria-label=&quot;relay server specificationに準拠する必要がありますか permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Relay Server Specificationに準拠する必要がありますか&lt;/h3&gt;
&lt;p&gt;ケースバイケースです。
&lt;code class=&quot;language-text&quot;&gt;Node&lt;/code&gt;インターフェース、Global Object Identification、Connectionベースのページネーションのことを指して、Relay Server Specificationと言います。
これらはフロントエンドでコンポーネント指向のアプリケーション（Reactとか）を書いてある場合は提供した方が良いと個人的には思います。&lt;a href=&quot;/posts/2020-08-27-an-introduction-to-relay-1&quot;&gt;コンポーネントにデータをバインドしやすくなります&lt;/a&gt;。Relayを使う場合は必須です。
あとで導入するのはかなりの痛みが伴いますので、最初によく検討してください。&lt;/p&gt;
&lt;h3 id=&quot;nodeidフィールドは神apiではないですか&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#nodeid%E3%83%95%E3%82%A3%E3%83%BC%E3%83%AB%E3%83%89%E3%81%AF%E7%A5%9Eapi%E3%81%A7%E3%81%AF%E3%81%AA%E3%81%84%E3%81%A7%E3%81%99%E3%81%8B&quot; aria-label=&quot;nodeidフィールドは神apiではないですか permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;code class=&quot;language-text&quot;&gt;node(id)&lt;/code&gt;フィールドは神APIではないですか&lt;/h3&gt;
&lt;p&gt;その通りです。あらゆるものと結合している、悪い意味での神API（ゴッドAPI）です。
&lt;code class=&quot;language-text&quot;&gt;node(id)&lt;/code&gt;フィールドはユニークな&lt;code class=&quot;language-text&quot;&gt;id&lt;/code&gt;であらゆる単一のオブジェクトを取得できるメソッド的なものです。
あらゆるオブジェクトが取得できるので、公開されているあらゆる型（モデル、テーブル）の情報が引けます。なのでバックエンドのコード上では&lt;code class=&quot;language-text&quot;&gt;node(id)&lt;/code&gt;のリゾルバからは様々な依存関係ができます。幸いなことに単方向の依存関係ですが。
RESTではありえない考え方ですが、ここにREST的な意味での正しい答えはありません。まごうことなき神APIが目の前にあるだけです。&lt;/p&gt;
&lt;h3 id=&quot;connectionベースのページネーションを調べましたが意味が分かりませんでした&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#connection%E3%83%99%E3%83%BC%E3%82%B9%E3%81%AE%E3%83%9A%E3%83%BC%E3%82%B8%E3%83%8D%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%82%92%E8%AA%BF%E3%81%B9%E3%81%BE%E3%81%97%E3%81%9F%E3%81%8C%E6%84%8F%E5%91%B3%E3%81%8C%E5%88%86%E3%81%8B%E3%82%8A%E3%81%BE%E3%81%9B%E3%82%93%E3%81%A7%E3%81%97%E3%81%9F&quot; aria-label=&quot;connectionベースのページネーションを調べましたが意味が分かりませんでした permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Connectionベースのページネーションを調べましたが意味が分かりませんでした&lt;/h3&gt;
&lt;p&gt;たぶんConnectionとEdgeという謎のオブジェクトがページネーションを複雑化させてるように見えるんですよね。分かります。&lt;a href=&quot;/posts/2020-08-28-an-introduction-to-relay-2&quot;&gt;こちら&lt;/a&gt;をご覧ください。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[最高のGraphQLクライアントを求めて Apollo vs Relay]]></title><description><![CDATA[GraphQLの代表的なクライアントと言えば、ApolloとRelayの2つです。
どちらも素晴らしいソフトウェアですが、この2つは根本的に異なる背景で作られています。
今回は、みなさんがそれぞれ最高のクライアントを選ぶことができるようにお手伝いしたいと思います。]]></description><link>https://kazekyo.com/posts/20200925</link><guid isPermaLink="false">https://kazekyo.com/posts/20200925</guid><pubDate>Fri, 25 Sep 2020 08:42:00 GMT</pubDate><content:encoded>&lt;p&gt;GraphQLの代表的なクライアントと言えば、&lt;a href=&quot;https://www.apollographql.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Apollo&lt;/a&gt;と&lt;a href=&quot;https://relay.dev/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Relay&lt;/a&gt;の2つです。
どちらも素晴らしいソフトウェアですが、この2つは根本的に異なる背景で作られています。&lt;/p&gt;
&lt;p&gt;今回は、みなさんがそれぞれ最高のクライアントを選ぶことができるようにお手伝いしたいと思います。&lt;/p&gt;
&lt;p&gt;なお、本来Relayと比較するのはApolloのOSSの中でもApollo Clientとなりますが、ここではApolloと記載しています。RelayもRelay ClassicではなくRelay Modernの方です。
&lt;a href=&quot;https://formidable.com/open-source/urql/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;URQL&lt;/a&gt;については今日は触れません。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;2022年6月追記: &lt;a href=&quot;https://kazekyo.com/posts/20220605-apollo-client-vs-relay-2&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;続編記事&lt;/a&gt;を書きました。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;企業的な背景&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E4%BC%81%E6%A5%AD%E7%9A%84%E3%81%AA%E8%83%8C%E6%99%AF&quot; aria-label=&quot;企業的な背景 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;企業的な背景&lt;/h2&gt;
&lt;p&gt;ApolloとRelayは2つともGraphQLクライアントではありますが、背景に存在する2つの企業の目的は完全に異なります。&lt;/p&gt;
&lt;h3 id=&quot;apollo&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#apollo&quot; aria-label=&quot;apollo permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Apollo&lt;/h3&gt;
&lt;p&gt;Apolloは元々&lt;a href=&quot;https://www.meteor.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Meteor&lt;/a&gt;の開発元が作成したGraphQLを扱うためのソフトウェア群です。Apolloの急成長により、創業者はMeteorを売却してApolloに集中するようになりました。&lt;/p&gt;
&lt;p&gt;Apolloのビジネスモデルは、Apollo Clientを中心にしたOSSを使用している企業に対して、いくつかのサポートを有料提供することです。例えばコンサルティング、可視化のシステム、Datadogなどの他システムとの連携などを有料提供しています。
つまり、この企業はApollo Clientを使用するユーザーが増えれば増えるほど儲かります。&lt;/p&gt;
&lt;p&gt;そのためApolloの最優先事項は、RESTを使用しているユーザーにGraphQLを使わせることです。現状、GraphQLを使っているユーザーよりRESTを使っているユーザの方が多いので、そのユーザーのシームレスな移行を狙っています。&lt;/p&gt;
&lt;p&gt;このためにApolloは血の滲む作業を継続しています。&lt;/p&gt;
&lt;p&gt;まず、Apolloは多様な環境で動作します。仮にバックエンドをGraphQLにしたところで、WebアプリやモバイルアプリがAPIに接続できなければ意味がありません。
このため、Apollo ClientはWeb/iOS/Androidのクロスプラットフォームで使うことができます。
バックエンド用のライブラリを含む多くのOSSも提供しており、GraphQL界隈で使用される様々なものがApolloとそのコミュニティの努力によって支えられています。&lt;sup id=&quot;fnref-1&quot;&gt;&lt;a href=&quot;#fn-1&quot; class=&quot;footnote-ref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;また、ユーザーが簡単に使い始められるように常に努力しています。GraphQLに入門するだけで若干大変なので、クライアントを使う時にセットアップや覚えるものがなるべく少なくなるように配慮されています。
Apolloは入門コストを下げることにかなり意識的です。後述しますが、Relayより明らかに始めやすいです。&lt;/p&gt;
&lt;p&gt;ドキュメントを手厚くしたり、カンファレンスを開いたり、コミュニティの意見に耳を傾ける努力もしています。
もちろん、これらは超多様な範囲をカバーする必要があるため完璧とはいきませんが、確実に努力は行われています。&lt;/p&gt;
&lt;h3 id=&quot;relay&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#relay&quot; aria-label=&quot;relay permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Relay&lt;/h3&gt;
&lt;p&gt;対してRelayはFacebookが開発しています。
FacebookはRelayを使って利益を回収しませんが、わざわざ人件費をかけて専門のコアチームを構成しています。
この理由はReactと同じです。つまり、facebook.comのような超大規模なコードベースを素早く安全に開発するためには既存の手法では難しく、より良いソフトウェアを求めているのです。
バグなく新しい機能を高速にFacebookのプロダクト群に投入すること、それがFacebookに利益をもたらします。&lt;/p&gt;
&lt;p&gt;そのためRelayの最優先事項は、壊れないフロントエンドを最速で作ることです。GraphQLは常にその手段です。
彼らは自分達が使用している環境以外には興味が薄いため、念頭に置かれている環境はReactとReactNativeだけです。逆にReactとの連携を限界まで高めることに非常に熱心です。
また、サーバーサイドの仕様についてもGraphQLのベストプラクティス（という名のRelayのルール）に準拠していることを求めますし、フロントエンドの開発中に独自のコンパイラを使う必要もあります。
しかし、こうしたセットアップの面倒さを乗り越えたら、以降は爆速で壊れないフロントエンド開発に邁進できます。ここまでの多種多様な”仮定”により、システム側が使用できる知識が増えているため、人間が手動で設定したり気をつけるべき点が減ります。
かなりの部分を型安全で短く書け、かつ高いパフォーマンスを出すことができます。&lt;/p&gt;
&lt;h2 id=&quot;比較表&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E6%AF%94%E8%BC%83%E8%A1%A8&quot; aria-label=&quot;比較表 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;比較表&lt;/h2&gt;
&lt;p&gt;Apollo（Apollo Client）とRelayを簡単にまとめてみます。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;項目&lt;/th&gt;
&lt;th&gt;Apollo Client&lt;/th&gt;
&lt;th&gt;Relay&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ユーザー数&lt;/td&gt;
&lt;td&gt;◎&lt;/td&gt;
&lt;td&gt;△&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;入門しやすさ&lt;/td&gt;
&lt;td&gt;◎&lt;/td&gt;
&lt;td&gt;△&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;公式の対象環境&lt;/td&gt;
&lt;td&gt;React, ReactNative&lt;/td&gt;
&lt;td&gt;React, ReactNative, iOS, Androidなど&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;スキーマ&lt;/td&gt;
&lt;td&gt;高い柔軟性&lt;/td&gt;
&lt;td&gt;ベストプラクティス準拠&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;堅牢性&lt;/td&gt;
&lt;td&gt;○&lt;/td&gt;
&lt;td&gt;◎&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;キャッシュ&lt;/td&gt;
&lt;td&gt;○&lt;/td&gt;
&lt;td&gt;◎&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;パフォーマンス&lt;/td&gt;
&lt;td&gt;○&lt;/td&gt;
&lt;td&gt;◎&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ドキュメント&lt;/td&gt;
&lt;td&gt;○&lt;/td&gt;
&lt;td&gt;△&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;関連OSS&lt;/td&gt;
&lt;td&gt;◎&lt;/td&gt;
&lt;td&gt;△&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;ユーザー数&lt;/strong&gt;はApolloの方が多いです。コミュニティとしてもApolloの方が繁栄していると思います。
最近ReactのSuspenseなどとの絡みでRelayユーザーも増えつつあるようですが、絶対数ではまだまだApolloの方が上です。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;入門のしやすさ&lt;/strong&gt;はそもそもGraphQLの導入で覚えることが多いですが、Apolloの方がしやすいです。Relayは学習曲線がかなり急で、正しく動くまで混乱しがちです。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;公式の対象環境&lt;/strong&gt;は圧倒的にApolloが優れています。将来的にRelayで公式に他の環境が充実していく可能性もあるかもしれませんが、今のところそのような兆候はありません。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;スキーマ&lt;/strong&gt;はApolloの方が柔軟ですが、柔軟であるが故にコンピューターに与えられる知識が少なかったり、チームで統一されなかったりと、どちらが良いかは意見が分かれます。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;堅牢性&lt;/strong&gt;はRelayの方が優れています。コンパイラがかなりの部分をチェックできる上に、コンポーネントの密結合（データの依存）を強制的になくすなど、Relayの方が堅牢性を重視しています。また、多くを自動化するために（手動で書かないので）ミスが減ります。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;キャッシュ&lt;/strong&gt;は、機能はApolloの方が、安全性はRelayの方が優れています。
Apolloは手動でキャッシュを弄る部分が多くハマりがちですが、Relayはほとんど自動化されていて安全に使えます。一方Apolloは手動で弄るために色々な機能が用意されており、それを使ってキャッシュデータに対してハック（例えば文字列を加工するとか）ができます。多機能ですが、こうした手動ハックは基本的に必要ない（むしろ怪しい挙動をもたらす）と個人的には思うのでApolloの点数を下げました。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;パフォーマンス&lt;/strong&gt;はRelayの方が優れています。Relayではデータの変更時にクエリに関連する全てのコンポーネントを更新するのではなく、データに変更があったコンポーネントだけを自動的に更新します。Apolloも同様のことができますが手動です。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;ドキュメント&lt;/strong&gt;はApolloの方が優れています。Apolloのドキュメントも完全とは言い難いですが、Relayよりはかなり良いと思います。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;関連OSS&lt;/strong&gt;はApolloの方が優れています。前提として、通常必要なものはどちらにもあります。Relayはオールインワンで、Apolloは組み合わせて使います。
ただ、単純にユーザー数が違うのでエコシステムの繁栄度は異なりますし、Apolloの方が関連OSSは多いです。Apolloが小さく小分けしてくれてるので、むしろRelayユーザーも足りないものはApolloのOSSに全力で乗っかってます。&lt;/p&gt;
&lt;h2 id=&quot;まとめ&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E3%81%BE%E3%81%A8%E3%82%81&quot; aria-label=&quot;まとめ permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;まとめ&lt;/h2&gt;
&lt;p&gt;過去、Relayではsubscriptionやローカルステートの管理などができませんでしたが、現在は問題なくできますので、重要な機能的な違いはほとんどないと思います。&lt;/p&gt;
&lt;p&gt;イメージとしてはTypeScript使うときにstrictを絶対つけたい人はRelayと相性が良いと思いますし、自由度を求めるならApolloを使う方が良いのではないでしょうか。
Apolloは自由の国、ちょっと手動、RESTより安全だけどまだデンジャラスな部分は残ります。
Relayは規律ガチガチの国、ほぼ自動、デンジャラスな部分は抹殺！という世界観です。&lt;/p&gt;
&lt;p&gt;私はRelayを多く使っていますが、今回は可能な限りフェアに書いてあるつもりです。Apolloに配慮するあまり、逆にApolloの採点が甘口になっている疑惑はありますが…（比較表に△ないですし）。
もしこれを読んでRelayも学んでみようかなと思う方がいれば嬉しく思います。ディープに学んでみたい方は&lt;a href=&quot;/posts/2020-08-27-an-introduction-to-relay-1&quot;&gt;こちら&lt;/a&gt;の記事を見てみてください。&lt;/p&gt;
&lt;h2 id=&quot;おまけ&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E3%81%8A%E3%81%BE%E3%81%91&quot; aria-label=&quot;おまけ permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;おまけ&lt;/h2&gt;
&lt;p&gt;最後にRelay信者としてフェアな比較から離れ、Relay最高！と言っている記事としてQuoraでの事例を紹介します。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.quora.com/q/quoraengineering/Choosing-Quora-s-GraphQL-client&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;QuoraのGraphQLクライアントの選択&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;かなり充実した良い記事だと思いますが、Relay信者寄りなのは否めない。&lt;/p&gt;
&lt;div class=&quot;footnotes&quot;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&quot;fn-1&quot;&gt;
&lt;p&gt;実際、私はRelayを使っていてもバックエンドはapollo-serverを使ってますし、subscriptionにはsubscriptions-transport-wsを使ってます。&lt;/p&gt;
&lt;a href=&quot;#fnref-1&quot; class=&quot;footnote-backref&quot;&gt;↩&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Nodeインターフェースの魔法]]></title><description><![CDATA[Facebookが作ったGraphQLクライアントのRelayを理解する、Relay入門シリーズ。最後となるこの記事では、Nodeについて学びます。]]></description><link>https://kazekyo.com/posts/2020-08-30-an-introduction-to-relay-4</link><guid isPermaLink="false">https://kazekyo.com/posts/2020-08-30-an-introduction-to-relay-4</guid><pubDate>Sat, 29 Aug 2020 15:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Relay入門シリーズも最後となりました。
この翻訳記事ではGraphQLのベストプラクティスの1つであるGlobal Object Identificationと&lt;code class=&quot;language-text&quot;&gt;Node&lt;/code&gt;インターフェースの解説になります。
Relayにとっては、fragmentと同じくあらゆる便利機能を生み出す源泉となっている仕組みです。ぜひご覧ください！&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://dev.to/zth/the-magic-of-the-node-interface-4le1&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;（原文）&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Relay入門シリーズ（全4記事）&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;/posts/2020-08-27-an-introduction-to-relay-1&quot;&gt;Relay：あなたのために泥臭い仕事をしてくれるGraphQLクライアント&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;/posts/2020-08-28-an-introduction-to-relay-2&quot;&gt;GraphQLでのConnectionベースのページネーション&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;/posts/2020-08-29-an-introduction-to-relay-3&quot;&gt;Relayを使って最小限の努力でページネーションをする&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nodeインターフェースの魔法（本記事）&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;この連載は、&lt;a href=&quot;https://github.com/zth&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Gabriel Nordeborn&lt;/a&gt;と&lt;a href=&quot;https://github.com/sgrove&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Sean Grove&lt;/a&gt;が執筆しています。GabrielはスウェーデンのITコンサルタント会社&lt;a href=&quot;https://arizon.se/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Arizon&lt;/a&gt;のフロントエンド開発者でありパートナーでもあり、長い間Relayを利用してきました。SeanはサードパーティのAPIをGraphQLで統一する&lt;a href=&quot;https://www.onegraph.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;OneGraph.com&lt;/a&gt;の共同創業者です。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;GraphQLの公式サイトは最近、&lt;a href=&quot;https://graphql.org/learn/global-object-identification/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;ベストプラクティス集に「Global Object Identification（グローバルなオブジェクトの識別）」というセクションを追加&lt;/a&gt;しました。この記事では、Global Object Identificationとは何か、なぜそれが有用なのか、そしてそれによってどのような種類の開発者体験が可能になるのかについて掘り下げていきます。&lt;/p&gt;
&lt;h2 id=&quot;global-object-identificationとは何か？&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#global-object-identification%E3%81%A8%E3%81%AF%E4%BD%95%E3%81%8B%EF%BC%9F&quot; aria-label=&quot;global object identificationとは何か？ permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Global Object Identificationとは何か？&lt;/h2&gt;
&lt;p&gt;GraphQLのGlobal Object Identificationには2つの意味があります。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;グラフ内の個々のオブジェクトは、型を超えてグローバルに一意なIDで識別できます。つまり、異なる型であっても、2つのオブジェクトが同じIDを持つことはできません。&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;id&lt;/code&gt;を持つ&lt;em&gt;任意の1つのオブジェクト&lt;/em&gt;はその&lt;code class=&quot;language-text&quot;&gt;id&lt;/code&gt;だけでqueryできます。これは後ほど詳しく説明する&lt;code class=&quot;language-text&quot;&gt;Node&lt;/code&gt;インターフェースを介して行われます。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;これにより、ライブラリは開発者が自分で実装したり管理したりしなければならないいくつかのことを以下のように安全に自動化することができます。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;すべてのオブジェクトが一意のIDを持っているので、GraphQLフレームワークは任意のオブジェクトのキャッシュを自動的に更新することができます。&lt;/li&gt;
&lt;li&gt;オブジェクトの&lt;em&gt;新しい&lt;/em&gt;フィールドや&lt;em&gt;更新された&lt;/em&gt;フィールドを取得する必要がある場合、私たちが知る必要があるのは、そのオブジェクトのGraphQLの型とその&lt;code class=&quot;language-text&quot;&gt;id&lt;/code&gt;だけです。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;この2つのポイントがなぜ素晴らしい開発者体験を可能にするのかについて、詳しく説明していきましょう。&lt;/p&gt;
&lt;h2 id=&quot;そもそもnodeインターフェースとは何か？&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E3%81%9D%E3%82%82%E3%81%9D%E3%82%82node%E3%82%A4%E3%83%B3%E3%82%BF%E3%83%BC%E3%83%95%E3%82%A7%E3%83%BC%E3%82%B9%E3%81%A8%E3%81%AF%E4%BD%95%E3%81%8B%EF%BC%9F&quot; aria-label=&quot;そもそもnodeインターフェースとは何か？ permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;そもそも&lt;code class=&quot;language-text&quot;&gt;Node&lt;/code&gt;インターフェースとは何か？&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;Node&lt;/code&gt;インターフェースを実装しているスキーマでは、&lt;code class=&quot;language-text&quot;&gt;node&lt;/code&gt;と呼ばれる&lt;code class=&quot;language-text&quot;&gt;Query&lt;/code&gt;オブジェクトのトップレベルのフィールドを見ることができます。&lt;code class=&quot;language-text&quot;&gt;node&lt;/code&gt;は&lt;code class=&quot;language-text&quot;&gt;id&lt;/code&gt;という単一の引数を取り、単一のフィールド&lt;code class=&quot;language-text&quot;&gt;id&lt;/code&gt;を返します。&lt;/p&gt;
&lt;p&gt;「何だそれ」と思いましたか？
初めて見ると確かに少し不思議な感じがしますよね！&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;graphql&quot;&gt;&lt;pre class=&quot;language-graphql&quot;&gt;&lt;code class=&quot;language-graphql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;query&lt;/span&gt; NodeInterfaceExample1 &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  gitHub &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    node&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;MDQ6VXNlcjM1Mjk2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      id &lt;span class=&quot;token comment&quot;&gt;# 予想通り、これは&quot;MDQ6VXNlcjM1Mjk2&quot;になります&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;node&lt;/code&gt;は&lt;em&gt;インターフェース&lt;/em&gt;なので、スキーマ内のすべてのGraphQLオブジェクトが実装しており、&lt;code class=&quot;language-text&quot;&gt;id&lt;/code&gt;を介してオブジェクトを取得することができます。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;graphql&quot;&gt;&lt;pre class=&quot;language-graphql&quot;&gt;&lt;code class=&quot;language-graphql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;query&lt;/span&gt; NodeInterfaceExample2 &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  gitHub &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    node&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;MDQ6VXNlcjM1Mjk2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      id
      &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;on&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;GitHubUser&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        login &lt;span class=&quot;token comment&quot;&gt;# &quot;sgrove&quot;&lt;/span&gt;
        followers &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          totalCount &lt;span class=&quot;token comment&quot;&gt;# 9000人以上!!!!&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;sup id=&quot;fnref-1&quot;&gt;&lt;a href=&quot;#fn-1&quot; class=&quot;footnote-ref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;h2 id=&quot;なぜそれが役に立つのか？&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E3%81%AA%E3%81%9C%E3%81%9D%E3%82%8C%E3%81%8C%E5%BD%B9%E3%81%AB%E7%AB%8B%E3%81%A4%E3%81%AE%E3%81%8B%EF%BC%9F&quot; aria-label=&quot;なぜそれが役に立つのか？ permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;なぜそれが役に立つのか？&lt;/h2&gt;
&lt;p&gt;これが意味するのは、あるオブジェクトの&lt;em&gt;id&lt;/em&gt;と&lt;em&gt;GraphQLの型&lt;/em&gt;を知っていれば、私たちは&lt;em&gt;いつでも&lt;/em&gt;オブジェクトを取得する方法が分かっているということです。この代替案としては次のようなものができるかもしれません。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;graphql&quot;&gt;&lt;pre class=&quot;language-graphql&quot;&gt;&lt;code class=&quot;language-graphql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;query&lt;/span&gt; WithoutInterfaceExample &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  gitHub &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    user&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;login&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;sgrove&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      login
      followers &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        totalCount
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;しかしこのqueryには2つの課題があります。これらの課題は開発者にとっては余分な作業が多くなる、そつまり何かを間違えてしまう機会が増えることを意味しています。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;上記の方法で&lt;code class=&quot;language-text&quot;&gt;GitHubUser&lt;/code&gt;を見つけようとしても、&lt;code class=&quot;language-text&quot;&gt;id&lt;/code&gt;という引数は受け付けずに&lt;code class=&quot;language-text&quot;&gt;login&lt;/code&gt;という引数のみを受け付けます。つまり、たとえば&lt;code class=&quot;language-text&quot;&gt;ownerId&lt;/code&gt;の値を既に持っていたとしても、&lt;code class=&quot;language-text&quot;&gt;login&lt;/code&gt;の値を追加で知る必要があるということです。あらゆる種類のオブジェクトで異なるキーを使って探すことになる可能性があるため、ますます面倒なことになってしまいます。&lt;/li&gt;
&lt;li&gt;仮に&lt;code class=&quot;language-text&quot;&gt;login&lt;/code&gt;の値を&lt;em&gt;持っていた&lt;/em&gt;としても、追加で必要なフィールドがあった時に、私たちはネストされた項目（この場合は&lt;code class=&quot;language-text&quot;&gt;gitHub.user&lt;/code&gt;）を使って複雑になる可能性のあるqueryを構築しなければなりません&lt;sup id=&quot;fnref-2&quot;&gt;&lt;a href=&quot;#fn-2&quot; class=&quot;footnote-ref&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;結局のところ、これらは両方とも暗黙的に分かっていることです。私たちはそれらを開発者として知っており、頭の中にその知識を持っています。それはつまり、コンピューターとツールはその知識を持っていないということです。&lt;/p&gt;
&lt;p&gt;しかし、もし私たちのツールが&lt;code class=&quot;language-text&quot;&gt;id&lt;/code&gt;を与えたオブジェクトを探す方法を知っていれば、それは素晴らしい方法で私たちを助けてくれるでしょう。&lt;/p&gt;
&lt;p&gt;これはバックエンドの負荷を軽減することにもなります。queryの中で深くネストされているノードを再取得する必要がある場合は常に、バックエンドは対象のノードにたどり着くために、対象のノードより上のすべての階層を解決（データを取得）しなければなりません。しかし、&lt;code class=&quot;language-text&quot;&gt;id&lt;/code&gt;を取得して単一のノードを直接解決することができれば、そのような負荷はなくなります。&lt;/p&gt;
&lt;h2 id=&quot;なぜグローバルに一意なidなのか？-整数で十分では？&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E3%81%AA%E3%81%9C%E3%82%B0%E3%83%AD%E3%83%BC%E3%83%90%E3%83%AB%E3%81%AB%E4%B8%80%E6%84%8F%E3%81%AAid%E3%81%AA%E3%81%AE%E3%81%8B%EF%BC%9F-%E6%95%B4%E6%95%B0%E3%81%A7%E5%8D%81%E5%88%86%E3%81%A7%E3%81%AF%EF%BC%9F&quot; aria-label=&quot;なぜグローバルに一意なidなのか？ 整数で十分では？ permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;なぜグローバルに一意なIDなのか？ 整数で十分では？&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;Node&lt;/code&gt;インターフェースを使えば、&lt;em&gt;どんな&lt;/em&gt;オブジェクトでも&lt;code class=&quot;language-text&quot;&gt;id&lt;/code&gt;で検索できるようになります。いいですね！ ただ、これはAPI全体の中で2つのオブジェクトが同じidを持つことができないことを暗示しています。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;余談：この仕組みは最初は難しいと思うかもしれませんが、どんなスキーマでも簡単にこれを行う方法があるとすぐに分かるようになります。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;id&lt;/code&gt;フィールド（&lt;code class=&quot;language-text&quot;&gt;User.id&lt;/code&gt;や&lt;code class=&quot;language-text&quot;&gt;Post.id&lt;/code&gt;など）を持つすべてのGraphQLオブジェクトは、&lt;em&gt;他のGraphQLオブジェクトが持っていない&lt;/em&gt;一意のIDを持たなければなりません。次のqueryを例に考えてみましょう。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;graphql&quot;&gt;&lt;pre class=&quot;language-graphql&quot;&gt;&lt;code class=&quot;language-graphql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;query&lt;/span&gt; FirstUserAndFirstPost &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  user &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    id
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  post &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    id
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;多くのデータベースではインクリメントする整数をidとして使用するのが一般的なので、次のようなレスポンスを期待するかもしれません。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;data&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;user&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;post&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;しかし&lt;code class=&quot;language-text&quot;&gt;Node&lt;/code&gt;インターフェースと組み合わせるとこれはうまくいきません。例えば、投稿（post）のidを持っていて、いくつかの追加フィールド（&lt;code class=&quot;language-text&quot;&gt;title&lt;/code&gt;など）を取得したいとします。その場合、次のようなqueryを実行できるかと思います。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;graphql&quot;&gt;&lt;pre class=&quot;language-graphql&quot;&gt;&lt;code class=&quot;language-graphql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;query&lt;/span&gt; NodeInterfaceExample3 &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  node&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    id
    &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;on&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Post&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      title
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;しかし、&lt;code class=&quot;language-text&quot;&gt;1&lt;/code&gt;の&lt;code class=&quot;language-text&quot;&gt;id&lt;/code&gt;で探すことができるオブジェクトが&lt;em&gt;2つも&lt;/em&gt;存在しています！ 戻り値のオブジェクトが&lt;code class=&quot;language-text&quot;&gt;User&lt;/code&gt;になるのか&lt;code class=&quot;language-text&quot;&gt;Post&lt;/code&gt;になるのかは今のところ分かりません。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;がっかりしないでください！ 簡単な解決策があります！&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;内部的には&lt;/em&gt;整数をIDに使い続けつつ、グローバルに一意なIDを持つにはどうすればいいのでしょうか？ GitHubで使われているIDの例を見てみましょう（APIは美しく、Relayとの互換性もあります！）。&lt;code class=&quot;language-text&quot;&gt;node(id: &amp;quot;MDQ6VXNlcjM1Mjk2&amp;quot;)&lt;/code&gt;というqueryを使ってアクセスできます。ここで使われているIDを見てみましょう。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;&amp;gt; atob(&amp;quot;MDQ6VXNlcjM1Mjk2&amp;quot;)
&amp;quot;04:User35296&amp;quot;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;なんと、整数が入ってますね&lt;sup id=&quot;fnref-3&quot;&gt;&lt;a href=&quot;#fn-3&quot; class=&quot;footnote-ref&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;。&lt;/p&gt;
&lt;p&gt;基本的には&lt;code class=&quot;language-text&quot;&gt;nodeId&lt;/code&gt;を構築するの手法は以下のようになります。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;`&lt;span class=&quot;token function&quot;&gt;base64Encode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;$&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;apiVersion&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;$&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;GraphQLObjectTypeName&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;$&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;ObjectId&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;よってAPIバージョン1の&lt;code class=&quot;language-text&quot;&gt;post&lt;/code&gt;と&lt;code class=&quot;language-text&quot;&gt;user&lt;/code&gt;については、次のようになると期待します。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;&amp;quot;MDE6UG9zdDE=&amp;quot; # &amp;quot;01:Post1&amp;quot; にデコード
&amp;quot;MDE6VXNlcjE=&amp;quot; # &amp;quot;01:User1&amp;quot; にデコード&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;競合があった前の&lt;code class=&quot;language-text&quot;&gt;node&lt;/code&gt;の例に戻って、新しいIDを使用すると次のようになります。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;graphql&quot;&gt;&lt;pre class=&quot;language-graphql&quot;&gt;&lt;code class=&quot;language-graphql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;query&lt;/span&gt; NodeInterfaceExample4 &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  node&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;MDE6UG9zdDE=&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    id
    &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;on&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Post&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      title
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;これで&lt;code class=&quot;language-text&quot;&gt;node&lt;/code&gt;のリゾルバは&lt;code class=&quot;language-text&quot;&gt;id&lt;/code&gt;をbase64でデコードし、&lt;code class=&quot;language-text&quot;&gt;id&lt;/code&gt;が&lt;code class=&quot;language-text&quot;&gt;1&lt;/code&gt;の&lt;code class=&quot;language-text&quot;&gt;Post&lt;/code&gt;オブジェクトを探していることが分かるため、正しいオブジェクトを返すことができるようになりました。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;グローバルidにAPIのバージョンをprefixとしてつけるのは、必要になった時に既存のグローバルidを壊さずに後でエンコード方法を変更できるようにするためです。これは実質的には何もしなくても獲得できるちょっとした柔軟性です。
&lt;sup id=&quot;fnref-4&quot;&gt;&lt;a href=&quot;#fn-4&quot; class=&quot;footnote-ref&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;もしまだピンときてない場合（特に、なぜ&lt;code class=&quot;language-text&quot;&gt;id&lt;/code&gt;がそのオブジェクトのデータベース上のID以外のものになるのか理解できない場合）、GraphQLの型の&lt;code class=&quot;language-text&quot;&gt;id&lt;/code&gt;フィールドを次のように考えると良いかもしれません。つまりGraphQLの型のidを見るときは、実際にはデータベースのテーブルではなくグラフのノードを見ているということです。このように考えると、GraphQL型のidをグローバルに一意にすることはより意味のあることです&lt;sup id=&quot;fnref-5&quot;&gt;&lt;a href=&quot;#fn-5&quot; class=&quot;footnote-ref&quot;&gt;5&lt;/a&gt;&lt;/sup&gt;。&lt;/p&gt;
&lt;h2 id=&quot;これは何を可能にするのか？&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E3%81%93%E3%82%8C%E3%81%AF%E4%BD%95%E3%82%92%E5%8F%AF%E8%83%BD%E3%81%AB%E3%81%99%E3%82%8B%E3%81%AE%E3%81%8B%EF%BC%9F&quot; aria-label=&quot;これは何を可能にするのか？ permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;これは何を可能にするのか？&lt;/h2&gt;
&lt;p&gt;最初に見たように、ほとんどのスキーマでは、トップレベルの&lt;code class=&quot;language-text&quot;&gt;node&lt;/code&gt;フィールドを使用していなくても、ほとんどのものは単一のアイテムを解決する（探す）&lt;em&gt;何らか&lt;/em&gt;の方法をすでに持っています。ではなぜ&lt;code class=&quot;language-text&quot;&gt;node&lt;/code&gt;フィールドが重要なのでしょうか？ それは単に解決をより便利にするためでしょうか？&lt;/p&gt;
&lt;p&gt;もちろんそれも役立ちます。しかし、真の力は標準化から生まれます。&lt;/p&gt;
&lt;p&gt;考えてみれば、スキーマに&lt;code class=&quot;language-text&quot;&gt;postById(id: ID!)&lt;/code&gt;というトップレベルのフィールドがあって、それが実際に&lt;code class=&quot;language-text&quot;&gt;id&lt;/code&gt;で&lt;code class=&quot;language-text&quot;&gt;Post&lt;/code&gt;を解決していたとしても、スキーマの作成者以外にはそのフィールドが実際に何をしているのかを知る方法はありません。&lt;code class=&quot;language-text&quot;&gt;postById&lt;/code&gt;の意味を解析して、&lt;em&gt;かつ&lt;/em&gt;、実際に「単一の&lt;code class=&quot;language-text&quot;&gt;Post&lt;/code&gt;を与えられた&lt;code class=&quot;language-text&quot;&gt;id&lt;/code&gt;で探す」という意味であることを推論しなければ、外部ツールやフレームワークはその特定のフィールドが何をするのかを安全に仮定することができません。1つの&lt;code class=&quot;language-text&quot;&gt;Post&lt;/code&gt;を解決するフィールドは、次のようにいくつでも持つことができます。&lt;code class=&quot;language-text&quot;&gt;postByDatabaseId(id: ID!): Post&lt;/code&gt;や&lt;code class=&quot;language-text&quot;&gt;mostLikedPost(id: ID!): Post&lt;/code&gt;、&lt;code class=&quot;language-text&quot;&gt;latestPost(id: ID!): Post&lt;/code&gt;などなど。 こうなると外部ツールが単一の&lt;code class=&quot;language-text&quot;&gt;Post&lt;/code&gt;を再取得する際にどのフィールドを使用するかを安全に仮定することができなくなります。&lt;/p&gt;
&lt;p&gt;しかし、&lt;code class=&quot;language-text&quot;&gt;Node&lt;/code&gt;インターフェースは&lt;a href=&quot;https://graphql.org/learn/global-object-identification/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;公式のGraphQLのベストプラクティス&lt;/a&gt;であるため、フレームワークやツールはその上に構築することができ、それを実装する人は誰でも恩恵を受けることができます。そしてそれを使って構築できるものには、いくつかとてもクールなものがあります。Relayの具体的な例を見てみましょう。&lt;/p&gt;
&lt;h3 id=&quot;ページネーションqueryの自動生成&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E3%83%9A%E3%83%BC%E3%82%B8%E3%83%8D%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3query%E3%81%AE%E8%87%AA%E5%8B%95%E7%94%9F%E6%88%90&quot; aria-label=&quot;ページネーションqueryの自動生成 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;ページネーションqueryの自動生成&lt;/h3&gt;
&lt;p&gt;Relayの最新版（今はRelayとのみ呼んでいますが、一般的にはRelay Modernとして知られています）では、&lt;code class=&quot;language-text&quot;&gt;Node&lt;/code&gt;インターフェースを介して任意のGraphQLオブジェクトを再取得できるため、データの再取得やページネーションのためのqueryを自動的に生成することができます。これにより、非常に人間工学的な開発体験を実現できます。基本的にRelayでは次のようにします。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;graphql&quot;&gt;&lt;pre class=&quot;language-graphql&quot;&gt;&lt;code class=&quot;language-graphql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;fragment&lt;/span&gt; &lt;span class=&quot;token fragment function&quot;&gt;UserFriendsList_user&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;on&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  id
  friends&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$first&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;after&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$after&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    edges &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      node &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        firstName
        lastName
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;このようなfragmentがあったら、このためのページネーションqueryを下記のように自動的に生成します。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;graphql&quot;&gt;&lt;pre class=&quot;language-graphql&quot;&gt;&lt;code class=&quot;language-graphql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;query&lt;/span&gt; UserFriendsListPaginationQuery&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ID&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$first&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Int&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$after&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  node&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;on&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      id
      friends&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$first&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;after&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$after&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        edges &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          node &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            firstName
            lastName
          &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;…なぜこんなことができるかと言うと、ページネーションで定義している&lt;code class=&quot;language-text&quot;&gt;UserFriendsList_user&lt;/code&gt;fragmentの型である&lt;code class=&quot;language-text&quot;&gt;User&lt;/code&gt;が&lt;code class=&quot;language-text&quot;&gt;Node&lt;/code&gt;インターフェースを実装していることを知っているため、&lt;code class=&quot;language-text&quot;&gt;id&lt;/code&gt;を使って再取得できるということが分かっているからです。&lt;/p&gt;
&lt;p&gt;ページネーションの対象となる&lt;code class=&quot;language-text&quot;&gt;User&lt;/code&gt;が最初のquery内でどこにいたかは関係ありません。&lt;code class=&quot;language-text&quot;&gt;Node&lt;/code&gt;インターフェースがあれば、その&lt;code class=&quot;language-text&quot;&gt;User&lt;/code&gt;の&lt;code class=&quot;language-text&quot;&gt;id&lt;/code&gt;さえあればRelayは任意の&lt;code class=&quot;language-text&quot;&gt;User&lt;/code&gt;を再取得することができます。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/posts/2020-08-29-an-introduction-to-relay-3&quot;&gt;Relayでのページページネーション&lt;/a&gt;についてはこちらの記事に書いているので参照してみてください。&lt;/p&gt;
&lt;h3 id=&quot;典型的な「もっと見る」機能のためのqueryの自動生成&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E5%85%B8%E5%9E%8B%E7%9A%84%E3%81%AA%E3%80%8C%E3%82%82%E3%81%A3%E3%81%A8%E8%A6%8B%E3%82%8B%E3%80%8D%E6%A9%9F%E8%83%BD%E3%81%AE%E3%81%9F%E3%82%81%E3%81%AEquery%E3%81%AE%E8%87%AA%E5%8B%95%E7%94%9F%E6%88%90&quot; aria-label=&quot;典型的な「もっと見る」機能のためのqueryの自動生成 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;典型的な「もっと見る」機能のためのqueryの自動生成&lt;/h3&gt;
&lt;p&gt;同様に、Relayを使えば定番の「もっと見る」機能&lt;sup id=&quot;fnref-6&quot;&gt;&lt;a href=&quot;#fn-6&quot; class=&quot;footnote-ref&quot;&gt;6&lt;/a&gt;&lt;/sup&gt;を簡単に構築することができます。Relayを使えば、以下のようにできます。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;graphql&quot;&gt;&lt;pre class=&quot;language-graphql&quot;&gt;&lt;code class=&quot;language-graphql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;fragment&lt;/span&gt; &lt;span class=&quot;token fragment function&quot;&gt;ProfilePage_user&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;on&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  id
  name
  avatarUrl
  bio &lt;span class=&quot;token directive function&quot;&gt;@include&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$showMore&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;…そしてRelayはこのフラグメントを再取得できるようにするqueryを生成しますが、そこでは&lt;code class=&quot;language-text&quot;&gt;$showMore&lt;/code&gt;を&lt;code class=&quot;language-text&quot;&gt;true&lt;/code&gt;に設定して&lt;code class=&quot;language-text&quot;&gt;bio&lt;/code&gt;をフィールドに含めます。&lt;/p&gt;
&lt;p&gt;「もっと見る」機能の構築は、基本的にはユーザが「もっと見る」ボタンを押したときに&lt;code class=&quot;language-text&quot;&gt;refetch({showMore: true})&lt;/code&gt;を実行するだけのシンプルなものになります。残りの処理はRelayが行います。これは、&lt;code class=&quot;language-text&quot;&gt;User&lt;/code&gt;がquery中のどこにいるかに関係なく、&lt;code class=&quot;language-text&quot;&gt;Node&lt;/code&gt;インターフェースで&lt;code class=&quot;language-text&quot;&gt;User&lt;/code&gt;の&lt;code class=&quot;language-text&quot;&gt;id&lt;/code&gt;を使って再取得する方法を知っているためにできることです。&lt;/p&gt;
&lt;h2 id=&quot;まとめ&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E3%81%BE%E3%81%A8%E3%82%81&quot; aria-label=&quot;まとめ permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;まとめ&lt;/h2&gt;
&lt;p&gt;グローバルに一意なIDと&lt;code class=&quot;language-text&quot;&gt;Node&lt;/code&gt;インターフェースがなぜ良いアイデアなのか、理解していただけていたら幸いです。Relayはこの仕組みをうまく活用していますが、これは公式のGraphQLのベストプラクティスなので他のツールでもその上に構築することができます。&lt;/p&gt;
&lt;div class=&quot;footnotes&quot;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&quot;fn-1&quot;&gt;
&lt;p&gt;訳注：sgroveはこの記事の作者の名前、Sean Groveのハンドルネーム。&lt;/p&gt;
&lt;a href=&quot;#fnref-1&quot; class=&quot;footnote-backref&quot;&gt;↩&lt;/a&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-2&quot;&gt;
&lt;p&gt;訳注：後述されますが、idを使ってオブジェクトを取得できるのであればグラフから必要なところだけオブジェクトの取得ができるため、不要な上位階層を解決する（取得する）必要がなく、基本的にはネストが浅くなります。&lt;/p&gt;
&lt;a href=&quot;#fnref-2&quot; class=&quot;footnote-backref&quot;&gt;↩&lt;/a&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-3&quot;&gt;
&lt;p&gt;訳注：atob関数はBase64をデコードするための関数です。「githubで使うidをBase64でデコードしたら整数が入ってるように見えるんですけど？」ということです。 &lt;a href=&quot;https://developer.mozilla.org/ja/docs/Web/API/WindowBase64/Base64%5C_encoding%5C_and%5C_decoding&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://developer.mozilla.org/ja/docs/Web/API/WindowBase64/Base64\_encoding\_and\_decoding&lt;/a&gt;&lt;/p&gt;
&lt;a href=&quot;#fnref-3&quot; class=&quot;footnote-backref&quot;&gt;↩&lt;/a&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-4&quot;&gt;
&lt;p&gt;標準的な実装であるgraphql-relay-jsはidと型名のみで構成しています &lt;a href=&quot;https://github.com/graphql/graphql-relay-js/blob/3bd6838d41808674b97d024210c11937881b5ffd/src/node/node.js#L90&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://github.com/graphql/graphql-relay-js/blob/3bd6838d41808674b97d024210c11937881b5ffd/src/node/node.js#L90&lt;/a&gt; ただ、idを必ず一意にしなければならない関係上、このidのエンコード方法には自由度が求められます。githubの例のようにAPIバージョンが違えば別の値が返るようにしたい、などです。そのため型名とidで構成すれば絶対に大丈夫とは（世界中のユースケースで）言い切れません。&lt;/p&gt;
&lt;a href=&quot;#fnref-4&quot; class=&quot;footnote-backref&quot;&gt;↩&lt;/a&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-5&quot;&gt;
&lt;p&gt;DB（一般的なRelational Database）の場合はテーブルに名前がついていて、その中でidがユニークになることが求められます。GraphQLでは様々な型が1つのグラフの中に入っているので、その中で1つをIDだけで指し示すためにはグラフ全体で一意のIDが必要になるということです。&lt;/p&gt;
&lt;a href=&quot;#fnref-5&quot; class=&quot;footnote-backref&quot;&gt;↩&lt;/a&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-6&quot;&gt;
&lt;p&gt;ここで言う「もっと見る」とは、ページネーションで残りのアイテムをロードすることを指すのではなく、文章の頭だけ表示されていてクリックすることで全体が読めるようになるようなUIのことを指しています。「詳細を見る」のような感じです。&lt;/p&gt;
&lt;a href=&quot;#fnref-6&quot; class=&quot;footnote-backref&quot;&gt;↩&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Relayを使って最小限の努力でページネーションをする]]></title><description><![CDATA[Facebookが作ったGraphQLクライアントのRelayを理解する、Relay入門シリーズ。3記事目はRelayでのページネーションを学びます。]]></description><link>https://kazekyo.com/posts/2020-08-29-an-introduction-to-relay-3</link><guid isPermaLink="false">https://kazekyo.com/posts/2020-08-29-an-introduction-to-relay-3</guid><pubDate>Fri, 28 Aug 2020 15:00:00 GMT</pubDate><content:encoded>&lt;p&gt;GraphQLのConnectionベースのページネーションは確かに良いアイディアですが、Relayを使うことでこの仕様を最大限活用し、重複するコードを限界まで排除できます。
この翻訳記事ではRelayがどのようにConnectionベースのページネーションを処理するのか、詳細を解説していきます。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://dev.to/zth/pagination-with-minimal-effort-in-relay-gl4&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;（原文）&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Relay入門シリーズ（全4記事）&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;/posts/2020-08-27-an-introduction-to-relay-1&quot;&gt;Relay：あなたのために泥臭い仕事をしてくれるGraphQLクライアント&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;/posts/2020-08-28-an-introduction-to-relay-2&quot;&gt;GraphQLでのConnectionベースのページネーション&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Relayを使って最小限の努力でページネーションをする（本記事）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;/posts/2020-08-30-an-introduction-to-relay-4&quot;&gt;Nodeインターフェースの魔法&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;この連載は、&lt;a href=&quot;https://github.com/zth&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Gabriel Nordeborn&lt;/a&gt;と&lt;a href=&quot;https://github.com/sgrove&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Sean Grove&lt;/a&gt;が執筆しています。GabrielはスウェーデンのITコンサルタント会社&lt;a href=&quot;https://arizon.se/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Arizon&lt;/a&gt;のフロントエンド開発者でありパートナーでもあり、長い間Relayを利用してきました。SeanはサードパーティのAPIをGraphQLで統一する&lt;a href=&quot;https://www.onegraph.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;OneGraph.com&lt;/a&gt;の共同創業者です。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;ページネーション。それは誰もが最終的に辿り着くもので、そして正直に言えば楽しいものではありません。この記事では、いくつかの規則に従えば、Relayでのページネーションは&lt;em&gt;楽しく&lt;/em&gt;ないかもしれませんが&lt;em&gt;簡単&lt;/em&gt;で人間工学に基づいたものであることを紹介します。&lt;/p&gt;
&lt;p&gt;この記事では、フィルタを使用せずに前方へのページネーションのみを行うシンプルなページネーションに焦点を当てます。しかし、Relayでは後方へのページネーションも同様に簡単に行うことができ、フィルターも美しく処理できます。この2つについては&lt;a href=&quot;https://relay.dev/docs/en/experimental/a-guided-tour-of-relay#rendering-list-data-and-pagination&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;こちらをご覧ください&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;また、Relayでのページネーションを最大限に実現するためには、お使いのGraphQLサーバが以下の2つのGraphQLのベストプラクティスに従う必要があります。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://graphql.org/learn/global-object-identification/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;グローバルなオブジェクトの識別（Global object identification）と&lt;code class=&quot;language-text&quot;&gt;Node&lt;/code&gt;インターフェース&lt;/a&gt;。&lt;a href=&quot;/posts/2020-08-30-an-introduction-to-relay-4&quot;&gt;こちらの記事&lt;/a&gt;でも紹介しています。&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://graphql.org/learn/pagination/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Connectionベースのページネーション&lt;/a&gt;。こちらも&lt;a href=&quot;/posts/2020-08-28-an-introduction-to-relay-2&quot;&gt;記事がありますのでぜひ読んでみてください&lt;/a&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;この記事ではまず身近なアプリの例を紹介し、次に必要なページネーションを実装する上での課題を説明します。そして、これらの課題に対するRelayのソリューションを説明します。&lt;/p&gt;
&lt;h2 id=&quot;graphqlクライアントでは、通常どのようにページネーションが行われるのか？&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#graphql%E3%82%AF%E3%83%A9%E3%82%A4%E3%82%A2%E3%83%B3%E3%83%88%E3%81%A7%E3%81%AF%E3%80%81%E9%80%9A%E5%B8%B8%E3%81%A9%E3%81%AE%E3%82%88%E3%81%86%E3%81%AB%E3%83%9A%E3%83%BC%E3%82%B8%E3%83%8D%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%8C%E8%A1%8C%E3%82%8F%E3%82%8C%E3%82%8B%E3%81%AE%E3%81%8B%EF%BC%9F&quot; aria-label=&quot;graphqlクライアントでは、通常どのようにページネーションが行われるのか？ permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;GraphQLクライアントでは、通常どのようにページネーションが行われるのか？&lt;/h2&gt;
&lt;p&gt;通常、ページネーションは次のように構成されます。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;通常は何らかのquery（典型的には現在見ているビューのメインのquery）から、何らかの形式でアイテムの最初のリストを取得します。このqueryには、通常はページネーションしたいリストのアイテムに加えて、他にも多くのものが含まれています。&lt;/li&gt;
&lt;li&gt;リストのアイテムを &lt;em&gt;追加で&lt;/em&gt; 取得できる &lt;em&gt;別の&lt;/em&gt; queryを定義します。&lt;/li&gt;
&lt;li&gt;2で定義した &lt;em&gt;別の&lt;/em&gt; queryと &lt;em&gt;最初の&lt;/em&gt; queryから取得した適切なカーソルを使用して、次のページを取得します。このとき、必要なアイテムの数も指定します。&lt;/li&gt;
&lt;li&gt;そして、 &lt;em&gt;最初の&lt;/em&gt; リストのアイテムに新しいアイテムをマージするコードを書いて、ビューを再レンダリングします&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;それではやってみましょう。ユーザーのプロフィールページのすべてのデータを取得する典型的な例です。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;graphql&quot;&gt;&lt;pre class=&quot;language-graphql&quot;&gt;&lt;code class=&quot;language-graphql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;query&lt;/span&gt; ProfileQuery&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$userLogin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  gitHub &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    user&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;login&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$userLogin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      name
      avatarUrl
      email
      following &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        totalCount
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      followers&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        totalCount
        edges &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          node &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            id
            firstName
            lastName
            avatarUrl
          &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;このqueryは、私たちが気になる2つのデータのグループを取り出します。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;名前やメールアドレスなどのユーザーのプロフィール情報。&lt;/li&gt;
&lt;li&gt;それぞれいくつかのフィールドを持つフォロワーのリスト。まず最初の5人のフォロワーを取得します。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;最初のqueryができたので、次の5人のフォロワーを取得するためにページネーションしてみましょう（人気のあるユーザーがそこにいるかも！）。&lt;/p&gt;
&lt;h2 id=&quot;元のqueryを再利用しようとしても十分ではない&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E5%85%83%E3%81%AEquery%E3%82%92%E5%86%8D%E5%88%A9%E7%94%A8%E3%81%97%E3%82%88%E3%81%86%E3%81%A8%E3%81%97%E3%81%A6%E3%82%82%E5%8D%81%E5%88%86%E3%81%A7%E3%81%AF%E3%81%AA%E3%81%84&quot; aria-label=&quot;元のqueryを再利用しようとしても十分ではない permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;元のqueryを再利用しようとしても十分ではない&lt;/h2&gt;
&lt;p&gt;最初に気づくことは、ページネーションのために定義した最初のqueryを再利用すべきではないということです。私たちには新しいqueryが必要です。なぜなら…&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ユーザーの全てのプロフィール情報を再取得したくありません。ユーザーのプロフィール情報はすでに取得しており、再度取得するのはコストがかかります。&lt;/li&gt;
&lt;li&gt;最初の5人のフォロワーだけから始めて、実際のページネーションに追加のロードを委任したいと思っています。なので最初のqueryにページネーションのための変数を追加することは冗長ですし、不要な複雑さが増してしまうように感じます。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;そこで、新しいqueryを書いてみましょう。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;graphql&quot;&gt;&lt;pre class=&quot;language-graphql&quot;&gt;&lt;code class=&quot;language-graphql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;query&lt;/span&gt; UserProfileFollowersPaginationQuery&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;token variable&quot;&gt;$userLogin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;
  &lt;span class=&quot;token variable&quot;&gt;$first&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Int&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;
  &lt;span class=&quot;token variable&quot;&gt;$after&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; String
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  gitHub &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    user&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;login&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$userLogin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      followers&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$first&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;after&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$after&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        pageInfo &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          hasNextPage
          endCursor
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        edges &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          node &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            id
            firstName
            lastName
            avatarUrl
          &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;これでいきましょう！ これでページネーションに必要なものはすべて揃いました。素晴らしい。しかし、ここで注意すべきことがいくつかあります。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;このqueryを手書きで書く必要があります。&lt;/li&gt;
&lt;li&gt;フォロワーをページングしたいユーザーがわかっていても、変数を使ってその情報&lt;sup id=&quot;fnref-1&quot;&gt;&lt;a href=&quot;#fn-1&quot; class=&quot;footnote-ref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;を再度queryに与える必要があります。これは最初のqueryでどのようにユーザーを選択したかと&lt;em&gt;正確に一致&lt;/em&gt;させる必要があり、間違えないようにする必要があります。&lt;/li&gt;
&lt;li&gt;手動で次のカーソルをqueryに指定してページネーションする必要があります。指定するカーソルはこのビューでは常に最後のカーソルになることが分かっていますが、手動でやる必要があります。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;このような作業を手動でしなければならないのは残念なことです。もしフレームワークがページネーションのqueryを生成して、毎回同じことになるこれらのステップを処理できるとしたらどうでしょうか…？&lt;/p&gt;
&lt;p&gt;そう、&lt;code class=&quot;language-text&quot;&gt;node&lt;/code&gt;インターフェイスとConnectionベースのページネーションを使えば、Relayはやってくれます！&lt;/p&gt;
&lt;h2 id=&quot;relayでのページネーション&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#relay%E3%81%A7%E3%81%AE%E3%83%9A%E3%83%BC%E3%82%B8%E3%83%8D%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3&quot; aria-label=&quot;relayでのページネーション permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Relayでのページネーション&lt;/h2&gt;
&lt;p&gt;ここでは、Relayでのページネーションの仕組みを先ほどの例と同様にシンプルなプロフィールページを使って説明しましょう。プロフィールページにはユーザに関する情報が掲載されており、ユーザの友達のリストも掲載されています。友達のリストはページネーションができるものにしておきましょう。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Profile.ts&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; React &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;react&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; useLazyLoadQuery &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;react-relay/hooks&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; graphql &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;react-relay&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; ProfileQuery &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;./__generated__/ProfileQuery.graphql&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; FriendsList &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;./FriendsList&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Props&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  userId&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;Profile&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; userId &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Props&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; userById &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token generic-function&quot;&gt;&lt;span class=&quot;token function&quot;&gt;useLazyLoadQuery&lt;/span&gt;&lt;span class=&quot;token generic class-name&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;ProfileQuery&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    graphql&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;
      query ProfileQuery($userId: ID!) {
        userById(id: $userId) {
          firstName
          lastName
          ...FriendsList_user
        }
      }
    &lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      variables&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; userId &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;userById&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;userById&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;firstName&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;userById&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;lastName&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;Friends&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;FriendsList&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;userById&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;これがプロフィールページを表示するためのルートのコンポーネントです。ご覧のように、これはqueryを作成して、このコンポーネント自身を表示するための情報（&lt;code class=&quot;language-text&quot;&gt;firstName&lt;/code&gt;と&lt;code class=&quot;language-text&quot;&gt;lastName&lt;/code&gt;）を要求し、そして&lt;code class=&quot;language-text&quot;&gt;FriendsList_user&lt;/code&gt;のfragmentをqueryに含みます。fragmentには&lt;code class=&quot;language-text&quot;&gt;FriendsList&lt;/code&gt;コンポーネントをレンダリングできるようにするために必要な&lt;code class=&quot;language-text&quot;&gt;User&lt;/code&gt;型のデータを含んでいます。&lt;/p&gt;
&lt;h2 id=&quot;コンポーネントの真のモジュール性の力&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E3%82%B3%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%8D%E3%83%B3%E3%83%88%E3%81%AE%E7%9C%9F%E3%81%AE%E3%83%A2%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AB%E6%80%A7%E3%81%AE%E5%8A%9B&quot; aria-label=&quot;コンポーネントの真のモジュール性の力 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;コンポーネントの真のモジュール性の力&lt;/h2&gt;
&lt;p&gt;今のところどこにもページネーションは見当たりませんよね？ 待ってください、すぐ来ますから。ただ、その前に気をつけてほしいことがあります。&lt;code class=&quot;language-text&quot;&gt;&amp;lt;FriendsList /&amp;gt;&lt;/code&gt;がページネーションを行っていることを&lt;em&gt;このコンポーネントが知る必要はありません&lt;/em&gt;。これが Relay のもう一つの強みです。このことがもたらす意味をいくつか挙げてみましょう。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;どんなコンポーネントでも、既にレンダリングしているコンポーネントを弄らずに、ページネーションを&lt;em&gt;独立して&lt;/em&gt;導入できます。「そんなことか」と思いましたか？ 2週間の急ぎのプロジェクトでなくても、ページネーションを導入する必要のあるコンポーネントがかなり多くの画面に分散されている場合、きっとそうは思わないでしょう。&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;ProfileQuery&lt;/code&gt;は変数のような不要なものを定義する必要はなく、&lt;code class=&quot;language-text&quot;&gt;&amp;lt;FriendsList /&amp;gt;&lt;/code&gt;がページネーションできるようにするだけです。&lt;/li&gt;
&lt;li&gt;既に仄めかされていますが、これらの特徴はコンポーネント間に暗黙の（または明示的な）依存関係が作成されないことを意味します。つまり、何かを壊す危険を冒すことなく、コンポーネントを安全にリファクタリングしたりメンテナンスできます。&lt;em&gt;高速に&lt;/em&gt;作業ができるということです。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;ページネーションを行うコンポーネントの構築&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E3%83%9A%E3%83%BC%E3%82%B8%E3%83%8D%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%82%92%E8%A1%8C%E3%81%86%E3%82%B3%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%8D%E3%83%B3%E3%83%88%E3%81%AE%E6%A7%8B%E7%AF%89&quot; aria-label=&quot;ページネーションを行うコンポーネントの構築 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;ページネーションを行うコンポーネントの構築&lt;/h2&gt;
&lt;p&gt;下記は&lt;code class=&quot;language-text&quot;&gt;FriendsList&lt;/code&gt;コンポーネントで、これが実際にページネーションを行っているものです。こちらは少し濃い内容になります。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// FriendsList.ts&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; React &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;react&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; usePaginationFragment &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;react-relay/hooks&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; graphql &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;react-relay&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; FriendsList_user$key &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;./__generated__/FriendsList_user_graphql&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; FriendsListPaginationQuery &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;./__generated__/FriendsListPaginationQuery_graphql&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; getConnectionNodes &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;./utils/getConnectionNodes&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Props&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  user&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; FriendsList_user$key&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;FriendsList&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; user &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Props&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; hasNext&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; loadNext&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; isLoadingNext &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token generic-function&quot;&gt;&lt;span class=&quot;token function&quot;&gt;usePaginationFragment&lt;/span&gt;&lt;span class=&quot;token generic class-name&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;FriendsListPaginationQuery&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; _&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    graphql&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;
      fragment FriendsList_user on User
        @argumentDefinitions(first: { type: &quot;Int!&quot;, defaultValue: 5 }, after: { type: &quot;String&quot; })
        @refetchable(queryName: &quot;FriendsListPaginationQuery&quot;) {
        friends(first: $first, after: $after) @connection(key: &quot;FriendsList_user_friends&quot;) {
          edges {
            node {
              id
              firstName
            }
          }
        }
      }
    &lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    user&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getConnectionNodes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;friends&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;friend&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;friend&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;friend&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;firstName&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;hasNext &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;disabled&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;isLoadingNext&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;onClick&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;loadNext&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;isLoadingNext &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Loading...&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Load more&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;ここにはたくさんのことが書かれていて、これからすべてを少しずつ分解していきますが、まずはどれだけ手作業が少ないかに注目してください。その点について、いくつか見ていきましょう。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ページネーションに使用するカスタムqueryを定義する必要はありません。Relayが自動的に生成してくれます。&lt;/li&gt;
&lt;li&gt;ページネーションで次のカーソルを追跡する必要はありません。Relayが自動的にやってくれるので、私たちがミスすることはありません。&lt;/li&gt;
&lt;li&gt;ページネーション結果をストアにあるものとマージするためのカスタムロジックは不要です。Relayが代行してくれます。&lt;/li&gt;
&lt;li&gt;ロードの状態を追跡したりロード可能なアイテムの数が増えたかどうかを確認したりするために、余計なことをする必要はありません。Relayが提供してくれるので、こちら側で追加のアクションをする必要はありません。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;コードが少なくて済むというメリット以外にも、自分で書かなければならないコードが少なくなる、つまり何かをミスする可能性が少なくて済むというメリットもあります。&lt;/p&gt;
&lt;p&gt;それでは上のコードの中で、それを可能にしているものを分解してみましょう。難しいものがいくつかありますね。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; FriendsList_user$key &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;./__generated__/FriendsList_user_graphql&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; FriendsListPaginationQuery &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;./__generated__/FriendsListPaginationQuery_graphql&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;このコードでは、&lt;code class=&quot;language-text&quot;&gt;__generated__&lt;/code&gt;フォルダから型定義をインポートしています。これらは、定義したfragmentと、Relayが自動的に生成したページネーションのためのクエリの、両方の型安全を確保するためのものです。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; getConnectionNodes &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;./utils/getConnectionNodes&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;getConnectionNodes&lt;/code&gt;という関数もインポートします。これはカスタムヘルパーで、任意のconnectionから内部にあるすべての&lt;code class=&quot;language-text&quot;&gt;node&lt;/code&gt;を型安全な方法で配列に取り出すことができます。公式のRelayのパッケージにはありませんが自分で作るのはとても簡単で、&lt;a href=&quot;https://github.com/zth/relay-utils/blob/master/src/collectConnectionNodes.js&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;ここで例を見ることができます&lt;/a&gt;。標準化されているからこそ簡単に作れるツールの良い例です。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; hasNext&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; loadNext&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; isLoadingNext &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token generic-function&quot;&gt;&lt;span class=&quot;token function&quot;&gt;usePaginationFragment&lt;/span&gt;&lt;span class=&quot;token generic class-name&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;FriendsListPaginationQuery&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; _&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    graphql&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;
      fragment FriendsList_user on User
        @argumentDefinitions(first: { type: &quot;Int!&quot;, defaultValue: 5 }, after: { type: &quot;String&quot; })
        @refetchable(queryName: &quot;FriendsListPaginationQuery&quot;) {
        friends(first: $first, after: $after) @connection(key: &quot;FriendsList_user_friends&quot;) {
          edges {
            node {
              id
              firstName
            }
          }
        }
      }
    &lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    user&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;usePaginationFragment&lt;/code&gt;というhookを使って、propsのページネーションに関連する一部を返してくれます。また&lt;code class=&quot;language-text&quot;&gt;data&lt;/code&gt;も返していますが、これは定義した &lt;code class=&quot;language-text&quot;&gt;FriendsList_user&lt;/code&gt;fragmentのデータです。&lt;/p&gt;
&lt;p&gt;fragmentといえば、良いことが起きています。fragmentの定義で何が起こっているのか、もっと深く掘り下げてみましょう。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;@&lt;span class=&quot;token function&quot;&gt;argumentDefinitions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;first&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Int!&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; defaultValue&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; after&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;String&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;relayではfragmentの引数を定義することができる&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#relay%E3%81%A7%E3%81%AFfragment%E3%81%AE%E5%BC%95%E6%95%B0%E3%82%92%E5%AE%9A%E7%BE%A9%E3%81%99%E3%82%8B%E3%81%93%E3%81%A8%E3%81%8C%E3%81%A7%E3%81%8D%E3%82%8B&quot; aria-label=&quot;relayではfragmentの引数を定義することができる permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Relayではfragmentの引数を定義することができる&lt;/h2&gt;
&lt;p&gt;まず目立っているのは、fragmentに&lt;code class=&quot;language-text&quot;&gt;@argumentDefinitions&lt;/code&gt;というdirectiveを追加したことです。このdirectiveは&lt;code class=&quot;language-text&quot;&gt;first&lt;/code&gt;（&lt;code class=&quot;language-text&quot;&gt;Int!&lt;/code&gt;型）と&lt;code class=&quot;language-text&quot;&gt;after&lt;/code&gt;（&lt;code class=&quot;language-text&quot;&gt;String&lt;/code&gt;型）という2つの引数を取ります。&lt;code class=&quot;language-text&quot;&gt;first&lt;/code&gt;は必須なので、fragmentの引数に値を指定しない場合Relayは定義されているデフォルト値（この場合&lt;code class=&quot;language-text&quot;&gt;5&lt;/code&gt;）を使用します。&lt;/p&gt;
&lt;p&gt;fragmentの引数を定義できることも、モジュール性とスケーラビリティに大きな違いをもたらすRelayの機能の1つです。これがどのように機能するのかについてここでは詳しく説明しませんが、&lt;code class=&quot;language-text&quot;&gt;FriendsList_user&lt;/code&gt;fragmentを使用しているユーザーは、そのfragmentを使用する際に&lt;code class=&quot;language-text&quot;&gt;first&lt;/code&gt;と&lt;code class=&quot;language-text&quot;&gt;after&lt;/code&gt;の値をオーバーライドすることができるようになります。以下のようになります。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;graphql&quot;&gt;&lt;pre class=&quot;language-graphql&quot;&gt;&lt;code class=&quot;language-graphql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;query&lt;/span&gt; SomeUserQuery &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  loggedInUser &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token fragment function&quot;&gt;FriendsList_user&lt;/span&gt; &lt;span class=&quot;token directive function&quot;&gt;@arguments&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;これにより、デフォルトの最初の5人のフォロワーではなく、最初の10人のフォロワーを直接&lt;code class=&quot;language-text&quot;&gt;&amp;lt;FriendsList /&amp;gt;&lt;/code&gt;に取得することができます。&lt;/p&gt;
&lt;h2 id=&quot;relayがページネーションqueryを作成する&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#relay%E3%81%8C%E3%83%9A%E3%83%BC%E3%82%B8%E3%83%8D%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3query%E3%82%92%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B&quot; aria-label=&quot;relayがページネーションqueryを作成する permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Relayがページネーションqueryを作成する&lt;/h2&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;@&lt;span class=&quot;token function&quot;&gt;refetchable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;queryName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;FriendsListPaginationQuery&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;先ほどのコードの後には、&lt;code class=&quot;language-text&quot;&gt;@refetchable&lt;/code&gt;という別のdirectiveがあります。これは、新しい変数でfragmentのデータを再取得できるようにしたいということをRelayに伝えています。directiveに与えられた&lt;code class=&quot;language-text&quot;&gt;queryName&lt;/code&gt;は、生成されるqueryを呼び出すための名前に&lt;code class=&quot;language-text&quot;&gt;FriendsListPaginationQuery&lt;/code&gt;を使用することを示しています。&lt;/p&gt;
&lt;p&gt;これにより、&lt;em&gt;おおむね&lt;/em&gt;次のようなqueryが生成されます。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;graphql&quot;&gt;&lt;pre class=&quot;language-graphql&quot;&gt;&lt;code class=&quot;language-graphql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;query&lt;/span&gt; FriendsListPaginationQuery&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ID&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$first&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Int&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$after&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  node&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;on&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      friends&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$first&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;after&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$after&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        pageInfo &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          endCursor
          hasNextPage
          startCursor
          hasPreviousPage
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        edges &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          node &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            id
            firstName
          &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
          cursor
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;しかし、このqueryについて知ったり、考えたり、気にする必要はありません！&lt;/strong&gt; queryに必要な変数（&lt;code class=&quot;language-text&quot;&gt;id&lt;/code&gt;や&lt;code class=&quot;language-text&quot;&gt;after&lt;/code&gt;、次へページネーションするためのカーソルなど）の供給など、すべての繋ぎ込みはRelayがやってくれます。あなたは取得したいアイテムの数を指定するだけです。&lt;/p&gt;
&lt;p&gt;これこそが、Relayによるページネーションをとても人間工学的にしている部分です。Relayが文字通り&lt;em&gt;あなたのためにコードとqueryを記述してくれる&lt;/em&gt;ので、ページネーションの複雑さをすべて隠してくれます！&lt;/p&gt;
&lt;h2 id=&quot;connectionをrelayに知らせれば、あとはrelayにおまかせ&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#connection%E3%82%92relay%E3%81%AB%E7%9F%A5%E3%82%89%E3%81%9B%E3%82%8C%E3%81%B0%E3%80%81%E3%81%82%E3%81%A8%E3%81%AFrelay%E3%81%AB%E3%81%8A%E3%81%BE%E3%81%8B%E3%81%9B&quot; aria-label=&quot;connectionをrelayに知らせれば、あとはrelayにおまかせ permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;ConnectionをRelayに知らせれば、あとはRelayにおまかせ&lt;/h2&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;        &lt;span class=&quot;token function&quot;&gt;friends&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;first&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; $first&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; after&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; $after&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; @&lt;span class=&quot;token function&quot;&gt;connection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;key&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;FriendsList_user_friends&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          edges &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            node &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
              id
              firstName
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;**friends(first: $first, after: $after)**&lt;/code&gt;
ここでフィールドを指定します。&lt;code class=&quot;language-text&quot;&gt;friends&lt;/code&gt;はページネーションしたいconnectionを持つフィールドです。&lt;code class=&quot;language-text&quot;&gt;argumentDefinitions&lt;/code&gt;で定義されている&lt;code class=&quot;language-text&quot;&gt;first&lt;/code&gt;と&lt;code class=&quot;language-text&quot;&gt;after&lt;/code&gt;を渡していることに注意してください。&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;**@connection**&lt;/code&gt;
&lt;code class=&quot;language-text&quot;&gt;friends&lt;/code&gt;には&lt;code class=&quot;language-text&quot;&gt;@connection(key: &amp;quot;FriendsList_user_friends&amp;quot;)&lt;/code&gt; というdirectiveが付いています。このdirectiveは、ページネーションしたいconnectionの場所をRelayに伝えます。このdirectiveを追加することで、サーバに送信されるqueryの中で、connectionを指定した部分に&lt;code class=&quot;language-text&quot;&gt;pageInfo&lt;/code&gt;の全てのフィールドの指定を自動的に追加するなど、Relay はいくつかのことを行うことができるようになります。これによりRelayはその情報を利用してさらにロードできるかどうかを判断したり、適切なカーソルを自動的に使用してページネーションを行うことができます。このように間違ったことをしてしまう可能性のある手動のステップを削除し、自動化しているのです。&lt;/p&gt;
&lt;p&gt;繰り返しになりますが、Relayがこれら全てを行ってくれるのであなたが何かを見たり考える必要はないものの、サーバーに送信される&lt;code class=&quot;language-text&quot;&gt;friends&lt;/code&gt;の指定は次のようになります。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token function&quot;&gt;friends&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;first&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; $first&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; after&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; $after&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  pageInfo &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    endCursor
    hasNextPage
    startCursor
    hasPreviousPage
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  egdes &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    node &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    cursor
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;@connection&lt;/code&gt;アノテーションを追加することで、Relayはページネーションに必要な項目を追加するべき場所を知ることができます。&lt;/p&gt;
&lt;p&gt;さらに&lt;code class=&quot;language-text&quot;&gt;@connection&lt;/code&gt;が行うのは、キャッシュの更新でアイテムを追加したり削除したりするときなど、キャッシュ内でこのconnectionと対話する必要がある場合&lt;sup id=&quot;fnref-3&quot;&gt;&lt;a href=&quot;#fn-3&quot; class=&quot;footnote-ref&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;に使用するキーをRelayに指定することです。複数のリストで同じconnectionを介してページネーションする場合があるため、ここで一意のキーを設定することは重要です。&lt;/p&gt;
&lt;p&gt;これはまた、Relayがページネーションのレスポンスから取り出して現在のページネーションリストに追加する必要のある、すべての場所を推測できることを意味します。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;disabled&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;isLoadingNext&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;onClick&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;loadNext&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;それ以外、Relayが提供するものを実際に使用するコードのほとんどはかなり自明のはずです。&lt;/p&gt;
&lt;h1 id=&quot;これはどのように機能するのか？&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E3%81%93%E3%82%8C%E3%81%AF%E3%81%A9%E3%81%AE%E3%82%88%E3%81%86%E3%81%AB%E6%A9%9F%E8%83%BD%E3%81%99%E3%82%8B%E3%81%AE%E3%81%8B%EF%BC%9F&quot; aria-label=&quot;これはどのように機能するのか？ permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;これはどのように機能するのか？&lt;/h1&gt;
&lt;p&gt;つまり、ページネーションがどのように見えるかをまとめると、基本的にはfragmentの定義の中でdirectiveを使って必要な情報をRelayに渡し、その見返りとしてRelayができる限りのことを自動化してくれるということになります。&lt;/p&gt;
&lt;p&gt;しかし、このようなことをRelayはどのようにして実現できるのでしょうか？&lt;/p&gt;
&lt;p&gt;それはすべて規則と標準化に帰着します。&lt;a href=&quot;https://graphql.org/learn/global-object-identification/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;グローバルな識別と&lt;code class=&quot;language-text&quot;&gt;node&lt;/code&gt;インターフェースの仕様&lt;/a&gt;に従えば、Relayは以下のことが可能です。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;現在いる特定の&lt;code class=&quot;language-text&quot;&gt;node&lt;/code&gt;を再取得するためのqueryを自動的に生成し、そのqueryに再取得中のfragmentを自動的に追加します。&lt;/li&gt;
&lt;li&gt;対象となるオブジェクトの&lt;code class=&quot;language-text&quot;&gt;id&lt;/code&gt;が特定のオブジェクトのみを示すことが分かっているため、生成されたクエリに変数を与える必要がないことを保証します&lt;sup id=&quot;fnref-2&quot;&gt;&lt;a href=&quot;#fn-2&quot; class=&quot;footnote-ref&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;また、&lt;a href=&quot;https://graphql.org/learn/pagination/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Connectionのページネーション仕様&lt;/a&gt;に従うことで、Relayは以下のようなことができます。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;初期queryとなる&lt;code class=&quot;language-text&quot;&gt;ProfileQuery&lt;/code&gt;と生成された&lt;code class=&quot;language-text&quot;&gt;FriendsListPaginationQuery&lt;/code&gt;のqueryの両方に、必要なメタデータの指定を自動的に追加します。&lt;/li&gt;
&lt;li&gt;データの構造が標準化されたconnectionであることを知っているので、ページネーションの結果を既存のリストに自動的にマージします。したがって、必要なものは全て取り出せます。&lt;/li&gt;
&lt;li&gt;標準化された方法で&lt;code class=&quot;language-text&quot;&gt;pageInfo&lt;/code&gt;で利用できるようになるため、より多くの結果をロードするために使用するカーソルを自動的に追跡します。&lt;code class=&quot;language-text&quot;&gt;pageInfo&lt;/code&gt;は、（上で述べたように）あなたが知らないところで自動的にクエリに挿入することができます。これも標準化されているからできることです。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;そして、その結果は本当に素晴らしいものになっています。Relayは、ページネーションを人間工学的にしただけでなく、手作業でのエラーが発生する可能性をほぼすべて排除してくれました。&lt;/p&gt;
&lt;h2 id=&quot;まとめ&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E3%81%BE%E3%81%A8%E3%82%81&quot; aria-label=&quot;まとめ permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;まとめ&lt;/h2&gt;
&lt;p&gt;この記事では、Relayのようなフレームワークがどれだけ自動化できるのか、また、規則に従えばDXがどれだけ素晴らしいものになるのか、ということにスポットを当ててみました。この記事では、以下の内容に光を当てました。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;GraphQLでのページネーションは多くの手作業を必要とし、開発者をミスさせてしまう可能性があります。&lt;/li&gt;
&lt;li&gt;規則に従うことで、Relayのようなフレームワークはページネーションの開発体験を信じられないほど人間工学的なものに変えることができ、手作業によるエラーの原因となることのほとんど（&lt;em&gt;すべて&lt;/em&gt;ではないにしても）を取り除くことができます。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;これは良い入門編ですが、Relayにはページネーションのための機能が他にもたくさんあります。詳細については&lt;a href=&quot;https://relay.dev/docs/en/experimental/a-guided-tour-of-relay#rendering-list-data-and-pagination&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Relayの公式ドキュメント&lt;/a&gt;をご覧ください。&lt;/p&gt;
&lt;p&gt;最後までお読みいただきありがとうございました！&lt;/p&gt;
&lt;div class=&quot;footnotes&quot;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&quot;fn-1&quot;&gt;
&lt;p&gt;訳注：&lt;code class=&quot;language-text&quot;&gt;$userLogin&lt;/code&gt;のこと。&lt;code class=&quot;language-text&quot;&gt;ProfileQuery&lt;/code&gt;と&lt;code class=&quot;language-text&quot;&gt;UserProfileFollowersPaginationQuery&lt;/code&gt;の両方に同じ値を指定する必要があります。&lt;/p&gt;
&lt;a href=&quot;#fnref-1&quot; class=&quot;footnote-backref&quot;&gt;↩&lt;/a&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-2&quot;&gt;
&lt;p&gt;訳注：自動的に生成されたクエリに対して何らかの変数を与える必要はないということです。最初にqueryでアクセスできたオブジェクトの&lt;code class=&quot;language-text&quot;&gt;id&lt;/code&gt;を使用して、そのオブジェクト配下のリストを取得するqueryを実行できるので、複雑な変数は不要です。仮に&lt;code class=&quot;language-text&quot;&gt;id&lt;/code&gt;が一意ではなかったり&lt;code class=&quot;language-text&quot;&gt;node&lt;/code&gt;インターフェースがない場合、リストを所有するオブジェクトを単一で取得するqueryを自動生成する方法がありません。&lt;/p&gt;
&lt;a href=&quot;#fnref-2&quot; class=&quot;footnote-backref&quot;&gt;↩&lt;/a&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-3&quot;&gt;
&lt;p&gt;訳注：対話する必要がある理由は、アイテムを追加したり削除するなら、connectionからも同様に追加したり削除する必要があるからです。そうしないとリストのコンポーネントはユーザーのアクション（追加や削除）で変化しません。&lt;/p&gt;
&lt;a href=&quot;#fnref-3&quot; class=&quot;footnote-backref&quot;&gt;↩&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</content:encoded></item><item><title><![CDATA[GraphQLでのConnectionベースのページネーション]]></title><description><![CDATA[Facebookが作ったGraphQLクライアントのRelayを理解する、Relay入門シリーズ。2記事目はConnectionとEdgeについて。]]></description><link>https://kazekyo.com/posts/2020-08-28-an-introduction-to-relay-2</link><guid isPermaLink="false">https://kazekyo.com/posts/2020-08-28-an-introduction-to-relay-2</guid><pubDate>Thu, 27 Aug 2020 15:00:00 GMT</pubDate><content:encoded>&lt;p&gt;GraphQLではConnectionベースのページネーションが一般的だ思いますが、一度は「なんでこんな仕様なんだ、もっとシンプルにできるだろ」と思うものですよね。
この翻訳記事ではConnectionベースのページネーションについて深く掘り下げ、なぜこうする必要があるのか、どのようなメリットが得られるかを解説します。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://dev.to/zth/connection-based-pagination-in-graphql-2588&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;（原文）&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Relay入門シリーズ（全4記事）&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;/posts/2020-08-27-an-introduction-to-relay-1&quot;&gt;Relay：あなたのために泥臭い仕事をしてくれるGraphQLクライアント&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GraphQLでのConnectionベースのページネーション（本記事）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;/posts/2020-08-29-an-introduction-to-relay-3&quot;&gt;Relayを使って最小限の努力でページネーションをする&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;/posts/2020-08-30-an-introduction-to-relay-4&quot;&gt;Nodeインターフェースの魔法&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;この連載は、&lt;a href=&quot;https://github.com/zth&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Gabriel Nordeborn&lt;/a&gt;と&lt;a href=&quot;https://github.com/sgrove&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Sean Grove&lt;/a&gt;が執筆しています。GabrielはスウェーデンのITコンサルタント会社&lt;a href=&quot;https://arizon.se/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Arizon&lt;/a&gt;のフロントエンド開発者でありパートナーでもあり、長い間Relayを利用してきました。SeanはサードパーティのAPIをGraphQLで統一する&lt;a href=&quot;https://www.onegraph.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;OneGraph.com&lt;/a&gt;の共同創業者です。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Connectionベースのページネーションはどのようにページネーションするかを構造化する方法で、シンプルなページネーションから少し高度なページネーションまで対応しています。このページネーションの方法は&lt;a href=&quot;https://graphql.org/learn/pagination/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;最近GraphQLの公式のベストプラクティス&lt;/a&gt;に昇格しました。この記事では、なぜこれがGraphQLでは良いことなのか、また、どのようなタイプのツールが使えるようになるのかに焦点を当ててみたいと思います。&lt;/p&gt;
&lt;p&gt;Connectionベースのページネーションとは何か、なぜそれが有用なのかについては、多くのリソースがあります。この記事を読む前に、まずは&lt;a href=&quot;https://graphql.org/learn/pagination/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;GraphQLの公式サイト&lt;/a&gt;のページネーションのセクションを見てみることをお勧めします。&lt;/p&gt;
&lt;h3 id=&quot;標準化が勝利をもたらす&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E6%A8%99%E6%BA%96%E5%8C%96%E3%81%8C%E5%8B%9D%E5%88%A9%E3%82%92%E3%82%82%E3%81%9F%E3%82%89%E3%81%99&quot; aria-label=&quot;標準化が勝利をもたらす permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;標準化が勝利をもたらす&lt;/h3&gt;
&lt;p&gt;Connectionベースのページネーションは、ページネーションのために標準化された取り決めを使用するということです。なぜそんなことを気にするのでしょうか？ この記事でお見せするように、標準化された取り決めを使うことはツールやフレームワークをページネーションと深く統合できることを意味します。そしてそれは、あなたの開発者としての人生がずっと楽になることを意味します。また、ツールがヘビーな仕事をしてくれるので、より早くリリースできるということも意味します。&lt;/p&gt;
&lt;h2 id=&quot;それは何で、何に適しているのか？&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E3%81%9D%E3%82%8C%E3%81%AF%E4%BD%95%E3%81%A7%E3%80%81%E4%BD%95%E3%81%AB%E9%81%A9%E3%81%97%E3%81%A6%E3%81%84%E3%82%8B%E3%81%AE%E3%81%8B%EF%BC%9F&quot; aria-label=&quot;それは何で、何に適しているのか？ permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;それは何で、何に適しているのか？&lt;/h2&gt;
&lt;p&gt;Connectionベースのページネーションは、既存のリストに任意の量のアイテムを追加し続けるようなUI、つまり無限スクロール型のページネーションに最適です。既存のリストにアイテムを追加し続けるのではなく、ページ全体のアイテムを個別に取得するページネーション&lt;sup id=&quot;fnref-1&quot;&gt;&lt;a href=&quot;#fn-1&quot; class=&quot;footnote-ref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;を実装したい場合は、別で実装した方が良いでしょう。&lt;/p&gt;
&lt;p&gt;ここで重要なのは、Connectionベースのページネーションは、&lt;em&gt;ページネーションのための取り決めを構造化する方法にすぎない&lt;/em&gt;ということです。Connectionベースのページネーションを使用する場合、バックエンドでデータを取得する方法に影響がある&lt;em&gt;かも&lt;/em&gt;しれませんが、GraphQLの世界でこれを使用する主な目的は、バックエンドを特定のページネーション戦略にコミットさせずに、ページネーションのための標準化された構造を提供することです。&lt;/p&gt;
&lt;h2 id=&quot;ページネーションのための標準化された構造&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E3%83%9A%E3%83%BC%E3%82%B8%E3%83%8D%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%AE%E3%81%9F%E3%82%81%E3%81%AE%E6%A8%99%E6%BA%96%E5%8C%96%E3%81%95%E3%82%8C%E3%81%9F%E6%A7%8B%E9%80%A0&quot; aria-label=&quot;ページネーションのための標準化された構造 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;ページネーションのための標準化された構造&lt;/h2&gt;
&lt;p&gt;それでは、Connectionベースのページネーションの構造の例を見てみましょう。あなたが好きだと思う映画を見つけるための検索機能を構築していると想像してみてください。私たちのバックエンドの検索エンジンは非常にスマートなので、あなたが見たいと思うものを評価する際に、さまざまな要素を考慮することができます。以下の定義は、任意の検索ワードで&lt;code class=&quot;language-text&quot;&gt;Movie&lt;/code&gt;のリストを取得し、ページネーションすることを表現しています。検索ワードは、&lt;code class=&quot;language-text&quot;&gt;アクション映画&lt;/code&gt;、または&lt;code class=&quot;language-text&quot;&gt;恋愛コメディ&lt;/code&gt;のようなものである可能性があります。私たちの検索エンジンは、それをすべて理解できます！&lt;/p&gt;
&lt;p&gt;気軽に下記の仕様に目を通してみてください。今は理解しようしなくて大丈夫です。記事を読み進める上で、大体の形が頭に入っていれば良いと思います。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;graphql&quot;&gt;&lt;pre class=&quot;language-graphql&quot;&gt;&lt;code class=&quot;language-graphql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Movie&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ID&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;releasedYear&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Int&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;coverUrl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Movie型のedge&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MovieEdge&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Movie &lt;span class=&quot;token comment&quot;&gt;# node。このEdgeの中ではMovieです&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;cursor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; String &lt;span class=&quot;token comment&quot;&gt;# このEdgeのカーソル&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# 現在のページネーションのメタデータ&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PageInfo&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;hasNextPage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Boolean&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;endCursor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; String
  &lt;span class=&quot;token attr-name&quot;&gt;hasPreviousPage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Boolean&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;startCursor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; String
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# connection。ページネーションのrootの型です&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MovieConnection&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;pageInfo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; PageInfo&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;edges&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;MovieEdge&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Query&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;# first、last、after、beforeを使ってページを前後させることができます&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Int&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 
    &lt;span class=&quot;token attr-name&quot;&gt;last&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Int&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 
    &lt;span class=&quot;token attr-name&quot;&gt;after&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 
    &lt;span class=&quot;token attr-name&quot;&gt;before&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; String
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; MovieConnection
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;本当に簡単なはずのものにしては構造が多いですよね？上記の構造を頭に入れておいて、ページネーションがどのように実装され、またプロジェクトでどのように成長するのか、典型的なシナリオを見てみましょう。&lt;/p&gt;
&lt;h2 id=&quot;ページネーションの実装方法の一般的なシナリオ&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E3%83%9A%E3%83%BC%E3%82%B8%E3%83%8D%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%AE%E5%AE%9F%E8%A3%85%E6%96%B9%E6%B3%95%E3%81%AE%E4%B8%80%E8%88%AC%E7%9A%84%E3%81%AA%E3%82%B7%E3%83%8A%E3%83%AA%E3%82%AA&quot; aria-label=&quot;ページネーションの実装方法の一般的なシナリオ permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;ページネーションの実装方法の一般的なシナリオ&lt;/h2&gt;
&lt;p&gt;ほとんどのアプリケーションでは、遅かれ早かれ何らかの形でのページネーションが必要となり、様々な方法で実装することができます。ここでは最も一般的でありきたりなページネーションをGraphQLで実装するとどのようになるか、シナリオを描いてみましょう。&lt;/p&gt;
&lt;h2 id=&quot;step-1-可能な限りシンプルなページネーション&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#step-1-%E5%8F%AF%E8%83%BD%E3%81%AA%E9%99%90%E3%82%8A%E3%82%B7%E3%83%B3%E3%83%97%E3%83%AB%E3%81%AA%E3%83%9A%E3%83%BC%E3%82%B8%E3%83%8D%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3&quot; aria-label=&quot;step 1 可能な限りシンプルなページネーション permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Step 1. 可能な限りシンプルなページネーション&lt;/h2&gt;
&lt;p&gt;先ほどの映画の例をもとに、&lt;code class=&quot;language-text&quot;&gt;西部劇&lt;/code&gt;や&lt;code class=&quot;language-text&quot;&gt;激しいアクション&lt;/code&gt;など、任意の検索クエリで映画をページネートできる検索機能を設計します。&lt;/p&gt;
&lt;p&gt;ほとんどの人は（当然ですが）基本的なことから始めます。つまり、「非常にシンプルなページネーションをしたいだけで、派手なことをする必要はない」ということです。通常は次のようになります。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;graphql&quot;&gt;&lt;pre class=&quot;language-graphql&quot;&gt;&lt;code class=&quot;language-graphql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Query&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;searchMovies&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;limit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Int
    &lt;span class=&quot;token attr-name&quot;&gt;offset&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Int
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;Movie&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;私たちは、検索クエリを使用して&lt;code class=&quot;language-text&quot;&gt;Movie&lt;/code&gt;の単純なリストをフィルタリングし、その検索クエリに最適にマッチする映画を見つけます。これはすべて問題なく、うまく機能しています。&lt;/p&gt;
&lt;h2 id=&quot;step-2-さらに検索結果があるかを知るにはどうすれば良いのか？&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#step-2-%E3%81%95%E3%82%89%E3%81%AB%E6%A4%9C%E7%B4%A2%E7%B5%90%E6%9E%9C%E3%81%8C%E3%81%82%E3%82%8B%E3%81%8B%E3%82%92%E7%9F%A5%E3%82%8B%E3%81%AB%E3%81%AF%E3%81%A9%E3%81%86%E3%81%99%E3%82%8C%E3%81%B0%E8%89%AF%E3%81%84%E3%81%AE%E3%81%8B%EF%BC%9F&quot; aria-label=&quot;step 2 さらに検索結果があるかを知るにはどうすれば良いのか？ permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Step 2. さらに検索結果があるかを知るにはどうすれば良いのか？&lt;/h2&gt;
&lt;p&gt;製品は進化していくので、取得できる映画が増えた場合にはUIに「もっと見る」ボタンを追加したいと思います。しかし、上の例ではリストを返しているだけなので、さらにロードするべき検索結果があるかどうかを知る方法がありません。&lt;/p&gt;
&lt;p&gt;これも簡単に解決できますね！ 例を拡張してみましょう。個々の&lt;code class=&quot;language-text&quot;&gt;Movie&lt;/code&gt;を変更したくないので（そこに&lt;code class=&quot;language-text&quot;&gt;hasNextPage&lt;/code&gt;プロパティを持っていても意味がないでしょう&lt;sup id=&quot;fnref-2&quot;&gt;&lt;a href=&quot;#fn-2&quot; class=&quot;footnote-ref&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;）、&lt;code class=&quot;language-text&quot;&gt;searchMovies&lt;/code&gt;がメタデータを置くことができる小さなラッパーオブジェクト（&lt;code class=&quot;language-text&quot;&gt;MovieSearchResult&lt;/code&gt;）を返す必要があります。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;graphql&quot;&gt;&lt;pre class=&quot;language-graphql&quot;&gt;&lt;code class=&quot;language-graphql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MovieSearchResult&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;hasNext&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Boolean
  &lt;span class=&quot;token attr-name&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;Movie&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Query&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;searchMovies&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;limit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Int
     &lt;span class=&quot;token attr-name&quot;&gt;offset&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Int
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; MovieSearchResult&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;素晴らしい。これで必要なものは全て揃いましたね。問題は解決しました。&lt;/p&gt;
&lt;h2 id=&quot;step-3-オフセットを使用する場合&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#step-3-%E3%82%AA%E3%83%95%E3%82%BB%E3%83%83%E3%83%88%E3%82%92%E4%BD%BF%E7%94%A8%E3%81%99%E3%82%8B%E5%A0%B4%E5%90%88&quot; aria-label=&quot;step 3 オフセットを使用する場合 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Step 3. オフセットを使用する場合&lt;/h2&gt;
&lt;p&gt;製品はさらに進化し、私たちは今、検索結果の中に含まれる特定の&lt;code class=&quot;language-text&quot;&gt;Movie&lt;/code&gt;からページネーションを継続できる一意のURLを生成する必要があります。&lt;/p&gt;
&lt;p&gt;パッと思いつくやり方としては、次のようなURLを構築することかもしれません。
&lt;code class=&quot;language-text&quot;&gt;/search?limit=${limit}&amp;amp;offset=${offset}&amp;amp;query=${query}&lt;/code&gt;
これでいいんじゃないでしょうか？ 悲しいことに、そうではありません。&lt;/p&gt;
&lt;p&gt;私たちがこの検索結果からある映画を見ているとして、その検索結果のリストの&lt;em&gt;前&lt;/em&gt;に加えられるような映画をデータベースに追加した場合どうなるのでしょうか？ オフセットは目的の映画を指し示すことはありません。&lt;/p&gt;
&lt;p&gt;一歩引いて考えてみましょう。人間がバックエンドAPIのメンテナと話しているように、私たちの意図を表現してみます。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;私は「Frost 2」までの映画の検索結果をすべて持っています。「Frost 2」の次の10作品をお願いします。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;私たちは人間でも分かるように意図を十分説明し、「ああ、あなたの検索クエリにマッチする結果にはいくつかの映画が追加されたり削除されたりしましたが、ここではFrost 2以降の10作品を表示しますね」と言われました。これがページネーションに求める動作です！&lt;/p&gt;
&lt;p&gt;これを機能させるには、&lt;em&gt;検索結果の特定のアイテムから&lt;/em&gt;ページネーションできるようにする必要があります。そのため、当然のことながら結果の中の1つのアイテムを識別する方法が必要になります。ここに”カーソル”（cursor）と呼ばれる、そのための既存の概念があります。我々はまさにその概念を使用し（一流の芸術家は盗む、そういうことです）、そしてそれをサポートするために私たちの検索クエリを微調整します。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;graphql&quot;&gt;&lt;pre class=&quot;language-graphql&quot;&gt;&lt;code class=&quot;language-graphql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MovieSearchResultWrapper&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Movie
  &lt;span class=&quot;token attr-name&quot;&gt;cursor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; String
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MovieSearchResult&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;hasNext&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Boolean
  &lt;span class=&quot;token attr-name&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;MovieSearchResultWrapper&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Query&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;searchMovies&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;limit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Int
    &lt;span class=&quot;token attr-name&quot;&gt;cursor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; String
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; MovieSearchResult&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;これで&lt;code class=&quot;language-text&quot;&gt;offset&lt;/code&gt;ではなく&lt;code class=&quot;language-text&quot;&gt;cursor&lt;/code&gt;で映画を検索できるようなり、結果ごとに&lt;code class=&quot;language-text&quot;&gt;cursor&lt;/code&gt;でアクセスできるようになります。これにより、ページネーションが安定します （現在のアイテムの前に追加されたアイテムは、以前のようにリストを混乱させることはありません）。これで次のようなURLを生成することができます。&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;/search?limit=${limit}&amp;amp;cursor=${cursor}&amp;amp;query=${query}&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;このURLは安定&lt;sup id=&quot;fnref-5&quot;&gt;&lt;a href=&quot;#fn-5&quot; class=&quot;footnote-ref&quot;&gt;5&lt;/a&gt;&lt;/sup&gt;しており、常に検索結果リストのアイテムから始まる検索結果を指すようになります（たとえその間にいくつかのアイテムが追加されたり削除されたりしていても）。素晴らしいですね！&lt;/p&gt;
&lt;h2 id=&quot;step-4-検索結果の個々のアイテムに関連するメタデータ&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#step-4-%E6%A4%9C%E7%B4%A2%E7%B5%90%E6%9E%9C%E3%81%AE%E5%80%8B%E3%80%85%E3%81%AE%E3%82%A2%E3%82%A4%E3%83%86%E3%83%A0%E3%81%AB%E9%96%A2%E9%80%A3%E3%81%99%E3%82%8B%E3%83%A1%E3%82%BF%E3%83%87%E3%83%BC%E3%82%BF&quot; aria-label=&quot;step 4 検索結果の個々のアイテムに関連するメタデータ permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Step 4. 検索結果の個々のアイテムに関連するメタデータ&lt;/h2&gt;
&lt;p&gt;新しい機能のリクエストが入ってきました。検索結果がどれだけあなたの&lt;em&gt;好み&lt;/em&gt;にマッチしているか、また&lt;em&gt;検索クエリ&lt;/em&gt;にどれだけ関連性があるか、ということについて知っているため、当然ながらそれをユーザーに表示したいと考えています。&lt;/p&gt;
&lt;p&gt;一方で、これは特定の検索クエリに対する特定の検索結果のアイテムに対してのみ有効なデータです。私たちの検索エンジンは、検索結果に対して効率的な方法でその情報を自動的に生成してくれます（結果の順序付けに既に使用している情報なので）。しかし、ルートの&lt;code class=&quot;language-text&quot;&gt;Movie&lt;/code&gt;型を拡張したくない場合（この拡張は本当に筋が悪いので&lt;sup id=&quot;fnref-3&quot;&gt;&lt;a href=&quot;#fn-3&quot; class=&quot;footnote-ref&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;）、それぞれの結果の情報を置ける場所は論理的にどこでしょうか？ さて、カーソルのニーズのために先ほど&lt;code class=&quot;language-text&quot;&gt;MovieSearchResultWrapper&lt;/code&gt;を導入したところですが、これは特定の検索に関連するメタデータだけを保持しますね。そこに置いてみましょう。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;graphql&quot;&gt;&lt;pre class=&quot;language-graphql&quot;&gt;&lt;code class=&quot;language-graphql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MovieSearchResultWrapper&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Movie
  &lt;span class=&quot;token attr-name&quot;&gt;cursor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; String
  &lt;span class=&quot;token attr-name&quot;&gt;relevanceFactor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Float
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;素晴らしい！これで素敵な数字を手に入れたので、派手なゲージメーターに変えることができ、みんながハッピーになることでしょう。&lt;/p&gt;
&lt;h2 id=&quot;待てよ、connectionベースのページネーションを自然と実装しただけでは？&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E5%BE%85%E3%81%A6%E3%82%88%E3%80%81connection%E3%83%99%E3%83%BC%E3%82%B9%E3%81%AE%E3%83%9A%E3%83%BC%E3%82%B8%E3%83%8D%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%82%92%E8%87%AA%E7%84%B6%E3%81%A8%E5%AE%9F%E8%A3%85%E3%81%97%E3%81%9F%E3%81%A0%E3%81%91%E3%81%A7%E3%81%AF%EF%BC%9F&quot; aria-label=&quot;待てよ、connectionベースのページネーションを自然と実装しただけでは？ permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;待てよ、Connectionベースのページネーションを自然と実装しただけでは？&lt;/h2&gt;
&lt;p&gt;ここで自然と作ってきたものを見て、Connectionベースのページネーションで作成した最初の構造と比較すると、この2つは事実上同じではないでしょうか？ その通りです！ 上記のモデルをConnection構造に当てはめてみましょう。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;graphql&quot;&gt;&lt;pre class=&quot;language-graphql&quot;&gt;&lt;code class=&quot;language-graphql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Movie&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ID&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;releasedYear&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Int&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;coverUrl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MovieEdge&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Movie &lt;span class=&quot;token comment&quot;&gt;# &quot;node&quot;はコレクション内のアイテムの一般的な名前です&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;cursor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; String &lt;span class=&quot;token comment&quot;&gt;# 検索結果のアイテムのカーソルはここに自然に収まります&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;relevanceFactor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Float &lt;span class=&quot;token comment&quot;&gt;# この検索結果にのみ関連する追加のメタデータもここに収まります&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# ページネーションをするために必要な全てのメタデータ&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PageInfo&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;hasNextPage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Boolean&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;endCursor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; String
  &lt;span class=&quot;token attr-name&quot;&gt;hasPreviousPage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Boolean&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;startCursor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; String
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# ページネーションのルート型。繰り返しますが、一般化/標準化された名前です&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MovieConnection&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;pageInfo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; PageInfo&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;edges&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;MovieEdge&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Query&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;searchMovies&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Int&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 
    &lt;span class=&quot;token attr-name&quot;&gt;last&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Int&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 
    &lt;span class=&quot;token attr-name&quot;&gt;after&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 
    &lt;span class=&quot;token attr-name&quot;&gt;before&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; String
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; MovieConnection
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;そういうことなので、独自の命名法を使う代わりにページネーションをConnectionベースのモデルに簡単に適合させることができます。しかし、そんなことをして何のメリットがあるのでしょうか？ もしかしたら、あなたはこの命名に反対しているのかもしれませんし、複雑すぎると思っているのかもしれませんし、当面はシンプルなページネーションだけが必要な段階にあるのかもしれません。なぜ気にするのでしょうか？&lt;/p&gt;
&lt;h2 id=&quot;テンプレは意思決定をなくし、ときめきをもらたす他のものに集中することができる&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E3%83%86%E3%83%B3%E3%83%97%E3%83%AC%E3%81%AF%E6%84%8F%E6%80%9D%E6%B1%BA%E5%AE%9A%E3%82%92%E3%81%AA%E3%81%8F%E3%81%97%E3%80%81%E3%81%A8%E3%81%8D%E3%82%81%E3%81%8D%E3%82%92%E3%82%82%E3%82%89%E3%81%9F%E3%81%99%E4%BB%96%E3%81%AE%E3%82%82%E3%81%AE%E3%81%AB%E9%9B%86%E4%B8%AD%E3%81%99%E3%82%8B%E3%81%93%E3%81%A8%E3%81%8C%E3%81%A7%E3%81%8D%E3%82%8B&quot; aria-label=&quot;テンプレは意思決定をなくし、ときめきをもらたす他のものに集中することができる permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;テンプレは意思決定をなくし、ときめき&lt;sup id=&quot;fnref-4&quot;&gt;&lt;a href=&quot;#fn-4&quot; class=&quot;footnote-ref&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;をもらたす他のものに集中することができる&lt;/h2&gt;
&lt;p&gt;上記のスキーマを使用してテンプレート化できるようになったので、私たちは何か（好きな名詞を入れてください）のリストをページネートしたい時には、穴埋めを行うだけでできるようになりました。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;graphql&quot;&gt;&lt;pre class=&quot;language-graphql&quot;&gt;&lt;code class=&quot;language-graphql&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# nounには好きな名詞を入れてください&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;noun&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;フィールドを書く&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;noun&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;Edge &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;noun&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# &quot;node&quot;はコレクション内のアイテムの一般的な名前です。つまりnounです。&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;cursor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; String &lt;span class=&quot;token comment&quot;&gt;# 検索結果のアイテムのカーソルはここに自然に収まります&lt;/span&gt;
  $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token fragment function&quot;&gt;noun&lt;/span&gt;のためのページネーションの他のメタデータ&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# ページネーションをするために必要な全てのメタデータ&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# ここは同じままです！&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PageInfo&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;hasNextPage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Boolean&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;endCursor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; String
  &lt;span class=&quot;token attr-name&quot;&gt;hasPreviousPage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Boolean&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;startCursor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; String
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# ページネーションのルート型。繰り返しますが、一般化/標準化された名前です&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;noun&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;Connection &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;pageInfo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; PageInfo&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;edges&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;$&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;noun&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;Edge&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Query&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;noun&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Int&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 
    &lt;span class=&quot;token attr-name&quot;&gt;last&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Int&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 
    &lt;span class=&quot;token attr-name&quot;&gt;after&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 
    &lt;span class=&quot;token attr-name&quot;&gt;before&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; String
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;noun&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;Connection
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;このようなテンプレートを持つことで、&lt;code class=&quot;language-text&quot;&gt;${noun}&lt;/code&gt;のリストのページネーションを簡単に作ることができます。&lt;/p&gt;
&lt;h2 id=&quot;標準化--ツールを使うことで人生が楽になる&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E6%A8%99%E6%BA%96%E5%8C%96--%E3%83%84%E3%83%BC%E3%83%AB%E3%82%92%E4%BD%BF%E3%81%86%E3%81%93%E3%81%A8%E3%81%A7%E4%BA%BA%E7%94%9F%E3%81%8C%E6%A5%BD%E3%81%AB%E3%81%AA%E3%82%8B&quot; aria-label=&quot;標準化  ツールを使うことで人生が楽になる permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;標準化 == ツールを使うことで人生が楽になる&lt;/h2&gt;
&lt;p&gt;もっと重要なのは、私たちが今まで築き上げてきたこれらの慣習が、私たちの開発者としての人生をシンプルにするためのツールの構築を簡単にしてくれるということです。ここでは、Connectionベースのページネーションがもたらすいくつかの利点と、ツールがあなたのためにできることをご紹介します。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;未来を保証するページネーション&lt;/strong&gt;
Connectionベースのページネーションのために提供される構造は、ページネーションの際に必要になるかもしれないもの（カーソル、追加の結果に関するメタデータ、特定の検索におけるこの特定の結果アイテムに関するメタデータなど）を論理的に配置する場所を提供してくれるため、Connectionベースのページネーションを実装する際にAPIの将来性を保証することができます。最初に説明したような高度な機能を必要としない場合でも、構造がすでにあるため必要になったときに追加するのは簡単です。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;一般化されたツール&lt;/strong&gt;
一般化されたツールを書くことが簡単になります。例えば、任意の&lt;code class=&quot;language-text&quot;&gt;${noun}Connection&lt;/code&gt;を受け取り、そのConnectionのノードのリストを返す関数などです。これにより、やや複雑なConnectionの構造を手動で扱う必要がなくなります。&lt;/p&gt;
&lt;p&gt;また、バックエンドでツールを構築するのも簡単です。先と同じことが当てはまり、標準化されているため汎用的なツールを構築することができます。例えば、上で見たテンプレートを1つの&lt;code class=&quot;language-text&quot;&gt;makeNounPaginator(...)&lt;/code&gt;という関数にすることができるので、バックエンドでページネーションを作るのも簡単です！&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;フレームワークにすべての作業を任せる&lt;/strong&gt;
しかし、フロントエンド開発者としては、標準化されていれば、フレームワークがその上に構築できるというのが一番の利点かもしれません。Relayはその素晴らしい例です。Relay のページネーションは、Connectionの仕様に従えば信じられないほど人間工学的で簡単です。&lt;a href=&quot;/posts/2020-08-29-an-introduction-to-relay-3&quot;&gt;Relayのページネーションとその優れた点について説明した記事をご紹介しますので&lt;/a&gt;、ぜひ読んでみてください。&lt;/p&gt;
&lt;h2 id=&quot;まとめ&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E3%81%BE%E3%81%A8%E3%82%81&quot; aria-label=&quot;まとめ permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;まとめ&lt;/h2&gt;
&lt;p&gt;この記事を読むことで、Connectionベースのページネーションとは何か、そしてなぜそれが意味をなすのかについての感覚が得られたことを願っています。&lt;/p&gt;
&lt;p&gt;また、上述したように、&lt;a href=&quot;/posts/2020-08-29-an-introduction-to-relay-3&quot;&gt;Relayでのページネーションに関する記事&lt;/a&gt;もぜひ読んでみてください。Relayはここで紹介した内容を活用して、ページネーションを行うための優れた開発者エクスペリエンスを実現しています。これはConnectionベースのページネーションのような標準化された構造が可能にする、まさに良い典型例です。&lt;/p&gt;
&lt;p&gt;もしあなたがEggheadの会員で、この記事を 11 分間のビデオ形式でご覧になりたい方は、Nik Graf の&lt;a href=&quot;https://egghead.io/lessons/graphql-paginate-entries-using-the-connection-specification&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Paginate Entries using the Connection Specification lesson&lt;/a&gt;をご覧ください！&lt;/p&gt;
&lt;div class=&quot;footnotes&quot;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&quot;fn-1&quot;&gt;
&lt;p&gt;訳注：ページ番号がついているようなUIのページネーションです。工夫が必要ですが、Connectionベースのページネーションを使っても実装できます 例） &lt;a href=&quot;https://artsy.github.io/blog/2020/01/21/graphql-relay-windowed-pagination/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://artsy.github.io/blog/2020/01/21/graphql-relay-windowed-pagination/&lt;/a&gt;&lt;/p&gt;
&lt;a href=&quot;#fnref-1&quot; class=&quot;footnote-backref&quot;&gt;↩&lt;/a&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-2&quot;&gt;
&lt;p&gt;訳注：例えば10個ずつ映画を取得するとして、1個目や2個目の&lt;code class=&quot;language-text&quot;&gt;Movie&lt;/code&gt;に&lt;code class=&quot;language-text&quot;&gt;hasNextPage&lt;/code&gt;があっても使いようがありません。10件の結果結果自体に次のページがあるかどうかを示すフィールドが存在するべきです。&lt;/p&gt;
&lt;a href=&quot;#fnref-2&quot; class=&quot;footnote-backref&quot;&gt;↩&lt;/a&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-3&quot;&gt;
&lt;p&gt;訳注：ここで付与したいのは検索の文脈でのみ使われる情報であり、&lt;code class=&quot;language-text&quot;&gt;Movie&lt;/code&gt;自体が持つ情報ではありません。仮に&lt;code class=&quot;language-text&quot;&gt;Movie&lt;/code&gt;を拡張すると、&lt;code class=&quot;language-text&quot;&gt;Movie&lt;/code&gt;を1つ取得する場合などでこのフィールドにどう結果を入れれば良いか分からなくなります。これが「これは特定の検索クエリに対する特定の検索結果のアイテムに対してのみ有効なデータ」ということです。&lt;/p&gt;
&lt;a href=&quot;#fnref-3&quot; class=&quot;footnote-backref&quot;&gt;↩&lt;/a&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-4&quot;&gt;
&lt;p&gt;訳注：原文はsparks joy(tm)。こんまりメソッドのときめきのことね。 &lt;a href=&quot;https://www.youtube.com/watch?v=c8eYPDmpyd4&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://www.youtube.com/watch?v=c8eYPDmpyd4&lt;/a&gt;&lt;/p&gt;
&lt;a href=&quot;#fnref-4&quot; class=&quot;footnote-backref&quot;&gt;↩&lt;/a&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-5&quot;&gt;
&lt;p&gt;訳注：現実世界では、このような安定的なリストを生成するためにはバックエンドで工夫が必要です。バックエンドで使うGraphQLフレームワークの多くは、DBに問い合わせる時のoffsetをカーソルに変換しているため安定性を備えていません。標準的な実装であるgraphql-relay-jsもそうなっており、もちろん「offsetに依存しているためにリストに変更がない場合しか動作しない」と明記されています &lt;a href=&quot;https://github.com/graphql/graphql-relay-js/blob/8f4ed1ad35805ef233ad9fd1af33abb9c0cad35a/src/connection/arrayconnection.js#L17-L19&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://github.com/graphql/graphql-relay-js/blob/8f4ed1ad35805ef233ad9fd1af33abb9c0cad35a/src/connection/arrayconnection.js#L17-L19&lt;/a&gt; 安定的なリストを作成するためにはidなどを使ってカーソルを生成する必要があります（ソートを行いながら）。ここでは詳細に記述しませんが、実装はかなり難しくなり、速度も低下する可能性があります。&lt;/p&gt;
&lt;a href=&quot;#fnref-5&quot; class=&quot;footnote-backref&quot;&gt;↩&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Relay : あなたのために泥臭い仕事をしてくれるGraphQLクライアント]]></title><description><![CDATA[Facebookが作ったGraphQLクライアントのRelayを理解する、Relay入門シリーズ。1記事目はfragmentからRelayの特徴を学びます。]]></description><link>https://kazekyo.com/posts/2020-08-27-an-introduction-to-relay-1</link><guid isPermaLink="false">https://kazekyo.com/posts/2020-08-27-an-introduction-to-relay-1</guid><pubDate>Wed, 26 Aug 2020 15:00:00 GMT</pubDate><content:encoded>&lt;p&gt;私はGraphQLクライアントのRelayのファンで、2年ほど前から使用しています。
Relayは現代のフロントエンドの開発抱える課題とGraphQLへの深い理解に裏打ちされたFacebook渾身のフレームワークですが、残念ながらその哲学は十分に理解されていなかったように思います。&lt;/p&gt;
&lt;p&gt;そして2020年。ついにRelayをうまく表現した文章がやってきました。本記事はその翻訳になります &lt;sup id=&quot;fnref-1&quot;&gt;&lt;a href=&quot;#fn-1&quot; class=&quot;footnote-ref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;。
普段は異なるGraphQLクライアントを使っている方でも、Relayの哲学はコンポーネント指向のフロントエンド開発者なら刺激的で参考になると思います。ぜひご覧ください。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://dev.to/zth/relay-the-graphql-client-that-wants-to-do-the-dirty-work-for-you-55kd&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;（原文）&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Relay入門シリーズ（全4記事）&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Relay : あなたのために泥臭い仕事をしてくれるGraphQLクライアント（本記事）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;/posts/2020-08-28-an-introduction-to-relay-2&quot;&gt;GraphQLでのConnectionベースのページネーション&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;/posts/2020-08-29-an-introduction-to-relay-3&quot;&gt;Relayを使って最小限の努力でページネーションをする&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;/posts/2020-08-30-an-introduction-to-relay-4&quot;&gt;Nodeインターフェースの魔法&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;この連載は、&lt;a href=&quot;https://github.com/zth&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Gabriel Nordeborn&lt;/a&gt;と&lt;a href=&quot;https://github.com/sgrove&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Sean Grove&lt;/a&gt;が執筆しています。GabrielはスウェーデンのITコンサルタント会社&lt;a href=&quot;https://arizon.se/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Arizon&lt;/a&gt;のフロントエンド開発者でありパートナーでもあり、長い間Relayを利用してきました。SeanはサードパーティのAPIをGraphQLで統一する&lt;a href=&quot;https://www.onegraph.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;OneGraph.com&lt;/a&gt;の共同創業者です。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;この連載では、ひとつの疑問に&lt;em&gt;明確&lt;/em&gt;に答えるために、Relayを深く掘り下げていきたいと思います。&lt;/p&gt;
&lt;p&gt;「Relay（Facebookが提供するGraphQLを使ったアプリケーションを構築するためのJavaScriptクライアントフレームワーク）になぜ私が興味を持つのか？」&lt;/p&gt;
&lt;p&gt;これは良い疑問です。その疑問に答えるために、ブログをレンダリングする簡単なページを構築するところから見ていきましょう。ページを構築していくと、2つの主要なテーマが浮かび上がってきます。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Relayは実際、あなたのために望んで泥臭い仕事をする働きものです&lt;/li&gt;
&lt;li&gt;Relayが提示する規則に従えば、GraphQLを使ってクライアントサイドのアプリケーションを構築する際に素晴らしい開発者体験を提供してくれます&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;また、Relayを使ったアプリケーションがスケーラブルで、パフォーマンスが高く、モジュール化されており、何もせずとも&lt;em&gt;デフォルト&lt;/em&gt;で変更に強く、そしては現在開発中のReactの新機能に対応した将来性のあるものになることもお見せします。&lt;/p&gt;
&lt;p&gt;Relayには（若干の）コストがかかりますが、これについては正直に前もって検討しますので、トレードオフについては十分に理解していただけます。&lt;/p&gt;
&lt;h2 id=&quot;この記事について&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E3%81%93%E3%81%AE%E8%A8%98%E4%BA%8B%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6&quot; aria-label=&quot;この記事について permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;この記事について&lt;/h2&gt;
&lt;p&gt;この記事ではRelayの考え方や哲学を紹介することを目的としています。時折、他の GraphQLフレームワークとRelayの動作を比較することはありますが、この記事は Relayと他のフレームワークを比較すること自体を目的にしていません。Relayの哲学やアプリケーション構築に関わる概念を説明し、Relayそのものについて深く掘り下げていきたいと考えています。&lt;/p&gt;
&lt;p&gt;この記事に掲載しているコードサンプル（いくつかあります！）は、あくまでもRelayの動作を説明するためのものであり、少し浅く単純化されているかもしれません。&lt;/p&gt;
&lt;p&gt;また、ReactのSuspenseとConcurrent Modeに完全対応した、Relayの新しいHooksベースのAPIにも注目します。新しいAPIはまだexperimental &lt;sup id=&quot;fnref-2&quot;&gt;&lt;a href=&quot;#fn-2&quot; class=&quot;footnote-ref&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;ですが、FacebookはRelayとデータレイヤー専用のAPIを使ってfacebook.comを再構築しています &lt;sup id=&quot;fnref-3&quot;&gt;&lt;a href=&quot;#fn-3&quot; class=&quot;footnote-ref&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;。&lt;/p&gt;
&lt;p&gt;始める前に、この記事ではGraphQLの基本的な知識とクライアントサイドのJavaScriptアプリケーションの構築について基本的な知識があることを前提としています。GraphQLについては、まだよくわかっていないという方は&lt;a href=&quot;https://graphql.org/learn/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;こちらの記事で紹介しています&lt;/a&gt;。コードサンプルはTypeScriptで書かれているので、Typescriptの基本的な理解があればそれも役立つでしょう。&lt;/p&gt;
&lt;p&gt;最後に、この記事はかなり長いです。何度も読み返すことができる参考記事として参照してください。&lt;/p&gt;
&lt;p&gt;ここまでの点をふまえて、さっそく始めましょう!&lt;/p&gt;
&lt;h1 id=&quot;relayの概要&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#relay%E3%81%AE%E6%A6%82%E8%A6%81&quot; aria-label=&quot;relayの概要 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Relayの概要&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;Relayは、GraphQLコードを最適化するコンパイラと、Reactで使用するライブラリで構成されています。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;深く飛び込む前に、まずはRelayの概要を簡単に説明しましょう。Relayは2つの部分に分けることができます。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;em&gt;コンパイラ&lt;/em&gt;：あらゆる種類の最適化、型の生成、そして優れた開発者体験を可能にする役割を担っています。Relayを使って開発している時はコンパイラはバックグラウンドで動作します。&lt;/li&gt;
&lt;li&gt;&lt;em&gt;ライブラリ&lt;/em&gt;：Relayのコアと、ReactでRelayを使用するためのバインディング。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;この時点でコンパイラについて知っておくべきことは、あなたが起動する別プロセスですべてのGraphQLのoperation &lt;sup id=&quot;fnref-4&quot;&gt;&lt;a href=&quot;#fn-4&quot; class=&quot;footnote-ref&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;を監視してコンパイルするということだけです。コンパイラの詳細についてはすぐに説明します。&lt;/p&gt;
&lt;p&gt;加えて、Relayが最適に動作するためには、スキーマが3つの規則に従うことが必要です。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;GraphQLの型のすべての&lt;code class=&quot;language-text&quot;&gt;id&lt;/code&gt;フィールドはグローバルに一意であること（つまり、異なる種類のオブジェクトであっても、2つのオブジェクトは同じ&lt;code class=&quot;language-text&quot;&gt;id&lt;/code&gt;の値を持ってはいけません）。&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Node&lt;/code&gt;インターフェースが必要です。これが意味するのは、グラフ内のオブジェクトはトップレベルの&lt;code class=&quot;language-text&quot;&gt;node&lt;/code&gt;フィールドで&lt;code class=&quot;language-text&quot;&gt;id&lt;/code&gt;フィールドを指定して取得できるべきということです。グローバルに一意な&lt;code class=&quot;language-text&quot;&gt;id&lt;/code&gt;と&lt;code class=&quot;language-text&quot;&gt;Node&lt;/code&gt;インターフェースについての詳細は、&lt;a href=&quot;/posts/2020-08-30-an-introduction-to-relay-4&quot;&gt;こちらの記事&lt;/a&gt; を参照してください。&lt;/li&gt;
&lt;li&gt;ページネーションは、コネクションベースのページネーション規則に従うべきです。コネクションベースのページネーションとは何か、なぜそれが良いアイデアなのかについては、&lt;a href=&quot;/posts/2020-08-28-an-introduction-to-relay-2&quot;&gt;こちらの記事&lt;/a&gt; を読んでください。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;この時点では規則についてはこれ以上深く掘り下げませんが、興味のある方は上記のリンク先の記事をチェックすることをお勧めします。&lt;/p&gt;
&lt;h1 id=&quot;relayの核心-fragment&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#relay%E3%81%AE%E6%A0%B8%E5%BF%83-fragment&quot; aria-label=&quot;relayの核心 fragment permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Relayの核心: fragment&lt;/h1&gt;
&lt;p&gt;まず最初に、RelayとGraphQLの統合の核となるコンセプトについてお話しましょう。それがfragmentです。fragmentは、Relay（とGraphQL！）の力を発揮するための主要なキーの一つです。&lt;/p&gt;
&lt;p&gt;簡単に言えば、GraphQLのfragmentとは、特定のGraphQLの型（type）で共通の項目をグループ化する方法のことです。以下に例を示します。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;graphql&quot;&gt;&lt;pre class=&quot;language-graphql&quot;&gt;&lt;code class=&quot;language-graphql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;fragment&lt;/span&gt; &lt;span class=&quot;token fragment function&quot;&gt;Avatar_user&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;on&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  avatarUrl
  firstName
  lastName
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;興味のある方のために: fragmentの名前に &lt;code class=&quot;language-text&quot;&gt;Avatar_user&lt;/code&gt; という名前を付けることは、Relayが強制している規則です。Relayでは、すべてのfragmentの名前はグローバルに一意で、&lt;code class=&quot;language-text&quot;&gt;&amp;lt;moduleName&amp;gt;_&amp;lt;propertyName&amp;gt;&lt;/code&gt;の構造に従うようにします。fragmentの命名規則については&lt;a href=&quot;https://relay.dev/docs/en/experimental/a-guided-tour-of-relay#fragments&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;こちら&lt;/a&gt;をご覧ください。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;これはGraphQLの型である&lt;code class=&quot;language-text&quot;&gt;User&lt;/code&gt;で使用できる&lt;code class=&quot;language-text&quot;&gt;Avatar_user&lt;/code&gt;という名前のfragmentを定義しています。このfragmentはアバターをレンダリングするのに必要なフィールドを指定します。このfragmentを使うことで、アバターのレンダリングに必要なフィールドを必要な場所ごとで明示的に指定するのではなく、query全体でこのfragmentを再利用することができます。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;graphql&quot;&gt;&lt;pre class=&quot;language-graphql&quot;&gt;&lt;code class=&quot;language-graphql&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# authorのアバターとlikedByで使用するアバーターで別々に定義する代わりに…&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;query&lt;/span&gt; BlogPostQuery&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$blogPostId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ID&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  blogPostById&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$blogPostId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    author &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      firstName
      lastName
      avatarUrl
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    likedBy&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      edges &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        node &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          firstName
          lastName
          avatarUrl
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# こうやってAvatar_userでまとめることができる&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;query&lt;/span&gt; BlogPostQuery&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$blogPostId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ID&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  blogPostById&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$blogPostId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    author &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token fragment function&quot;&gt;Avatar_user&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    likedBy&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      edges &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        node &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token fragment function&quot;&gt;Avatar_user&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;こうすると定義を再利用できるので便利ですが、より重要なのは、アプリケーションの進化に合わせてアバターをレンダリングするのに必要なフィールドを&lt;em&gt;一箇所で&lt;/em&gt;追加したり削除したりできることです。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;fragmentを使用すると、GraphQL 型のフィールドの再利用可能な指定を定義することができます。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;relayはfragmentをもっと推し進める&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#relay%E3%81%AFfragment%E3%82%92%E3%82%82%E3%81%A3%E3%81%A8%E6%8E%A8%E3%81%97%E9%80%B2%E3%82%81%E3%82%8B&quot; aria-label=&quot;relayはfragmentをもっと推し進める permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Relayはfragmentをもっと推し進める&lt;/h2&gt;
&lt;p&gt;GraphQLクライアントアプリケーションを長期的にスケールさせするためには、データをレンダリングするコンポーネントを必要なデータと一緒に配置する（コロケーション）ことが良い方法です。そうすることで、コンポーネントのメンテナンスや拡張が非常に簡単になります。&lt;/p&gt;
&lt;p&gt;fragmentでは（上記で説明したように）特定のGraphQLの型のフィールドのサブフィールド &lt;sup id=&quot;fnref-5&quot;&gt;&lt;a href=&quot;#fn-5&quot; class=&quot;footnote-ref&quot;&gt;5&lt;/a&gt;&lt;/sup&gt;を定義することができるので、コロケーションの考え方にぴったりです。&lt;/p&gt;
&lt;p&gt;コンポーネントではレンダリングする必要のあるデータを記述した1つ以上のfragmentを定義するのが良い方法です。これは、コンポーネントが「親コンポーネントが誰であるかに関係なく、User型のこれら3つのフィールドに依存している」と言うことができることを意味します。上の例では、&lt;code class=&quot;language-text&quot;&gt;&amp;lt;Avatar /&amp;gt;&lt;/code&gt;という名前のコンポーネントがあり、&lt;code class=&quot;language-text&quot;&gt;Avatar_user&lt;/code&gt; fragmentで定義されたフィールドを使ってアバターを表示します。&lt;/p&gt;
&lt;p&gt;ほとんどのフレームワークでは、何らかの方法で GraphQL fragmentを使用することができます。しかし、Relayはこれをさらに進化させています。Relayでは、&lt;em&gt;ほとんどすべてがfragmentを中心に&lt;/em&gt;展開されています。&lt;/p&gt;
&lt;h1 id=&quot;relayはどうやってgraphql-fragmentを展開するのか&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#relay%E3%81%AF%E3%81%A9%E3%81%86%E3%82%84%E3%81%A3%E3%81%A6graphql-fragment%E3%82%92%E5%B1%95%E9%96%8B%E3%81%99%E3%82%8B%E3%81%AE%E3%81%8B&quot; aria-label=&quot;relayはどうやってgraphql fragmentを展開するのか permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;RelayはどうやってGraphQL fragmentを展開するのか&lt;/h1&gt;
&lt;p&gt;根本的に、Relayはすべてのコンポーネントに対して、コンポーネントと共に全てのデータの要件を完全かつ明示的にリストアップさせたいと考えています。
これによりRelayはfragmentと深く統合することが可能になります。これが何を意味し、何を可能にするのかを分解してみましょう。&lt;/p&gt;
&lt;h2 id=&quot;コロケーションされたデータ要件とモジュール性&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E3%82%B3%E3%83%AD%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%95%E3%82%8C%E3%81%9F%E3%83%87%E3%83%BC%E3%82%BF%E8%A6%81%E4%BB%B6%E3%81%A8%E3%83%A2%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AB%E6%80%A7&quot; aria-label=&quot;コロケーションされたデータ要件とモジュール性 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;コロケーションされたデータ要件とモジュール性&lt;/h2&gt;
&lt;p&gt;Relayでは、fragmentを使用してコンポーネントのデータ要件を実際に使用しているコードのすぐそばに配置します。Relayの規則に従うことで、すべてのコンポーネントがアクセスを必要とするすべてのフィールドを明示的にリストアップすることが保証されます。つまり、明示的に要求されていないデータに依存するコンポーネントは存在しないということです。コンポーネントはモジュール化されており、自己完結型であり、再利用やリファクタリングに強くなります。&lt;/p&gt;
&lt;p&gt;Relayでは、fragmentを使ってモジュール化を可能にするためのさまざまな機能も提供していますが、それについてはこの記事の後半で少し触れます。&lt;/p&gt;
&lt;h2 id=&quot;パフォーマンス&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E3%83%91%E3%83%95%E3%82%A9%E3%83%BC%E3%83%9E%E3%83%B3%E3%82%B9&quot; aria-label=&quot;パフォーマンス permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;パフォーマンス&lt;/h2&gt;
&lt;p&gt;Relayでは、コンポーネントは使用している&lt;em&gt;フィールド&lt;/em&gt;が変更された場合にのみ再レンダリングを行います。あなたが何かをする必要はありません！ これは各&lt;em&gt;fragment&lt;/em&gt;が指定したデータの更新のみを受け取るためです。&lt;/p&gt;
&lt;p&gt;Relayはデフォルトでビューの更新方法を最適化しているため、アプリの成長に伴ってパフォーマンスが不必要に低下することはありません。これは他のGraphQLクライアントの動作とは全く異なります。以下では、これがスケーラビリティにとってどれほど重要であるかについて、いくつかの素晴らしい例を紹介します。&lt;/p&gt;
&lt;p&gt;以上のことを念頭に置いて、いよいよページの構築に取り掛かりましょう&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Relayはfragmentの概念を推し進め、データ要件のコロケーション、モジュール性、優れたパフォーマンスを可能にするために利用しています。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&quot;ブログ記事をレンダリングするページの構築&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E3%83%96%E3%83%AD%E3%82%B0%E8%A8%98%E4%BA%8B%E3%82%92%E3%83%AC%E3%83%B3%E3%83%80%E3%83%AA%E3%83%B3%E3%82%B0%E3%81%99%E3%82%8B%E3%83%9A%E3%83%BC%E3%82%B8%E3%81%AE%E6%A7%8B%E7%AF%89&quot; aria-label=&quot;ブログ記事をレンダリングするページの構築 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;ブログ記事をレンダリングするページの構築&lt;/h1&gt;
&lt;p&gt;これは、1つのブログ記事を表示するページがどのように見えるかを説明したワイヤーフレームです。
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 880px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/de94abf03f04dbb2a4dd5e847af792c9/9c177/relay-intro-1-1-4671d2fb-2b76-3d9b-8c2b-64e07934930a.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 81.66666666666667%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAQCAYAAAAWGF8bAAAACXBIWXMAAAsSAAALEgHS3X78AAABPElEQVQ4y4WUiW7DIBBEfUBt4wPs1E7apur//2UgekgjlzZII65ldvawq+r3aCIs6z5ijHARpmDnuB84S+vn4y1ijZgjaoh0tDxONgf2E+c6JvU0MKd9J0bJwRv3We3M3GFfqcJSyCMkE+uuoKbmfAJpHSpyYyEwGL5HeFR49incnfUt4g5BD+ES8VFxmAwvGBtJcg7jG5KviE8eBynevyG3ObkMB9GG0w2btpCGWTcDaE8XO+Eu0iIehbPks0joCVkvUhp+JNwbOM4EGnLycCUcC3H2GrjLefNSoNy3hu5YlL2XSu+SgiC5O04KAyQz6i6lomjIucecNLXCcj6KqKeKu7SOEelLoZKlrvBKWOPJSCs4Udi8IGxEYVcyqCGyqF7kOz9jwHYl/+EvrzVwVDj3Yu6/lTlXPf8s7ANNyAqwz+ToqQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
        &lt;source
          srcset=&quot;/static/de94abf03f04dbb2a4dd5e847af792c9/8ac56/relay-intro-1-1-4671d2fb-2b76-3d9b-8c2b-64e07934930a.webp 240w,
/static/de94abf03f04dbb2a4dd5e847af792c9/d3be9/relay-intro-1-1-4671d2fb-2b76-3d9b-8c2b-64e07934930a.webp 480w,
/static/de94abf03f04dbb2a4dd5e847af792c9/fd35f/relay-intro-1-1-4671d2fb-2b76-3d9b-8c2b-64e07934930a.webp 880w&quot;
          sizes=&quot;(max-width: 880px) 100vw, 880px&quot;
          type=&quot;image/webp&quot;
        /&gt;
        &lt;source
          srcset=&quot;/static/de94abf03f04dbb2a4dd5e847af792c9/8ff5a/relay-intro-1-1-4671d2fb-2b76-3d9b-8c2b-64e07934930a.png 240w,
/static/de94abf03f04dbb2a4dd5e847af792c9/e85cb/relay-intro-1-1-4671d2fb-2b76-3d9b-8c2b-64e07934930a.png 480w,
/static/de94abf03f04dbb2a4dd5e847af792c9/9c177/relay-intro-1-1-4671d2fb-2b76-3d9b-8c2b-64e07934930a.png 880w&quot;
          sizes=&quot;(max-width: 880px) 100vw, 880px&quot;
          type=&quot;image/png&quot;
        /&gt;
        &lt;img
          class=&quot;gatsby-resp-image-image&quot;
          src=&quot;/static/de94abf03f04dbb2a4dd5e847af792c9/9c177/relay-intro-1-1-4671d2fb-2b76-3d9b-8c2b-64e07934930a.png&quot;
          alt=&quot;画像1.png&quot;
          title=&quot;画像1.png&quot;
          loading=&quot;lazy&quot;
          style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        /&gt;
      &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;
まず、このビューのすべてのデータを1つのトップレベルのqueryで取得する方法を考えてみましょう。ワイヤーフレームのニーズを満たすための合理的なqueryは、次のようになります。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;graphql&quot;&gt;&lt;pre class=&quot;language-graphql&quot;&gt;&lt;code class=&quot;language-graphql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;query&lt;/span&gt; BlogPostQuery&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$blogPostId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ID&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  blogPostById&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$blogPostId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    author &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      firstName
      lastName
      avatarUrl
      shortBio
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    title
    coverImgUrl
    createdAt
    tags &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      slug
      shortName
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    body
    likedByMe
    likedBy&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      totalCount
      edges &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        node &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          firstName
          lastName
          avatarUrl
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;1つのqueryで必要なデータをすべて取得することができます！ 良いですね！&lt;/p&gt;
&lt;p&gt;そして、UIコンポーネントの構造は以下のようになるかもしれません。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;BlogPost&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;BlogPostHeader&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;BlogPostAuthor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;Avatar&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;BlogPostAuthor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;BlogPostHeader&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;BlogPostBody&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;BlogPostTitle&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;BlogPostMeta&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;CreatedAtDisplayer&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;TagsDisplayer&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;BlogPostMeta&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;BlogPostContent&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;LikeButton&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;LikedByDisplayer&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;LikeButton&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;BlogPostBody&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;BlogPost&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;これをRelayで構築する方法を見てみましょう。&lt;/p&gt;
&lt;h2 id=&quot;relayでのデータの問い合わせ&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#relay%E3%81%A7%E3%81%AE%E3%83%87%E3%83%BC%E3%82%BF%E3%81%AE%E5%95%8F%E3%81%84%E5%90%88%E3%82%8F%E3%81%9B&quot; aria-label=&quot;relayでのデータの問い合わせ permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Relayでのデータの問い合わせ&lt;/h2&gt;
&lt;p&gt;Relayでは、ブログ記事をレンダリングするルートコンポーネントは通常以下のように書きます。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// BlogPost.ts&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; React &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;react&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; useLazyLoadQuery &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;react-relay/hooks&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; graphql &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;react-relay&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; BlogPostQuery &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;./__generated__/BlogPostQuery.graphql&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; BlogPostHeader &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;./BlogPostHeader&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; BlogPostBody &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;./BlogPostBody&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Props&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  blogPostId&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;BlogPost&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; blogPostId &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Props&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; blogPostById &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token generic-function&quot;&gt;&lt;span class=&quot;token function&quot;&gt;useLazyLoadQuery&lt;/span&gt;&lt;span class=&quot;token generic class-name&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;BlogPostQuery&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    graphql&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;
      query BlogPostQuery($blogPostId: ID!) {
        blogPostById(id: $blogPostId) {
          ...BlogPostHeader_blogPost
          ...BlogPostBody_blogPost
        }
      }
    &lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      variables&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; blogPostId &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;blogPostById&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;BlogPostHeader&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;blogPost&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;blogPostById&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;BlogPostBody&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;blogPost&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;blogPostById&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;ここで何が起こっているのか、一歩一歩分解してみましょう。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; blogPostById &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token generic-function&quot;&gt;&lt;span class=&quot;token function&quot;&gt;useLazyLoadQuery&lt;/span&gt;&lt;span class=&quot;token generic class-name&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;BlogPostQuery&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    graphql&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;
      query BlogPostQuery($blogPostId: ID!) {
        blogPostById(id: $blogPostId) {
          ...BlogPostHeader_blogPost
          ...BlogPostBody_blogPost
        }
      }
    &lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      variables&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; blogPostId &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;まず注意したいのは、次のようにReactのhookである&lt;code class=&quot;language-text&quot;&gt;useLazyLoadQuery&lt;/code&gt;をRelayから利用することです。
&lt;code class=&quot;language-text&quot;&gt;const { blogPostById } = useLazyLoadQuery&amp;lt;BlogPostQuery&amp;gt;&lt;/code&gt;
&lt;code class=&quot;language-text&quot;&gt;useLazyLoadQuery&lt;/code&gt;は、コンポーネントがレンダリングされるとすぐに&lt;code class=&quot;language-text&quot;&gt;BlogPostQuery&lt;/code&gt;の取得を開始します。&lt;/p&gt;
&lt;p&gt;型の安全性を確保するために、&lt;code class=&quot;language-text&quot;&gt;useLazyLoadQuery&lt;/code&gt;に型を指定します。&lt;code class=&quot;language-text&quot;&gt;./__generated__/BlogPostQuery.graphql&lt;/code&gt;からインポートした&lt;code class=&quot;language-text&quot;&gt;BlogPostQuery&lt;/code&gt;という型を明示的に指定しています。このファイルはRelayコンパイラによって&lt;em&gt;自動的&lt;/em&gt;に生成され（queryの定義を変更すると同期して保存されます）、queryに必要なすべての型情報（戻ってくるデータがどのように見えるか、queryが必要とする変数は何かなど）を持っています。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;留意点！：前述したように、&lt;code class=&quot;language-text&quot;&gt;useLazyLoadQuery&lt;/code&gt;は、queryがレンダリングされるとすぐにqueryの取得を開始します。しかし、Relayは実際には、このようにレンダリング時にデータを遅延ロードで取得することを望んでいません。むしろ、ページのレンダリングと同時にではなく、ユーザーが新しいページへのリンクをクリックしたときなど、できるだけ早くqueryの読み込みを開始してほしいと考えています。なぜこれが重要なのかについては、こちらの&lt;a href=&quot;https://ja.reactjs.org/docs/concurrent-mode-suspense.html#approach-3-render-as-you-fetch-using-suspense&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;ブログ記事&lt;/a&gt;や&lt;a href=&quot;https://www.youtube.com/watch?v=Tl0S7QkxFE4&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;トーク&lt;/a&gt;で詳しく説明しています。
この記事では、ほとんどの人にとってより身近なメンタルモデルであることと、できるだけシンプルで簡単に理解できるようにするために、まだ遅延ロードを使用しています。しかし、前述したようにこれは実際にRelayを使って構築する際にqueryでデータを取得する方法ではないことに注意してください。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;次に、実際のqueryを見てみましょう。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;    graphql&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;
      query BlogPostQuery($blogPostId: ID!) {
        blogPostById(id: $blogPostId) {
          ...BlogPostHeader_blogPost
          ...BlogPostBody_blogPost
        }
      }
    &lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;このqueryにはほとんど解説する部分はありません。queryにはブログ記事をIDで指定する以外に、あと2つの項目があります。&lt;code class=&quot;language-text&quot;&gt;BlogPost&lt;/code&gt;の&lt;code class=&quot;language-text&quot;&gt;&amp;lt;BlogPostHeader /&amp;gt;&lt;/code&gt;と&lt;code class=&quot;language-text&quot;&gt;&amp;lt;BlogPostBody /&amp;gt;&lt;/code&gt;のfragmentです。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;使用しているfragmentをインポートする必要がないことに注意してください。これらはRelayコンパイラによって自動的にインポートされます。これについては後ほど詳しく説明します。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;このようにfragmentを組み合わせてqueryを構築することは非常に重要です。別のアプローチとしては、コンポーネントごとに独自のqueryを定義させて、独自にデータを取得する責任を持たせるという方法もあります。これにはいくつかの有効なユースケースがありますが、2つの大きな問題があります。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;1つのqueryではなく、大量のqueryがサーバに送られてきます。&lt;/li&gt;
&lt;li&gt;queryを作成する各コンポーネントは、実際にレンダリングされてからデータの取得を開始するまで待たなければなりません。つまり、リクエストはウォーターフォールで行われるため、ビューの読み込みが必要以上に遅くなる可能性が高いということです。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;Relayでは、コンポーネントを組み合わせてUIを構築します。これらのコンポーネントでは、他のコンポーネントがどのようなデータを必要としているのか見えないように定義します。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;relayでモジュール性をどのように実現するか&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#relay%E3%81%A7%E3%83%A2%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AB%E6%80%A7%E3%82%92%E3%81%A9%E3%81%AE%E3%82%88%E3%81%86%E3%81%AB%E5%AE%9F%E7%8F%BE%E3%81%99%E3%82%8B%E3%81%8B&quot; aria-label=&quot;relayでモジュール性をどのように実現するか permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Relayでモジュール性をどのように実現するか&lt;/h2&gt;
&lt;p&gt;上のコードで覚えておくべきメンタルモデルは以下の通りです。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;BlogPost&lt;/code&gt;コンポーネントは2つの子コンポーネント、&lt;code class=&quot;language-text&quot;&gt;BlogPostHeader&lt;/code&gt;と&lt;code class=&quot;language-text&quot;&gt;BlogPostBody&lt;/code&gt;をレンダリングしたいことだけを知っています。2つのコンポーネントがどのようなデータを必要としているのかは知りません（親コンポーネントが知る必要はありませんよね。それは子コンポーネントのやるべきことです！）。
代わりに、必要なすべてのデータはGraphQLの&lt;code class=&quot;language-text&quot;&gt;BlogPost&lt;/code&gt;型の&lt;code class=&quot;language-text&quot;&gt;BlogPostHeader_blogPost&lt;/code&gt;および&lt;code class=&quot;language-text&quot;&gt;BlogPostBody_blogPost&lt;/code&gt;と呼ばれるfragmentに含まれていると子コンポーネントは教えてくれます。これらのfragmentをqueryに含める限り、詳細がわからなくても、必要なデータを確実に取得できることがわかっています。そして、必要なデータを手に入れたら、それらをレンダリングすることができます。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;私たちは、独自のデータ要件を定義するコンポーネントを&lt;em&gt;独立して&lt;/em&gt;構成することで UI を構築します。これらのコンポーネントは、独自のデータ要件を持つ他のコンポーネントと一緒に構成することができます。
しかし、どのコンポーネントも他のコンポーネントがどのようなデータを必要としているかについては、そのコンポーネントが&lt;em&gt;どのGraphQLのデータソース（型）&lt;/em&gt;からデータを必要としているか以外は何も知りません。Relayは、泥臭い作業を処理して、適切なコンポーネントが適切なデータを取得するようにしたり、サーバに送信されるqueryで必要なデータが一括ですべて指定さるようにします。&lt;/p&gt;
&lt;p&gt;これにより、開発者は&lt;em&gt;コンポーネント&lt;/em&gt;と&lt;em&gt;fragment&lt;/em&gt;を独立して考えることができ、Relayが全てを繋ぎ合わせる作業を担当してくれます。&lt;/p&gt;
&lt;p&gt;それでは次のステップへ行きましょう！&lt;/p&gt;
&lt;h2 id=&quot;relayコンパイラは、あなたのプロジェクトで定義したすべてのgraphqlコードを把握している&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#relay%E3%82%B3%E3%83%B3%E3%83%91%E3%82%A4%E3%83%A9%E3%81%AF%E3%80%81%E3%81%82%E3%81%AA%E3%81%9F%E3%81%AE%E3%83%97%E3%83%AD%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%81%A7%E5%AE%9A%E7%BE%A9%E3%81%97%E3%81%9F%E3%81%99%E3%81%B9%E3%81%A6%E3%81%AEgraphql%E3%82%B3%E3%83%BC%E3%83%89%E3%82%92%E6%8A%8A%E6%8F%A1%E3%81%97%E3%81%A6%E3%81%84%E3%82%8B&quot; aria-label=&quot;relayコンパイラは、あなたのプロジェクトで定義したすべてのgraphqlコードを把握している permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Relayコンパイラは、あなたのプロジェクトで定義したすべてのGraphQLコードを把握している&lt;/h2&gt;
&lt;p&gt;先ほどのqueryでは2つのfragmentを参照していましたが、それらのfragmentがどのファイルのどこで定義されているかを指定したり、queryに手動でインポートしたりする必要はありませんでした。これは、Relayでは各fragmentに&lt;em&gt;グローバルに一意な&lt;/em&gt;名前を付与しているため、Relayコンパイラはそれらを見つけてサーバに送信するqueryにfragmentの定義を&lt;em&gt;自動的に&lt;/em&gt;含めることができるからです。&lt;/p&gt;
&lt;p&gt;手作業でfragmentの定義を参照することは、不便で、手動で、エラーが発生しやすい作業ですが、Relayでは開発者の責任ではありません。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;コンポーネントと密接に結合されたfragmentを使用することで、Relayはコンポーネントのデータ要件を外部から隠すことができ、モジュール性と安全なリファクタリングが可能になります。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;最後に、結果をレンダリングする部分を見てみましょう。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// 両方のフラグメントをblogPostByIdに展開させているので、`BlogPostHeader` と `BlogPostBody` の両方のコンポーネントの要件を満たすことが保証されています。&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;blogPostById&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;BlogPostHeader&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;blogPost&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;blogPostById&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;BlogPostBody&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;blogPost&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;blogPostById&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;ここでは、&lt;code class=&quot;language-text&quot;&gt;&amp;lt;BlogPostHeader /&amp;gt;&lt;/code&gt;と&lt;code class=&quot;language-text&quot;&gt;&amp;lt;BlogPostBody /&amp;gt;&lt;/code&gt;をレンダリングしています。よく見ると、両方とも &lt;code class=&quot;language-text&quot;&gt;blogPostById&lt;/code&gt;オブジェクトを渡してレンダリングしていることがわかるかもしれません。これは、fragmentを展開した &lt;sup id=&quot;fnref-6&quot;&gt;&lt;a href=&quot;#fn-6&quot; class=&quot;footnote-ref&quot;&gt;6&lt;/a&gt;&lt;/sup&gt; query内のオブジェクトです。これがRelayでのfragmentデータの転送方法です。fragmentを使用して、コンポーネントにfragmentを展開したオジェクトを渡し、コンポーネントはそれを使って実際のfragmentデータを取得します。
心配しないでください、投げっぱなしにはしません。型システムを通じて、Relayは&lt;em&gt;正しいfragment&lt;/em&gt;が展開された&lt;em&gt;正しいオブジェクト&lt;/em&gt;を確実に渡します。これについてはもう少し詳しく説明します。&lt;/p&gt;
&lt;p&gt;ふー、新しいことがいくつかありましたね。しかし、これまでに見てきたところでは、Relayが私たちを助けるために行っていることの多くを紹介してきましたが、これらのメリットを受けるためには通常は手動で行わなければならないことばかりです。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Relayの規則に従うことで、要求されたデータを持っていないとコンポーネントを&lt;strong&gt;レンダリングすることができません&lt;/strong&gt;。つまり、壊れたコードをプロダクションに出荷するのに苦労することになります。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;それでは、コンポーネントのツリーを下に移動していきましょう。&lt;/p&gt;
&lt;h2 id=&quot;fragmentを使ったコンポーネントの構築&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#fragment%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%9F%E3%82%B3%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%8D%E3%83%B3%E3%83%88%E3%81%AE%E6%A7%8B%E7%AF%89&quot; aria-label=&quot;fragmentを使ったコンポーネントの構築 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;fragmentを使ったコンポーネントの構築&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;&amp;lt;BlogPostHeader&amp;gt;&lt;/code&gt;のコードを示します。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// BlogPostHeader.ts&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; React &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;react&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; useFragment &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;react-relay/hooks&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; graphql &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;react-relay&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; 
  BlogPostHeader_blogPost$key&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  BlogPostHeader_blogPost
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;./__generated__/BlogPostHeader_blogPost.graphql&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; BlogPostAuthor &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;./BlogPostAuthor&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; BlogPostLikeControls &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;./BlogPostLikeControls&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Props&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  blogPost&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; BlogPostHeader_blogPost$key&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;BlogPostHeader&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; blogPost &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Props&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; blogPostData &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token generic-function&quot;&gt;&lt;span class=&quot;token function&quot;&gt;useFragment&lt;/span&gt;&lt;span class=&quot;token generic class-name&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;BlogPostHeader_blogPost&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    graphql&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;
      fragment BlogPostHeader_blogPost on BlogPost {
        title
        coverImgUrl
        ...BlogPostAuthor_blogPost
        ...BlogPostLikeControls_blogPost
      }
    &lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    blogPost&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;blogPostData&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;coverImgUrl&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;blogPostData&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;BlogPostAuthor&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;blogPost&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;blogPostData&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;BlogPostLikeControls&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;blogPost&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;blogPostData&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;ここでの例では、1つのコンポーネントにつき1つのfragmentしか定義していませんが、1つのコンポーネントは、同じタイプの複数のfragmentを含む任意の数の GraphQLの型で任意の数のfragmentを定義することができます。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;分解してみましょう。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; 
  BlogPostHeader_blogPost$key&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  BlogPostHeader_blogPost
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;./__generated__/BlogPostHeader_blogPost.graphql&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Relay コンパイラが自動生成した &lt;code class=&quot;language-text&quot;&gt;BlogPostHeader_blogPost.graphql&lt;/code&gt;ファイルから2つの型定義をインポートします。&lt;/p&gt;
&lt;p&gt;Relayコンパイラはファイル &lt;sup id=&quot;fnref-7&quot;&gt;&lt;a href=&quot;#fn-7&quot; class=&quot;footnote-ref&quot;&gt;7&lt;/a&gt;&lt;/sup&gt;からGraphQL fragmentコードを抽出し、型定義を生成します。プロジェクトで作成してRelayで使用するすべてのGraphQLコード（query、mutation、subscription、fragment）に対してこの処理を行います。これは、fragmentの定義が変更されてもコンパイラが自動的に型を同期させることを意味します。&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;BlogPostHeader_blogPost&lt;/code&gt;にはfragmentの型定義が含まれており、それを&lt;code class=&quot;language-text&quot;&gt;useFragment&lt;/code&gt; (後ほど詳しく説明します)に渡すことで、fragmentのデータとの対話が型安全になるようにしています。&lt;/p&gt;
&lt;p&gt;しかし、12行目の&lt;code class=&quot;language-text&quot;&gt;interface Props { … }&lt;/code&gt;にある&lt;code class=&quot;language-text&quot;&gt;BlogPostHeader_blogPost$key&lt;/code&gt;は一体何なのでしょうか？まあ、それは型の安全性に関係しています。本当に今は気にする必要はありませんが、好奇心旺盛な方のためにとりあえず分解してみましょう（そうでない方は次の見出しに飛ばしてください）。&lt;/p&gt;
&lt;p&gt;この型定義は、型の黒魔法を使って、正しいオブジェクト（&lt;code class=&quot;language-text&quot;&gt;BlogPostHeader_blogPost&lt;/code&gt;fragmentが展開されるべき場所）だけを&lt;code class=&quot;language-text&quot;&gt;useFragment&lt;/code&gt;に渡すことができるようにし、間違っている場合はビルド時にエラーを発生させます（エラーをエディタで確認できます！）。ご覧のように、&lt;code class=&quot;language-text&quot;&gt;props&lt;/code&gt;から&lt;code class=&quot;language-text&quot;&gt;blogPost&lt;/code&gt;を受け取り&lt;code class=&quot;language-text&quot;&gt;useFragment&lt;/code&gt;の2番目の引数として渡しています。そして、もし&lt;code class=&quot;language-text&quot;&gt;blogPost&lt;/code&gt;が正しいfragment(&lt;code class=&quot;language-text&quot;&gt;BlogPostHeader_blogPost&lt;/code&gt;)を展開していない場合、型エラーが発生します。&lt;/p&gt;
&lt;p&gt;全く同じデータを指定した別々のfragmentが同時にそのオブジェクトに展開されていても問題ありません。Relayは&lt;code class=&quot;language-text&quot;&gt;useFragment&lt;/code&gt;を使って、使用したいfragmentが正確に正しいかどうかを確認してくれます。これは重要なことです。他のコンポーネントに暗黙のうちに影響を受けることなく、fragmentの定義を変更できることをRelayが保証する方法の一つだからです。&lt;/p&gt;
&lt;p&gt;Relayは&lt;em&gt;正しい&lt;/em&gt;フラグメントを含んでいる&lt;em&gt;正しい&lt;/em&gt;オブジェクトを渡すことで、間違ったデータソースを参照するようなの潜在的なエラーの原因を排除します。&lt;/p&gt;
&lt;h2 id=&quot;明示的に指定したデータだけが使用することができる&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E6%98%8E%E7%A4%BA%E7%9A%84%E3%81%AB%E6%8C%87%E5%AE%9A%E3%81%97%E3%81%9F%E3%83%87%E3%83%BC%E3%82%BF%E3%81%A0%E3%81%91%E3%81%8C%E4%BD%BF%E7%94%A8%E3%81%99%E3%82%8B%E3%81%93%E3%81%A8%E3%81%8C%E3%81%A7%E3%81%8D%E3%82%8B&quot; aria-label=&quot;明示的に指定したデータだけが使用することができる permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;明示的に指定したデータだけが使用することができる&lt;/h2&gt;
&lt;p&gt;fragmentとして&lt;code class=&quot;language-text&quot;&gt;BlogPostHeader_blogPost&lt;/code&gt;を&lt;code class=&quot;language-text&quot;&gt;BlogPost&lt;/code&gt;型で定義しました。このコンポーネントには2つのフィールドを明示的に指定しています。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;coverImgUrl&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;これは、&lt;em&gt;このコンポーネントでこれらのフィールドを使用しているから&lt;/em&gt;です。このことは、Relayの重要な機能のひとつであるデータのマスキングを強調しています。次の展開したfragment &lt;sup id=&quot;fnref-8&quot;&gt;&lt;a href=&quot;#fn-8&quot; class=&quot;footnote-ref&quot;&gt;8&lt;/a&gt;&lt;/sup&gt;である&lt;code class=&quot;language-text&quot;&gt;BlogPostAuthor_blogPost&lt;/code&gt;が&lt;code class=&quot;language-text&quot;&gt;title&lt;/code&gt;と&lt;code class=&quot;language-text&quot;&gt;coverImgUrl&lt;/code&gt;を指定していたとしても 、自分のfragmentで明示的に要求しない限り、これらのフィールドにアクセスすることはできません（つまり、フィールドは取得の定義を書いた場所でのみ使用されなければなりません）。&lt;/p&gt;
&lt;p&gt;これは型レベル（生成された型に明示的に指定されなかったフィールドは含まれません）でも実行時にも適用されます 。仮に型システムをバイパスしても、値は存在しません。&lt;/p&gt;
&lt;p&gt;最初は少し奇妙に感じるかもしれませんが、これもRelayの安全機構のひとつです。他のコンポーネントが指定したデータに暗黙的に依存することが不可能であることがわかっていれば、他のコンポーネントが奇妙で予期せぬ方法で壊れるリスクを冒すことなく、コンポーネントのリファクタリングを行うことができます。これはアプリが成長していく上で非常に有効です。また、すべてのコンポーネントとそのデータ要件が完全に自己完結するようになります。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;コンポーネントが必要とするすべてのデータが明示的に定義されていることは、他のコンポーネントが依存していたqueryやfragmentからフィールドの指定を削除することで、誤ってUIを壊してしまうことがないことを意味します。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; blogPostData &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token generic-function&quot;&gt;&lt;span class=&quot;token function&quot;&gt;useFragment&lt;/span&gt;&lt;span class=&quot;token generic class-name&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;BlogPostHeader_blogPost&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    graphql&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;
      fragment BlogPostHeader_blogPost on BlogPost {
        title
        coverImgUrl
        ...BlogPostAuthor_blogPost
        ...BlogPostLikeControls_blogPost
      }
    &lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    blogPost&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;ここでは、React hookの&lt;code class=&quot;language-text&quot;&gt;useFragment&lt;/code&gt;を使ってfragmentのデータを取得しています。&lt;code class=&quot;language-text&quot;&gt;useFragment&lt;/code&gt;は、&lt;em&gt;フラグメントの定義&lt;/em&gt;（&lt;code class=&quot;language-text&quot;&gt;graphql&lt;/code&gt;タグの中で定義されたもの）と、&lt;em&gt;そのフラグメントが展開されたオブジェクト&lt;/em&gt;（ここでは&lt;code class=&quot;language-text&quot;&gt;props&lt;/code&gt;からきた&lt;code class=&quot;language-text&quot;&gt;blogPost&lt;/code&gt;）を引数から理解し、それを使ってこの特定のフラグメントのデータを取得します。&lt;/p&gt;
&lt;p&gt;再度確認しますが、このfragmentのデータ(&lt;code class=&quot;language-text&quot;&gt;title&lt;/code&gt;/&lt;code class=&quot;language-text&quot;&gt;coverImgUrl&lt;/code&gt;)は、&lt;code class=&quot;language-text&quot;&gt;props&lt;/code&gt;からきた&lt;code class=&quot;language-text&quot;&gt;blogPost&lt;/code&gt;からは直接取得できません。 データは、fragmentの定義とfragmentが展開されたオブジェクトである&lt;code class=&quot;language-text&quot;&gt;blogPost&lt;/code&gt;を用いて、&lt;code class=&quot;language-text&quot;&gt;useFragment&lt;/code&gt;を呼び出すことでのみ取得できます。&lt;/p&gt;
&lt;p&gt;そして、先ほどと同じように、レンダリングしたいコンポーネントのfragmentを展開します。この場合、&lt;code class=&quot;language-text&quot;&gt;BlogPostAuthor_blogPost&lt;/code&gt;と&lt;code class=&quot;language-text&quot;&gt;BlogPostLikeControls_blogPost&lt;/code&gt;です。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;好奇心のある方へ：fragmentはどのフィールドを指定するかを記述するだけなので、&lt;code class=&quot;language-text&quot;&gt;useFragment&lt;/code&gt;は実際にデータをGraphQLのAPIにリクエストすることはありません。fragmentのデータを取得するためには、どこかの時点でquery (または他のGraphQL operation) を実行しなければなりません。しかし、Relayには非常にクールな機能があり、fragmentのデータをRelay自身が再取得できるようになっています。これは、特定の GraphQL オブジェクトを&lt;code class=&quot;language-text&quot;&gt;id&lt;/code&gt;で再取得するためのqueryをRelayが&lt;em&gt;自動的に生成&lt;/em&gt;してくれるからです。話は脱線してしまいましたが…。&lt;/p&gt;
&lt;p&gt;また、Redux をご存知であれば、&lt;code class=&quot;language-text&quot;&gt;useFragment&lt;/code&gt;をステートツリーから必要なものだけを取得するselectorに例えることもできます。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;blogPostData&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;coverImgUrl&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;blogPostData&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;BlogPostAuthor&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;blogPost&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;blogPostData&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;BlogPostLikeControls&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;blogPost&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;blogPostData&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;明示的に要求したデータ(&lt;code class=&quot;language-text&quot;&gt;coverImgUrl&lt;/code&gt;と&lt;code class=&quot;language-text&quot;&gt;title&lt;/code&gt;)をレンダリングし、2つの子コンポーネントにデータを渡して、子コンポーネントがレンダリングできるようにします。fragmentを展開するコンポーネントにオブジェクトを渡すことに注意してください。このオブジェクトはfragmentのルートである&lt;code class=&quot;language-text&quot;&gt;BlogPostHeader_blogPost&lt;/code&gt;で定義され使用されます。&lt;/p&gt;
&lt;h2 id=&quot;どうやってrelayはパフォーマンスを維持するのか&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E3%81%A9%E3%81%86%E3%82%84%E3%81%A3%E3%81%A6relay%E3%81%AF%E3%83%91%E3%83%95%E3%82%A9%E3%83%BC%E3%83%9E%E3%83%B3%E3%82%B9%E3%82%92%E7%B6%AD%E6%8C%81%E3%81%99%E3%82%8B%E3%81%AE%E3%81%8B&quot; aria-label=&quot;どうやってrelayはパフォーマンスを維持するのか permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;どうやってRelayはパフォーマンスを維持するのか&lt;/h2&gt;
&lt;p&gt;fragmentを使用する場合、各fragmentは実際に使用しているデータの更新のみをsubscribeします。つまり、上記の&lt;code class=&quot;language-text&quot;&gt;&amp;lt;BlogPostHeader /&amp;gt;&lt;/code&gt;コンポーネントは、レンダリングしている特定のブログ記事の&lt;code class=&quot;language-text&quot;&gt;coverImgUrl&lt;/code&gt;や&lt;code class=&quot;language-text&quot;&gt;title&lt;/code&gt;が更新された場合にのみ、自分自身で再レンダリングを行います。もし&lt;code class=&quot;language-text&quot;&gt;BlogPostAuthor_blogPost&lt;/code&gt;が他のフィールドを指定し、それらのフィールドが更新された場合でも、このコンポーネントは再レンダリングされません。データの変更は&lt;em&gt;fragmentレベル&lt;/em&gt;でsubscribeされます。&lt;/p&gt;
&lt;p&gt;これは少し混乱するように聞こえるかもしれませんし、最初はそれほど便利ではないかもしれませんが、パフォーマンスのためには非常に重要なことです。ここでは、クライアントでGraphQLデータを扱う場合の一般的な方法と対比させて、この点について詳しく見ていきましょう。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Relayでは、データが更新されると、更新されたデータを使用しているコンポーネントのみが再レンダリングされます。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;ビューで使うデータはどこから来るのか？-他のフレームワークとの比較&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E3%83%93%E3%83%A5%E3%83%BC%E3%81%A7%E4%BD%BF%E3%81%86%E3%83%87%E3%83%BC%E3%82%BF%E3%81%AF%E3%81%A9%E3%81%93%E3%81%8B%E3%82%89%E6%9D%A5%E3%82%8B%E3%81%AE%E3%81%8B%EF%BC%9F-%E4%BB%96%E3%81%AE%E3%83%95%E3%83%AC%E3%83%BC%E3%83%A0%E3%83%AF%E3%83%BC%E3%82%AF%E3%81%A8%E3%81%AE%E6%AF%94%E8%BC%83&quot; aria-label=&quot;ビューで使うデータはどこから来るのか？ 他のフレームワークとの比較 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;ビューで使うデータはどこから来るのか？ 他のフレームワークとの比較&lt;/h2&gt;
&lt;p&gt;ビューで使用するすべてのデータは、queryのようにサーバーから実際にデータを取得する操作に由来していなければなりません。queryを定義し、フレームワークにサーバーからそれを取得させ、必要なデータを渡して、ビュー内で必要なコンポーネントをレンダリングします。ほとんどの GraphQLフレームワークでは、データのソースは&lt;em&gt;queryです&lt;/em&gt;。データはqueryからコンポーネントへと流れていきます。他の GraphQL フレームワークで一般的にどのように行われているかの例を示します（矢印はデータがどのように流れるかを表しています）。
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 880px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/9c5c62fc07d098b02448bb44c0e9ac87/9c177/relay-intro-1-2-1d62333b-6392-849b-2e12-5ce35b3883f7.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 57.50000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAIAAADtbgqsAAAACXBIWXMAAAsSAAALEgHS3X78AAABbklEQVQoz3WSW0/CQBCF+f8/wxcTNVEfSYyRIFCJgEjCg63l0vQ22wvtbu8XPBRNfFjmYbKZzLfnzOz2tn4wt9yV6aR5zuPY9/0kSbIsQ47jmHN+zkdZ9AYmDd1g7IZv2oYRgTEMw3Vdx3FM09R1HWSe503TSOCH+epx9tlfq09fOk8Sz/O4OKmFYRgEAbIQIk3Ttm0l8IvtT9nhlcKZRY5tu0RohW1owgLIs7Lc9tIixQmmJlmMBd3AVVWBAQ8MGU0oym1nIt5t1sbuuyyrsiyBQYd3URQFbMMwKnLbOZscxXvtjyKml2UNGJcQEbZ1OBywAjRdhI3V7Wp0pS9uov0zHkiIk8PTnEVxdoEmmJfD28X1Znm//bgjtZ/naRRFWLWmaZPJBDuDOJxfhAt32Abjmgbc06q6xQOhG3tWFEVVVcuyqNu/HE5FmEVGxqlpWhjGkIwx/LP9fm/bNnVx8Yf9O5/uhkLTxW/pL6TwD3J9qx9TUZHjAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
        &lt;source
          srcset=&quot;/static/9c5c62fc07d098b02448bb44c0e9ac87/8ac56/relay-intro-1-2-1d62333b-6392-849b-2e12-5ce35b3883f7.webp 240w,
/static/9c5c62fc07d098b02448bb44c0e9ac87/d3be9/relay-intro-1-2-1d62333b-6392-849b-2e12-5ce35b3883f7.webp 480w,
/static/9c5c62fc07d098b02448bb44c0e9ac87/fd35f/relay-intro-1-2-1d62333b-6392-849b-2e12-5ce35b3883f7.webp 880w&quot;
          sizes=&quot;(max-width: 880px) 100vw, 880px&quot;
          type=&quot;image/webp&quot;
        /&gt;
        &lt;source
          srcset=&quot;/static/9c5c62fc07d098b02448bb44c0e9ac87/8ff5a/relay-intro-1-2-1d62333b-6392-849b-2e12-5ce35b3883f7.png 240w,
/static/9c5c62fc07d098b02448bb44c0e9ac87/e85cb/relay-intro-1-2-1d62333b-6392-849b-2e12-5ce35b3883f7.png 480w,
/static/9c5c62fc07d098b02448bb44c0e9ac87/9c177/relay-intro-1-2-1d62333b-6392-849b-2e12-5ce35b3883f7.png 880w&quot;
          sizes=&quot;(max-width: 880px) 100vw, 880px&quot;
          type=&quot;image/png&quot;
        /&gt;
        &lt;img
          class=&quot;gatsby-resp-image-image&quot;
          src=&quot;/static/9c5c62fc07d098b02448bb44c0e9ac87/9c177/relay-intro-1-2-1d62333b-6392-849b-2e12-5ce35b3883f7.png&quot;
          alt=&quot;画像2.png&quot;
          title=&quot;画像2.png&quot;
          loading=&quot;lazy&quot;
          style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        /&gt;
      &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;注: フレームワークのデータストアは、多くのフレームワークではキャッシュと呼ばれています。この記事では、フレームワークのデータストア === キャッシュと仮定します。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;フローは以下のようになります。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;&amp;lt;Profile /&amp;gt;&lt;/code&gt;が&lt;code class=&quot;language-text&quot;&gt;query ProfileQuery&lt;/code&gt;を作成し、GraphQL APIにリクエストを発行します。&lt;/li&gt;
&lt;li&gt;レスポンスはフレームワーク固有のデータストア (キャッシュ) に何らかの方法で保存されます。&lt;/li&gt;
&lt;li&gt;データはレンダリングのためにビューに届けられます。&lt;/li&gt;
&lt;li&gt;ビューはその後、データの一部を必要とする子孫コンポーネント（&lt;code class=&quot;language-text&quot;&gt;Avatar&lt;/code&gt;、&lt;code class=&quot;language-text&quot;&gt;Name&lt;/code&gt;、&lt;code class=&quot;language-text&quot;&gt;Bio&lt;/code&gt;など）に渡し続けます。最後に、あなたのビューはレンダリングされます。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;relayはどのように行うのか&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#relay%E3%81%AF%E3%81%A9%E3%81%AE%E3%82%88%E3%81%86%E3%81%AB%E8%A1%8C%E3%81%86%E3%81%AE%E3%81%8B&quot; aria-label=&quot;relayはどのように行うのか permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Relayはどのように行うのか&lt;/h2&gt;
&lt;p&gt;さて、Relayの場合はこれとは全く異なります。Relayの場合はイラストがどうなっているのか見てみましょう。
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 880px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/a84f87412aa9d882799f2cb3aefeaad0/9c177/relay-intro-1-3-b9174b65-6b1c-f501-2607-7944455087d3.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 57.50000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAIAAADtbgqsAAAACXBIWXMAAAsSAAALEgHS3X78AAABaElEQVQoz3WSSU/DMBCF+f8/gxNClYATEgiJqlUTtpZSIZaEJMTZbGd3trZJyqNR4ZLMYWRL88174/GR4Ydzh77YXlFVIk2DIMjzvCxL5DRNhRBd3vXF0dhmExrOaHSn6pzRLMsIIZRS13Vt29Y0DWRVVU3T9MDn89XF0/LqTbn++KrW6zRJIJUcchiGaFcURdu2PfCtG9zzeMrjB9urt1swqIbncB+dZyj32146TPZC2fKSvKjrXxgOwWNsYMgoQq9+22WWGvqrY+m4bDabTqoLXKMogmEo99uuuLTLHptgFnlKkgq8tu/7jDG8VhzHOKNoECar0Wp6rC1O4+8bQkzAqOvmhPluWpjvh43Fib48M55H9PMyz4VpEqipqipJEvYEcTgfhNd00gazmo9j+i6yQtc1iFuWJcuyoiiO42CEwVUVWVQmpBSsaXd1XXuexzkHb5om/gnbx+AP+z8eekPkbzHtIXrhH/iHqrVFgl5WAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
        &lt;source
          srcset=&quot;/static/a84f87412aa9d882799f2cb3aefeaad0/8ac56/relay-intro-1-3-b9174b65-6b1c-f501-2607-7944455087d3.webp 240w,
/static/a84f87412aa9d882799f2cb3aefeaad0/d3be9/relay-intro-1-3-b9174b65-6b1c-f501-2607-7944455087d3.webp 480w,
/static/a84f87412aa9d882799f2cb3aefeaad0/fd35f/relay-intro-1-3-b9174b65-6b1c-f501-2607-7944455087d3.webp 880w&quot;
          sizes=&quot;(max-width: 880px) 100vw, 880px&quot;
          type=&quot;image/webp&quot;
        /&gt;
        &lt;source
          srcset=&quot;/static/a84f87412aa9d882799f2cb3aefeaad0/8ff5a/relay-intro-1-3-b9174b65-6b1c-f501-2607-7944455087d3.png 240w,
/static/a84f87412aa9d882799f2cb3aefeaad0/e85cb/relay-intro-1-3-b9174b65-6b1c-f501-2607-7944455087d3.png 480w,
/static/a84f87412aa9d882799f2cb3aefeaad0/9c177/relay-intro-1-3-b9174b65-6b1c-f501-2607-7944455087d3.png 880w&quot;
          sizes=&quot;(max-width: 880px) 100vw, 880px&quot;
          type=&quot;image/png&quot;
        /&gt;
        &lt;img
          class=&quot;gatsby-resp-image-image&quot;
          src=&quot;/static/a84f87412aa9d882799f2cb3aefeaad0/9c177/relay-intro-1-3-b9174b65-6b1c-f501-2607-7944455087d3.png&quot;
          alt=&quot;画像3.png&quot;
          title=&quot;画像3.png&quot;
          loading=&quot;lazy&quot;
          style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        /&gt;
      &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;何が違うのでしょうか？&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;queryが GraphQL API に発行され、データはフレームワークのデータストアに格納されます。ここまでは同じですが、ここから違いが出てきます。&lt;/li&gt;
&lt;li&gt;データを使用するすべてのコンポーネントは、&lt;em&gt;データストア（キャッシュ）から直接データを取得&lt;/em&gt;していることに注目してください。これは、Relayのfragmentとの深い統合によるものです。UIでは、各fragmentはフレームワークのデータストアから直接データを取得し、実際のデータがqueryから渡されることには依存しません。&lt;/li&gt;
&lt;li&gt;queryから他のコンポーネントの矢印は消えています。queryから一部の情報は、データストアから必要なデータを探すためにfragmentに渡されています。しかし、実際のデータはfragmentに渡されず、全ての実際のデータはfragment自身によってデータストアから取得されます。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;以上、Relayやその他のGraphQLフレームワークの仕組みについて、詳しく説明してきました。なぜこれにこだわる必要があるのでしょうか？ 実は、この設定によりいくつかの優れた機能が利用できるようになります。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;他のフレームワークでは通常、データのソースとしてqueryを使用し、そのデータを他のコンポーネントにツリーダウンして渡すことに頼っています。Relayではこれを逆転させ、各コンポーネントがデータストアから必要なデータを取得できるようにします。
&lt;sup id=&quot;fnref-9&quot;&gt;&lt;a href=&quot;#fn-9&quot; class=&quot;footnote-ref&quot;&gt;9&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;タダでパフォーマンスが得られる&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E3%82%BF%E3%83%80%E3%81%A7%E3%83%91%E3%83%95%E3%82%A9%E3%83%BC%E3%83%9E%E3%83%B3%E3%82%B9%E3%81%8C%E5%BE%97%E3%82%89%E3%82%8C%E3%82%8B&quot; aria-label=&quot;タダでパフォーマンスが得られる permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;タダでパフォーマンスが得られる&lt;/h2&gt;
&lt;p&gt;考えてみてください。queryがデータのソースである場合、queryが持っているデータに影響を与えるデータストアへの更新は、queryを保持しているコンポーネントの再レンダリングを強制します。つまり、データストアの更新によって再レンダリングが発生し、子コンポーネントに渡すために親コンポーネントからデータを取得する以外に、更新とは何の関係もないコンポーネントのいくつものレイヤーを経由してカスケードしなければなりません。&lt;/p&gt;
&lt;p&gt;各コンポーネントが必要なデータをストアから直接取得し、使用するデータのみを対象に更新をsubscribeするというRelayのアプローチでは、アプリのサイズや複雑さが大きくなってもパフォーマンスを維持することができます。&lt;/p&gt;
&lt;p&gt;これはsubscriptionを使用する際にも重要です。Relayでは、subscriptionから送られてくる更新データが、その更新データを実際に使用しているコンポーネントの再レンダリングのみを発生させるようにしています。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;データのソースとしてqueryを使用すると、GraphQLのキャッシュが更新されたときにコンポーネントツリー全体が再レンダリングされることになります。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;モジュール化と独立性は、安全にリファクタリングできることを意味する&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E3%83%A2%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AB%E5%8C%96%E3%81%A8%E7%8B%AC%E7%AB%8B%E6%80%A7%E3%81%AF%E3%80%81%E5%AE%89%E5%85%A8%E3%81%AB%E3%83%AA%E3%83%95%E3%82%A1%E3%82%AF%E3%82%BF%E3%83%AA%E3%83%B3%E3%82%B0%E3%81%A7%E3%81%8D%E3%82%8B%E3%81%93%E3%81%A8%E3%82%92%E6%84%8F%E5%91%B3%E3%81%99%E3%82%8B&quot; aria-label=&quot;モジュール化と独立性は、安全にリファクタリングできることを意味する permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;モジュール化と独立性は、安全にリファクタリングできることを意味する&lt;/h2&gt;
&lt;p&gt;queryからデータを実際に必要とするコンポーネントにデータをルーティングする責任を開発者から取り除くことで、開発者が物事を台無しにする機会がなくなります。queryのデータにアクセスできないということは、コンポーネントツリーを通過しているはずのデータに誤って(あるいはもっと悪いことに、意図的に)依存してしまう &lt;sup id=&quot;fnref-10&quot;&gt;&lt;a href=&quot;#fn-10&quot; class=&quot;footnote-ref&quot;&gt;10&lt;/a&gt;&lt;/sup&gt;ということはあり得ません。Relayはあなたのためにヘビーな作業を代行してくれます。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Relayとそのfragmentファーストのアプローチを使うことで、コンポーネントツリー内のデータフローを混乱させることは非常に難しくなります。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;もちろん、「queryをデータのソースとして使用する」アプローチの欠点のほとんどは、&lt;code class=&quot;language-text&quot;&gt;React.memo&lt;/code&gt;や&lt;code class=&quot;language-text&quot;&gt;shouldComponentUpdate&lt;/code&gt;など、昔ながらの手動での最適化によって多少は緩和されることには注意が必要です。しかし、これはそれ自体がパフォーマンスの問題になる可能性があるだけでなく、ミスを起こしやすいという欠点があります（タスクが面倒であればあるほど、人間は最終的にそれを台無しにしてしまう可能性が高くなります）。一方、Relayは、そんなことを考えなくてもパフォーマンスを維持できるようにしてくれます。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;各コンポーネントがキャッシュから独自したデータを受け取ることで、ビューのフルデータが戻ってくるのを待っている間に、すでにストアで利用可能なデータを使ってビューを部分的にレンダリングするなど、Relayの非常にクールで高度な機能を利用することができます。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;fragmentのまとめ&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#fragment%E3%81%AE%E3%81%BE%E3%81%A8%E3%82%81&quot; aria-label=&quot;fragmentのまとめ permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;fragmentのまとめ&lt;/h2&gt;
&lt;p&gt;ここで少し立ち止まって、Relayがどのようなタイプの作業をしてくれているのかをダイジェストで見てみましょう。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;型システムを通じて、Relayは対象のコンポーネントが正確な使い方の正しいGraphQLのデータなしではレンダリングできないことを確認しています。これで失敗が一つ減りました。&lt;/li&gt;
&lt;li&gt;fragmentを使用する各コンポーネントは、使用するデータが更新された場合にのみ正確に更新されるため、Relayではデフォルトで高いパフォーマンスが得られます。&lt;/li&gt;
&lt;li&gt;型を生成することで、Relayはfragmentから得られたデータとの対話が型安全であることを保証しています。ここで注目すべきは、型生成はRelayコンパイラの中核機能であるということです。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Relayのアーキテクチャと哲学では、コンポーネントのデータ依存関係からサーバが提供するデータとその型に至るまで、コンポーネントに関する多くの情報をコンピュータに提供しています。これらすべての情報を利用して、通常であれば開発者である私たちが処理しなければならないようなあらゆる作業を行います。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;ビューが複雑になる速度を過小評価するのは簡単です。複雑さとパフォーマンスは、Relayが強制的に従わせる規則によってデフォルトで処理されます。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;これにより、開発者には大きな力がもたらされます。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ほぼ完全に分離された組み合わせ可能なコンポーネントを構築することができます。&lt;/li&gt;
&lt;li&gt;コンポーネントのリファクタリングは完全に安全で、Relayは何かを見逃したり、台無しにしたりすることがないようにしてくれます。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;再利用可能なコンポーネントを数多く作り始めると、その重要性は過大評価されることはないでしょう。コードベースの大部分で使用されるコンポーネントのリファクタリングが安全であることは、開発者のベロシティにとって&lt;em&gt;非常にクリティカル&lt;/em&gt;です。&lt;/p&gt;
&lt;p&gt;アプリが成長するにつれて、リファクタリングの容易さと安全性は、高速に開発し続けるために非常に重要になってきます。&lt;/p&gt;
&lt;h1 id=&quot;relayの紹介を締めくくる&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#relay%E3%81%AE%E7%B4%B9%E4%BB%8B%E3%82%92%E7%B7%A0%E3%82%81%E3%81%8F%E3%81%8F%E3%82%8B&quot; aria-label=&quot;relayの紹介を締めくくる permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Relayの紹介を締めくくる&lt;/h1&gt;
&lt;p&gt;この記事では多くのことを取り上げました。この記事から何かを得たとするなら、Relayは、保守やリファクタリングが簡単で型安全な、スケーラブルでパフォーマンスが高いアプリケーションを構築することを&lt;em&gt;強制している&lt;/em&gt;ということです。&lt;/p&gt;
&lt;p&gt;Relayは実際にあなたのために泥臭い仕事を肩代わりしてくれます。そして私たちが示した多くのことは、他のフレームワークを使っても英雄的な努力を通して達成することができますが、私たちはこれらのパターンを&lt;em&gt;強制する&lt;/em&gt;ことがもたらすことができる強力な利点を示したことを願っています。 それらの重要性は誇張ではありません。&lt;/p&gt;
&lt;h2 id=&quot;注目に値するソフトウェア&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E6%B3%A8%E7%9B%AE%E3%81%AB%E5%80%A4%E3%81%99%E3%82%8B%E3%82%BD%E3%83%95%E3%83%88%E3%82%A6%E3%82%A7%E3%82%A2&quot; aria-label=&quot;注目に値するソフトウェア permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;注目に値するソフトウェア&lt;/h2&gt;
&lt;p&gt;Relayは、血と汗と涙、そして何よりも重要な、長い間GraphQLを使って製品を出荷したりメンテナンスしたりしてきた経験と深い洞察力をもとに構築された、注目に値するソフトウェアです。&lt;/p&gt;
&lt;p&gt;この記事はかなり長く、かなり濃い内容になっていますが、Relayで何ができるのかについてはまだほんの少ししか触れていません。この記事の最後に、この記事では取り上げていないRelayでできることの詳細をいくつか挙げておきましょう。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;楽観的で複雑なキャッシュ更新によるmutation &lt;sup id=&quot;fnref-11&quot;&gt;&lt;a href=&quot;#fn-11&quot; class=&quot;footnote-ref&quot;&gt;11&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;subscription&lt;/li&gt;
&lt;li&gt;SuspenseとConcurrent Modeと完全に統合されており、次世代のReactに対応しています。&lt;/li&gt;
&lt;li&gt;Relayを使ってローカルな状態を管理することで、Relayをローカルな状態管理にも使えるという一般的なメリットを享受できます（SuspenseとConcurrent Modeとの統合など！）。&lt;/li&gt;
&lt;li&gt;リストの結果を&lt;code class=&quot;language-text&quot;&gt;@stream&lt;/code&gt;経由でストリーミングできます&lt;/li&gt;
&lt;li&gt;サーバレスポンスの読み込みに時間がかかる部分を&lt;code class=&quot;language-text&quot;&gt;@defer&lt;/code&gt;で遅延させることで、残りの UI をより高速にレンダリングできるようにします。&lt;/li&gt;
&lt;li&gt;fragmentの再取得とページネーションのためのqueryの自動生成&lt;/li&gt;
&lt;li&gt;複雑なキャッシュ管理：キャッシュのサイズを制御し、ビューのデータをキャッシュを使用するかネットワークから取得するかを制御します (またはその両方、または最初にキャッシュを実行してからネットワークを実行します)。&lt;/li&gt;
&lt;li&gt;安定した成熟した柔軟なキャッシュ。本当に安定して動きます。 &lt;sup id=&quot;fnref-12&quot;&gt;&lt;a href=&quot;#fn-12&quot; class=&quot;footnote-ref&quot;&gt;12&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;ユーザーがナビゲーションの開始を示すと、すぐに新しいビューのqueryをプリロードします。queryデータが到着するのを待っている間に、ストアですでに利用可能なデータを使ってビューを部分的にレンダリングします。&lt;/li&gt;
&lt;li&gt;fragmentの引数（コンポーネントの&lt;code class=&quot;language-text&quot;&gt;props&lt;/code&gt;のようなもの）を定義することで、コンポーネントの組み立てを次のレベルに引き上げることができます。&lt;/li&gt;
&lt;li&gt;スキーマから得られるデータよりも、グラフ内のデータがどのように接続されているのかをRelayに教えることで、キャッシュからより多くのデータを解決できるようにします（「ある変数を持つトップレベルのフィールドは同じUserを解決できる」と考えてください）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;この記事はここまでですが、Relayのページネーションに関する記事もぜひ読んでみてください。Relayページネーション編では、Relayのパワフルな機能を美しい方法でまとめ、フレームワークにすべての作業を任せることで、どれだけの自動化と驚くべきDXが可能になるのかを紹介しています。&lt;a href=&quot;/posts/2020-08-29-an-introduction-to-relay-3&quot;&gt;詳細はこちらをご覧ください&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;他にも続けて読める記事をいくつかご紹介します。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/posts/2020-08-30-an-introduction-to-relay-4&quot;&gt;Nodeインターフェイスの魔法&lt;/a&gt;。Node インターフェース、グローバルに一意な ID、そしてそれらがもたらす力についての記事。&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/posts/2020-08-28-an-introduction-to-relay-2&quot;&gt;Connectionベースのページネーション&lt;/a&gt;。Connectionベースのページペーションがなぜ良いアイデアなのかについての紹介。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;お読みいただきありがとうございました！&lt;/p&gt;
&lt;h1 id=&quot;スペシャルサンクス&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E3%82%B9%E3%83%9A%E3%82%B7%E3%83%A3%E3%83%AB%E3%82%B5%E3%83%B3%E3%82%AF%E3%82%B9&quot; aria-label=&quot;スペシャルサンクス permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;スペシャルサンクス&lt;/h1&gt;
&lt;p&gt;この記事の草稿への徹底的なフィードバックをしてくれた Xavier Cazalot, Arnar Þór Sveinsson, Jaap Frolich, Joe Previte, Stepan Parunashvili, Ben Sangster に感謝します!&lt;/p&gt;
&lt;div class=&quot;footnotes&quot;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&quot;fn-1&quot;&gt;
&lt;p&gt;訳注：翻訳の許可をくれたGabriel、ありがとう。あんた最高だぜブラザー。&lt;/p&gt;
&lt;a href=&quot;#fnref-1&quot; class=&quot;footnote-backref&quot;&gt;↩&lt;/a&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-2&quot;&gt;
&lt;p&gt;訳注：2020年8月現在、ReactのSuspenseとConcurrent Modeはexperimental buildでのみ利用可能です。 &lt;a href=&quot;https://ja.reactjs.org/docs/concurrent-mode-adoption.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://ja.reactjs.org/docs/concurrent-mode-adoption.html&lt;/a&gt; hooksなどのRelayのexperimental版にしかないAPIも同様です。&lt;/p&gt;
&lt;a href=&quot;#fnref-2&quot; class=&quot;footnote-backref&quot;&gt;↩&lt;/a&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-3&quot;&gt;
&lt;p&gt;訳注：2020年5月にrelayで作ったfacebookはリリースされました。&lt;/p&gt;
&lt;a href=&quot;#fnref-3&quot; class=&quot;footnote-backref&quot;&gt;↩&lt;/a&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-4&quot;&gt;
&lt;p&gt;訳注：GraphQLのoperationとは、QueryまたはMutation、Subscription、Fragmentのことです。&lt;/p&gt;
&lt;a href=&quot;#fnref-4&quot; class=&quot;footnote-backref&quot;&gt;↩&lt;/a&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-5&quot;&gt;
&lt;p&gt;訳注：全体が1つのグラフになっているので、blogフィールドの中にauthorフィールドがあると考えられます。そうなると、authorフィールドのサブフィールドがfirstNameなどであると考えられます。ここでは要するに、最小単位でまとめられるためにあらゆるコンポーネントのデータを定義できるということです。&lt;/p&gt;
&lt;a href=&quot;#fnref-5&quot; class=&quot;footnote-backref&quot;&gt;↩&lt;/a&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-6&quot;&gt;
&lt;p&gt;訳注：spreadを展開と訳していますが、ここで言う展開とはスプレッド演算子（…）を使用してfragmentのフィールドをクエリに展開することを言っています。fragmentのデータが展開されるわけではありません。&lt;/p&gt;
&lt;a href=&quot;#fnref-6&quot; class=&quot;footnote-backref&quot;&gt;↩&lt;/a&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-7&quot;&gt;
&lt;p&gt;訳注：BlogPostHeader.tsのこと。鶏と卵のように聞こえるかもしれませんが、まずBlogPostHeader.tsにqueryを書いてコンパイルすると型定義のファイルが作られるので、それをimportしてレンダリングするコードを書きます。&lt;/p&gt;
&lt;a href=&quot;#fnref-7&quot; class=&quot;footnote-backref&quot;&gt;↩&lt;/a&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-8&quot;&gt;
&lt;p&gt;訳注：次というのは&lt;code class=&quot;language-text&quot;&gt;BlogPostHeader_blogPost&lt;/code&gt;の子要素ということ&lt;/p&gt;
&lt;a href=&quot;#fnref-8&quot; class=&quot;footnote-backref&quot;&gt;↩&lt;/a&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-9&quot;&gt;
&lt;p&gt;訳注：ここで言うツリーダウンとは、親要素から子要素へ、子要素から孫要素へとツリーを降りながらデータを渡すことです。&lt;/p&gt;
&lt;a href=&quot;#fnref-9&quot; class=&quot;footnote-backref&quot;&gt;↩&lt;/a&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-10&quot;&gt;
&lt;p&gt;訳注：別のコンポーネントが要求して自身が明示的に要求していないデータに依存していると、別のコンポーネントが何らかの理由でそのデータを取得しない場合（もっと悪いとコンポーネントが消えた場合）にデータが欠損します。こうした事象を長期的に手動で完全にコントロールすることは非常に困難です&lt;/p&gt;
&lt;a href=&quot;#fnref-10&quot; class=&quot;footnote-backref&quot;&gt;↩&lt;/a&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-11&quot;&gt;
&lt;p&gt;訳注：楽観的（optimistic）なキャッシュ更新とは、更新時にサーバー側のレスポンスを待たずにキャッシュを更新することです&lt;/p&gt;
&lt;a href=&quot;#fnref-11&quot; class=&quot;footnote-backref&quot;&gt;↩&lt;/a&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-12&quot;&gt;
&lt;p&gt;訳注：原文はJust Works (TM) 。知らなかったので本人に聞いたら、Just Works (TM) is a joke. People often refer to software/hardware that’s really stable like “it just works” だそうです。ギャグの解説させてしまってごめん。&lt;/p&gt;
&lt;a href=&quot;#fnref-12&quot; class=&quot;footnote-backref&quot;&gt;↩&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</content:encoded></item></channel></rss>