relay-runtime 20.1.0 → 21.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (262) hide show
  1. package/experimental.js +1 -1
  2. package/experimental.js.flow +8 -8
  3. package/handlers/connection/ConnectionHandler.js.flow +5 -5
  4. package/handlers/connection/ConnectionInterface.js.flow +1 -1
  5. package/index.js +1 -1
  6. package/index.js.flow +125 -62
  7. package/lib/experimental.js +3 -3
  8. package/lib/index.js +105 -57
  9. package/lib/multi-actor-environment/ActorIdentifier.js +2 -2
  10. package/lib/multi-actor-environment/MultiActorEnvironment.js +3 -1
  11. package/lib/mutations/commitMutation.js +8 -8
  12. package/lib/mutations/validateMutation.js +4 -4
  13. package/lib/query/GraphQLTag.js +3 -3
  14. package/lib/query/fetchQuery.js +15 -3
  15. package/lib/store/DataChecker.js +38 -4
  16. package/lib/store/NormalizationEngine.js +373 -0
  17. package/lib/store/OperationExecutor.js +172 -113
  18. package/lib/store/RelayConcreteVariables.js +1 -1
  19. package/lib/store/RelayErrorTrie.js +2 -2
  20. package/lib/store/RelayExperimentalGraphResponseTransform.js +8 -8
  21. package/lib/store/RelayModernEnvironment.js +26 -19
  22. package/lib/store/RelayModernRecord.js +18 -8
  23. package/lib/store/RelayModernSelector.js +9 -9
  24. package/lib/store/RelayModernStore.js +152 -43
  25. package/lib/store/RelayPublishQueue.js +1 -1
  26. package/lib/store/RelayReader.js +76 -38
  27. package/lib/store/RelayRecordSource.js +6 -0
  28. package/lib/store/RelayReferenceMarker.js +2 -1
  29. package/lib/store/RelayResponseNormalizer.js +88 -55
  30. package/lib/store/RelayStoreSubscriptions.js +34 -10
  31. package/lib/store/RelayStoreUtils.js +8 -1
  32. package/lib/store/ResolverFragments.js +2 -2
  33. package/lib/store/live-resolvers/LiveResolverCache.js +25 -9
  34. package/lib/store/observeFragmentExperimental.js +17 -1
  35. package/lib/store/observeQueryExperimental.js +2 -2
  36. package/lib/subscription/requestSubscription.js +3 -3
  37. package/lib/util/RelayError.js +3 -0
  38. package/lib/util/RelayFeatureFlags.js +6 -2
  39. package/lib/util/RelayReplaySubject.js +4 -4
  40. package/lib/util/handlePotentialSnapshotErrors.js +2 -2
  41. package/lib/util/stableCopy.js +2 -2
  42. package/llm-docs/api-reference/entrypoint-apis/entrypoint-container.mdx +38 -0
  43. package/llm-docs/api-reference/entrypoint-apis/load-entrypoint.mdx +77 -0
  44. package/llm-docs/api-reference/entrypoint-apis/use-entrypoint-loader.mdx +99 -0
  45. package/llm-docs/api-reference/graphql/graphql-directives.mdx +378 -0
  46. package/llm-docs/api-reference/hooks/_use-lazy-load-query-extra.mdx +16 -0
  47. package/llm-docs/api-reference/hooks/load-query.mdx +84 -0
  48. package/llm-docs/api-reference/hooks/relay-environment-provider.mdx +78 -0
  49. package/llm-docs/api-reference/hooks/use-client-query.mdx +65 -0
  50. package/llm-docs/api-reference/hooks/use-fragment.mdx +69 -0
  51. package/llm-docs/api-reference/hooks/use-lazy-load-query.mdx +62 -0
  52. package/llm-docs/api-reference/hooks/use-mutation.mdx +94 -0
  53. package/llm-docs/api-reference/hooks/use-pagination-fragment.mdx +166 -0
  54. package/llm-docs/api-reference/hooks/use-prefetchable-forward-pagination-fragment.mdx +134 -0
  55. package/llm-docs/api-reference/hooks/use-preloaded-query.mdx +84 -0
  56. package/llm-docs/api-reference/hooks/use-query-loader.mdx +95 -0
  57. package/llm-docs/api-reference/hooks/use-refetchable-fragment.mdx +122 -0
  58. package/llm-docs/api-reference/hooks/use-relay-environment.mdx +37 -0
  59. package/llm-docs/api-reference/hooks/use-subscription.mdx +66 -0
  60. package/llm-docs/api-reference/relay-resolvers/docblock-format.mdx +321 -0
  61. package/llm-docs/api-reference/relay-resolvers/runtime-functions.mdx +94 -0
  62. package/llm-docs/api-reference/relay-runtime/commit-mutation.mdx +65 -0
  63. package/llm-docs/api-reference/relay-runtime/fetch-query.mdx +113 -0
  64. package/llm-docs/api-reference/relay-runtime/field-logger.mdx +170 -0
  65. package/llm-docs/api-reference/relay-runtime/observe-fragment.mdx +92 -0
  66. package/llm-docs/api-reference/relay-runtime/relay-environment.mdx +53 -0
  67. package/llm-docs/api-reference/relay-runtime/request-subscription.mdx +54 -0
  68. package/llm-docs/api-reference/relay-runtime/runtime-configuration.mdx +52 -0
  69. package/llm-docs/api-reference/relay-runtime/store.mdx +734 -0
  70. package/llm-docs/api-reference/relay-runtime/wait-for-fragment-data.mdx +89 -0
  71. package/llm-docs/api-reference/types/CacheConfig.mdx +8 -0
  72. package/llm-docs/api-reference/types/Disposable.mdx +4 -0
  73. package/llm-docs/api-reference/types/GraphQLSubscriptionConfig.mdx +17 -0
  74. package/llm-docs/api-reference/types/MutationConfig.mdx +31 -0
  75. package/llm-docs/api-reference/types/SelectorStoreUpdater.mdx +6 -0
  76. package/llm-docs/api-reference/types/UploadableMap.mdx +3 -0
  77. package/llm-docs/community/learning-resources.mdx +64 -0
  78. package/llm-docs/debugging/declarative-mutation-directives.mdx +34 -0
  79. package/llm-docs/debugging/disallowed-id-types-error.mdx +43 -0
  80. package/llm-docs/debugging/inconsistent-typename-error.mdx +47 -0
  81. package/llm-docs/debugging/relay-devtools.mdx +73 -0
  82. package/llm-docs/debugging/why-null.mdx +116 -0
  83. package/llm-docs/editor-support.mdx +55 -0
  84. package/llm-docs/error-reference/unknown-field.mdx +36 -0
  85. package/llm-docs/getting-started/babel-plugin.mdx +31 -0
  86. package/llm-docs/getting-started/compiler-config.mdx +25 -0
  87. package/llm-docs/getting-started/compiler.mdx +82 -0
  88. package/llm-docs/getting-started/lint-rules.mdx +87 -0
  89. package/llm-docs/getting-started/production.mdx +30 -0
  90. package/llm-docs/getting-started/quick-start.mdx +213 -0
  91. package/llm-docs/glossary/glossary.mdx +1040 -0
  92. package/llm-docs/guided-tour/list-data/advanced-pagination.mdx +157 -0
  93. package/llm-docs/guided-tour/list-data/connections.mdx +81 -0
  94. package/llm-docs/guided-tour/list-data/pagination.mdx +193 -0
  95. package/llm-docs/guided-tour/list-data/rendering-connections.mdx +112 -0
  96. package/llm-docs/guided-tour/list-data/streaming-pagination.mdx +87 -0
  97. package/llm-docs/guided-tour/managing-data-outside-react/retaining-queries.mdx +51 -0
  98. package/llm-docs/guided-tour/refetching/refetching-queries-with-different-data.mdx +337 -0
  99. package/llm-docs/guided-tour/refetching/refreshing-queries.mdx +350 -0
  100. package/llm-docs/guided-tour/rendering/environment.mdx +59 -0
  101. package/llm-docs/guided-tour/rendering/error-states.mdx +295 -0
  102. package/llm-docs/guided-tour/rendering/fragments.mdx +354 -0
  103. package/llm-docs/guided-tour/rendering/loading-states.mdx +245 -0
  104. package/llm-docs/guided-tour/rendering/queries.mdx +261 -0
  105. package/llm-docs/guided-tour/rendering/variables.mdx +233 -0
  106. package/llm-docs/guided-tour/reusing-cached-data/fetch-policies.mdx +56 -0
  107. package/llm-docs/guided-tour/reusing-cached-data/filling-in-missing-data.mdx +102 -0
  108. package/llm-docs/guided-tour/reusing-cached-data/introduction.mdx +22 -0
  109. package/llm-docs/guided-tour/reusing-cached-data/presence-of-data.mdx +93 -0
  110. package/llm-docs/guided-tour/reusing-cached-data/rendering-partially-cached-data.mdx +175 -0
  111. package/llm-docs/guided-tour/reusing-cached-data/staleness-of-data.mdx +116 -0
  112. package/llm-docs/guided-tour/updating-data/client-only-data.mdx +115 -0
  113. package/llm-docs/guided-tour/updating-data/graphql-mutations.mdx +334 -0
  114. package/llm-docs/guided-tour/updating-data/graphql-subscriptions.mdx +279 -0
  115. package/llm-docs/guided-tour/updating-data/imperatively-modifying-linked-fields.mdx +511 -0
  116. package/llm-docs/guided-tour/updating-data/imperatively-modifying-store-data-legacy.mdx +142 -0
  117. package/llm-docs/guided-tour/updating-data/imperatively-modifying-store-data.mdx +275 -0
  118. package/llm-docs/guided-tour/updating-data/introduction.mdx +25 -0
  119. package/llm-docs/guided-tour/updating-data/local-data-updates.mdx +71 -0
  120. package/llm-docs/guided-tour/updating-data/typesafe-updaters-faq.mdx +83 -0
  121. package/llm-docs/guided-tour/updating-data/updating-connections.mdx +592 -0
  122. package/llm-docs/guides/alias-directive.mdx +160 -0
  123. package/llm-docs/guides/catch-directive.mdx +167 -0
  124. package/llm-docs/guides/client-schema-extensions.mdx +208 -0
  125. package/llm-docs/guides/codemods.mdx +66 -0
  126. package/llm-docs/guides/data-driven-dependencies/client-3d.mdx +255 -0
  127. package/llm-docs/guides/data-driven-dependencies/configuration.mdx +127 -0
  128. package/llm-docs/guides/data-driven-dependencies/introduction.mdx +39 -0
  129. package/llm-docs/guides/data-driven-dependencies/server-3d.mdx +664 -0
  130. package/llm-docs/guides/document-comparison.mdx +106 -0
  131. package/llm-docs/guides/graphql-server-specification.mdx +453 -0
  132. package/llm-docs/guides/network-layer.mdx +69 -0
  133. package/llm-docs/guides/persisted-queries.mdx +328 -0
  134. package/llm-docs/guides/relay-resolvers/context.mdx +99 -0
  135. package/llm-docs/guides/relay-resolvers/defining-fields.mdx +151 -0
  136. package/llm-docs/guides/relay-resolvers/defining-types.mdx +164 -0
  137. package/llm-docs/guides/relay-resolvers/deprecated.mdx +27 -0
  138. package/llm-docs/guides/relay-resolvers/derived-fields.mdx +127 -0
  139. package/llm-docs/guides/relay-resolvers/descriptions.mdx +44 -0
  140. package/llm-docs/guides/relay-resolvers/enabling.mdx +41 -0
  141. package/llm-docs/guides/relay-resolvers/errors.mdx +64 -0
  142. package/llm-docs/guides/relay-resolvers/field-arguments.mdx +63 -0
  143. package/llm-docs/guides/relay-resolvers/introduction.mdx +62 -0
  144. package/llm-docs/guides/relay-resolvers/limitations.mdx +30 -0
  145. package/llm-docs/guides/relay-resolvers/live-fields.mdx +164 -0
  146. package/llm-docs/guides/relay-resolvers/return-types.mdx +161 -0
  147. package/llm-docs/guides/relay-resolvers/suspense.mdx +41 -0
  148. package/llm-docs/guides/required-directive.mdx +240 -0
  149. package/llm-docs/guides/semantic-nullability.mdx +93 -0
  150. package/llm-docs/guides/testing-relay-components.mdx +642 -0
  151. package/llm-docs/guides/testing-relay-with-preloaded-queries.mdx +160 -0
  152. package/llm-docs/guides/throw-on-field-error-directive.mdx +58 -0
  153. package/llm-docs/guides/type-emission.mdx +414 -0
  154. package/llm-docs/home.mdx +32 -0
  155. package/llm-docs/principles-and-architecture/architecture-overview.mdx +24 -0
  156. package/llm-docs/principles-and-architecture/compiler-architecture.mdx +106 -0
  157. package/llm-docs/principles-and-architecture/runtime-architecture.mdx +249 -0
  158. package/llm-docs/principles-and-architecture/thinking-in-graphql.mdx +309 -0
  159. package/llm-docs/principles-and-architecture/thinking-in-relay.mdx +104 -0
  160. package/llm-docs/principles-and-architecture/videos.mdx +50 -0
  161. package/llm-docs/tutorial/arrays-lists.mdx +126 -0
  162. package/llm-docs/tutorial/fragments-1.mdx +487 -0
  163. package/llm-docs/tutorial/graphql.mdx +172 -0
  164. package/llm-docs/tutorial/interfaces-polymorphism.mdx +161 -0
  165. package/llm-docs/tutorial/intro.mdx +58 -0
  166. package/llm-docs/tutorial/mutations-updates.mdx +624 -0
  167. package/llm-docs/tutorial/organizing-mutations-queries-and-subscriptions.mdx +13 -0
  168. package/llm-docs/tutorial/queries-1.mdx +267 -0
  169. package/llm-docs/tutorial/queries-2.mdx +389 -0
  170. package/llm-docs/tutorial/refetchable-fragments.mdx +352 -0
  171. package/multi-actor-environment/ActorIdentifier.js.flow +2 -2
  172. package/multi-actor-environment/ActorSpecificEnvironment.js.flow +7 -7
  173. package/multi-actor-environment/ActorUtils.js.flow +1 -1
  174. package/multi-actor-environment/MultiActorEnvironment.js.flow +12 -8
  175. package/multi-actor-environment/MultiActorEnvironmentTypes.js.flow +3 -3
  176. package/mutations/RelayDeclarativeMutationConfig.js.flow +9 -9
  177. package/mutations/RelayRecordProxy.js.flow +8 -11
  178. package/mutations/RelayRecordSourceMutator.js.flow +4 -4
  179. package/mutations/RelayRecordSourceProxy.js.flow +4 -4
  180. package/mutations/RelayRecordSourceSelectorProxy.js.flow +6 -6
  181. package/mutations/applyOptimisticMutation.js.flow +2 -2
  182. package/mutations/commitMutation.js.flow +20 -16
  183. package/mutations/createUpdatableProxy.js.flow +19 -19
  184. package/mutations/readUpdatableFragment.js.flow +3 -3
  185. package/mutations/readUpdatableQuery.js.flow +3 -3
  186. package/mutations/validateMutation.js.flow +7 -7
  187. package/network/RelayNetworkTypes.js.flow +4 -4
  188. package/network/RelayObservable.js.flow +16 -14
  189. package/network/RelayQueryResponseCache.js.flow +3 -3
  190. package/network/wrapNetworkWithLogObserver.js.flow +1 -1
  191. package/package.json +2 -1
  192. package/query/GraphQLTag.js.flow +22 -10
  193. package/query/fetchQuery.js.flow +23 -10
  194. package/query/fetchQuery_DEPRECATED.js.flow +1 -1
  195. package/store/DataChecker.js.flow +43 -9
  196. package/store/NormalizationEngine.js.flow +779 -0
  197. package/store/OperationExecutor.js.flow +173 -70
  198. package/store/RelayConcreteVariables.js.flow +5 -5
  199. package/store/RelayErrorTrie.js.flow +12 -12
  200. package/store/RelayExperimentalGraphResponseHandler.js.flow +3 -3
  201. package/store/RelayExperimentalGraphResponseTransform.js.flow +10 -10
  202. package/store/RelayModernEnvironment.js.flow +41 -26
  203. package/store/RelayModernFragmentSpecResolver.js.flow +1 -1
  204. package/store/RelayModernOperationDescriptor.js.flow +1 -1
  205. package/store/RelayModernRecord.js.flow +46 -22
  206. package/store/RelayModernSelector.js.flow +21 -21
  207. package/store/RelayModernStore.js.flow +219 -58
  208. package/store/RelayOperationTracker.js.flow +2 -2
  209. package/store/RelayOptimisticRecordSource.js.flow +2 -2
  210. package/store/RelayPublishQueue.js.flow +21 -12
  211. package/store/RelayReader.js.flow +130 -58
  212. package/store/RelayRecordSource.js.flow +10 -0
  213. package/store/RelayRecordState.js.flow +1 -1
  214. package/store/RelayReferenceMarker.js.flow +5 -4
  215. package/store/RelayResponseNormalizer.js.flow +130 -54
  216. package/store/RelayStoreSubscriptions.js.flow +52 -8
  217. package/store/RelayStoreTypes.js.flow +153 -64
  218. package/store/RelayStoreUtils.js.flow +15 -7
  219. package/store/ResolverCache.js.flow +2 -2
  220. package/store/ResolverFragments.js.flow +12 -12
  221. package/store/StoreInspector.js.flow +6 -7
  222. package/store/cloneRelayHandleSourceField.js.flow +1 -1
  223. package/store/cloneRelayScalarHandleSourceField.js.flow +1 -1
  224. package/store/createRelayContext.js.flow +1 -1
  225. package/store/createRelayLoggingContext.js.flow +4 -4
  226. package/store/defaultGetDataID.js.flow +2 -2
  227. package/store/isRelayModernEnvironment.js.flow +4 -2
  228. package/store/live-resolvers/LiveResolverCache.js.flow +55 -20
  229. package/store/live-resolvers/LiveResolverSuspenseSentinel.js.flow +3 -3
  230. package/store/live-resolvers/getOutputTypeRecordIDs.js.flow +1 -1
  231. package/store/live-resolvers/isLiveStateValue.js.flow +2 -2
  232. package/store/live-resolvers/resolverDataInjector.js.flow +8 -5
  233. package/store/observeFragmentExperimental.js.flow +49 -20
  234. package/store/observeQueryExperimental.js.flow +5 -5
  235. package/store/readInlineData.js.flow +4 -4
  236. package/store/waitForFragmentExperimental.js.flow +3 -3
  237. package/subscription/requestSubscription.js.flow +7 -7
  238. package/util/NormalizationNode.js.flow +34 -32
  239. package/util/ReaderNode.js.flow +32 -30
  240. package/util/RelayConcreteNode.js.flow +5 -5
  241. package/util/RelayError.js.flow +4 -1
  242. package/util/RelayFeatureFlags.js.flow +21 -1
  243. package/util/RelayProfiler.js.flow +1 -1
  244. package/util/RelayReplaySubject.js.flow +3 -3
  245. package/util/RelayRuntimeTypes.js.flow +11 -11
  246. package/util/createPayloadFor3DField.js.flow +9 -5
  247. package/util/deepFreeze.js.flow +2 -2
  248. package/util/getFragmentIdentifier.js.flow +1 -1
  249. package/util/getPaginationMetadata.js.flow +1 -1
  250. package/util/getPaginationVariables.js.flow +1 -1
  251. package/util/getPendingOperationsForFragment.js.flow +2 -2
  252. package/util/getRefetchMetadata.js.flow +6 -5
  253. package/util/getValueAtPath.js.flow +3 -3
  254. package/util/handlePotentialSnapshotErrors.js.flow +5 -5
  255. package/util/isEmptyObject.js.flow +1 -1
  256. package/util/isPromise.js.flow +2 -2
  257. package/util/isScalarAndEqual.js.flow +1 -1
  258. package/util/recycleNodesInto.js.flow +2 -2
  259. package/util/registerEnvironmentWithDevTools.js.flow +1 -1
  260. package/util/shallowFreeze.js.flow +1 -1
  261. package/util/stableCopy.js.flow +5 -5
  262. package/util/withProvidedVariables.js.flow +14 -10
@@ -0,0 +1,487 @@
1
+ # Fragments
2
+
3
+ Fragments are one of the distinguishing features of Relay. They let each component declare its own data needs independently, while retaining the efficiency of a single query. In this section, we’ll show how to split a query up into fragments.
4
+
5
+ import Tabs from '@theme/Tabs';
6
+ import TabItem from '@theme/TabItem';
7
+
8
+ * * *
9
+
10
+ To start with, let’s say we want our Story component to show the date that the story was posted. To do that, we need some more data from the server, so we’re going to have to add a field to the query.
11
+
12
+ Go to `Newsfeed.tsx` and find `NewsfeedQuery` so that you can add the new field:
13
+
14
+ ```
15
+ const NewsfeedQuery = graphql`
16
+ query NewsfeedQuery {
17
+ topStory {
18
+ title
19
+ summary
20
+ // change-line
21
+ createdAt // Add this line
22
+ poster {
23
+ name
24
+ profilePicture {
25
+ url
26
+ }
27
+ }
28
+ thumbnail {
29
+ url
30
+ }
31
+ }
32
+ }
33
+ `;
34
+ ```
35
+
36
+ Now we've updated the query, we need to run the Relay compiler so that it knows about the updated Graphql query by running `npm run relay`.
37
+
38
+ Next, go to `Story.tsx` and modify it to display the date:
39
+
40
+ ```
41
+ // change-line
42
+ import Timestamp from './Timestamp';
43
+
44
+ type Props = {
45
+ story: {
46
+ // change-line
47
+ createdAt: string; // Add this line
48
+ ...
49
+ };
50
+ };
51
+
52
+ export default function Story({story}: Props) {
53
+ return (
54
+ <Card>
55
+ <PosterByline poster={story.poster} />
56
+ <Heading>{story.title}</Heading>
57
+ // change-line
58
+ <Timestamp time={story.createdAt} /> // Add this line
59
+ <Image image={story.thumbnail} />
60
+ <StorySummary summary={story.summary} />
61
+ </Card>
62
+ );
63
+ }
64
+ ```
65
+
66
+ The date should now appear. And thanks to GraphQL, we didn't have to write and deploy any new server code.
67
+
68
+ But if you think about it, why should you have had to modify `Newsfeed.tsx`? Shouldn’t React components be self-contained? Why should Newsfeed care about the specific data required by Story? What if the data was required by some child component of Story way down in the hierarchy? What if it was a component that was used in many different places? Then we would have to modify many components whenever its data requirements changed.
69
+
70
+ To avoid these and many other problems, we can move the data requirements for the Story component into `Story.tsx`.
71
+
72
+ We do this by splitting off `Story`’s data requirements into a *fragment* defined in `Story.tsx`. Fragments are separate pieces of GraphQL that the Relay compiler stitches together into complete queries. They allow each component to define its own data requirements, without paying the cost at runtime of each component running its own queries.
73
+
74
+ ![The Relay compiler combines the fragment into the place it's spread](/img/docs/tutorial/fragments-newsfeed-story-compilation.png)
75
+
76
+ Let’s go ahead and split `Story`’s data requirements into a fragment now.
77
+
78
+ * * *
79
+
80
+ ### Step 1 — Define a fragment
81
+
82
+ Add the following to `Story.tsx` (within `src/components`) above the `Story` component:
83
+
84
+ ```
85
+ import { graphql } from 'relay-runtime';
86
+
87
+ const StoryFragment = graphql`
88
+ fragment StoryFragment on Story {
89
+ title
90
+ summary
91
+ createdAt
92
+ poster {
93
+ name
94
+ profilePicture {
95
+ url
96
+ }
97
+ }
98
+ thumbnail {
99
+ url
100
+ }
101
+ }
102
+ `;
103
+ ```
104
+
105
+ Note that we’ve taken all of the selections from within `topStory` in our query and copied them into this new Fragment declaration. Like queries, fragments have a name (`StoryFragment`), which we’ll use in a moment, but they also have a GraphQL type (`Story`) that they’re “on”. This means that this fragment can be used whenever we have a Story node in the graph.
106
+
107
+ ### Step 2 — Spread the fragment
108
+
109
+ Go to `Newsfeed.tsx` and modify `NewsfeedQuery` to look like this:
110
+
111
+ ```
112
+ const NewsfeedQuery = graphql`
113
+ query NewsfeedQuery {
114
+ topStory {
115
+ // change-line
116
+ ...StoryFragment
117
+ }
118
+ }
119
+ `;
120
+ ```
121
+
122
+ We’ve replaced the selections inside `topStory` with `StoryFragment`. The Relay compiler will make sure that all of Story’s data gets fetched from now on, without having to change `Newsfeed`.
123
+
124
+ ### Step 3 — Call useFragment
125
+
126
+ You’ll notice that Story now renders an empty card! All the data is missing! Wasn’t Relay supposed to include the fields selected by the fragment in the `story` object obtained from `useLazyLoadQuery()`?
127
+
128
+ The reason is that Relay hides them. Unless a component specifically asks for the data for a certain fragment, that data will not be visible to the component. This is called *data masking*, and enforces that components don’t implicitly rely on another component’s data dependencies, but declare all of their dependencies within their own fragments. This keeps components self-contained and maintainable.
129
+
130
+ Without data masking, you could never remove a field from a fragment, because it would be hard to verify that some other component somewhere wasn’t using it.
131
+
132
+ To access the data selected by a fragment, we use a hook called `useFragment`. Modify `Story` to look like this:
133
+
134
+ ```
135
+ import { useFragment } from 'react-relay';
136
+
137
+ export default function Story({story}: Props) {
138
+ const data = useFragment(
139
+ // color1
140
+ StoryFragment,
141
+ // color2
142
+ story,
143
+ );
144
+ return (
145
+ <Card>
146
+ <Heading>{data.title}</Heading>
147
+ <PosterByline poster={data.poster} />
148
+ <Timestamp time={data.createdAt} />
149
+ <Image image={data.thumbnail} />
150
+ <StorySummary summary={data.summary} />
151
+ </Card>
152
+ );
153
+ }
154
+ ```
155
+
156
+ `useFragment` takes two arguments:
157
+
158
+ * The <span className="color1">GraphQL tagged string</span> literal for the fragment we want to read
159
+ * The same <span className="color2">story object</span> as we used before, which comes from the place within a GraphQL query where we spread the fragment. This is called a *fragment key*.
160
+
161
+ It returns the data selected by that fragment.
162
+
163
+ :::tip
164
+ We’ve rewritten `story` to `data` (the data returned by `useFragment`) in all of the JSX here; make sure to do the same in your copy of the component, or it won't work.
165
+ :::
166
+
167
+ Fragment keys are the places in a GraphQL query response where a fragment was spread. For example, given the Newsfeed query:
168
+
169
+ ```
170
+ query NewsfeedQuery {
171
+ topStory {
172
+ ...StoryFragment
173
+ }
174
+ }
175
+ ```
176
+
177
+ Then if `queryResult` is the object returned by `useLazyLoadQuery`, `queryResult.topStory` will be a fragment key for `StoryFragment`.
178
+
179
+ Technically, `queryResult.topStory` is an object that contains some hidden fields that tell Relay's `useFragment` where to look for the data it needs. The fragment key specifies both which node to read from (here there's just one story, but soon we'll have multiple stories), and what fields can be read out (the fields selected by that specific fragment). The `useFragment` hook then reads that specific information out of Relay's local data store.
180
+
181
+ :::note
182
+ As we'll see in later examples, you can spread multiple fragments into the same place in a query, and also mix fragment spreads with directly-selected fields.
183
+ :::
184
+
185
+
186
+ ### Step 4 — TypeScript types for fragment refs
187
+
188
+ To complete the fragmentization, we also need to change the type definition for `Props` so that TypeScript knows this component expects to receive a fragment key instead of the raw data.
189
+
190
+ Recall that when you spread a fragment into a query (or another fragment), the part of the query result corresponding to where the fragment is spread becomes a *fragment key* for that fragment. This fragment key is the object that you pass to a component to tell it where in the graph to read the fragment from.
191
+
192
+ To make this type-safe, Relay generates a type that represents the fragment key for that specific fragment — this way, if you try to use a component without spreading its fragment into your query, you won’t be able to provide a fragment key that satisfies the type system. Here are the changes we need to make:
193
+
194
+ ```
195
+ // change-line
196
+ import type {StoryFragment$key} from './__generated__/StoryFragment.graphql';
197
+
198
+ type Props = {
199
+ // change-line
200
+ story: StoryFragment$key;
201
+ };
202
+ ```
203
+
204
+ With that done, we have a `Newsfeed` that no longer has to care what data `Story` requires, yet can still fetch that data up-front within its own query.
205
+
206
+ * * *
207
+
208
+ ## Exercise
209
+
210
+ The `PosterByline` component used by `Story` renders the poster’s name and profile picture. Use these same steps to fragmentize `PosterByline`. You need to:
211
+
212
+ * Declare a `PosterBylineFragment` on `Actor` and specify the fields it needs (`name`, `profilePicture`). The `Actor` type represents a person or organization that can post a story.
213
+ * Spread that fragment within `poster` in `StoryFragment`.
214
+ * Call `useFragment` to retrieve the data.
215
+ * Update the Props to accept a `PosterBylineFragment$key` as the `poster` prop.
216
+
217
+ It’s worth going through these steps a second time, to get the mechanics of using fragments under your fingers. There are a lot of parts here that need to slot together in the right way.
218
+
219
+ Once you’ve done that, let’s look at a basic example of how fragments help an app to scale.
220
+
221
+ * * *
222
+
223
+ ## Reusing a Fragment in Multiple Places
224
+
225
+ A fragment says, given *some* graph node of a particular type, what data to read from that node. The fragment key specifies *which node* in the graph the data is selected from. A re-usable component that specifies a fragment can retrieve the data from different parts of the graph in different contexts, by being passed a different fragment key.
226
+
227
+ For example, notice that the `Image` component is used in two places: directly within `Story` for the story’s thumbnail image, and also within `PosterByline` for the poster’s profile pic. Let’s fragmentize `Image` and see how it can select the data it needs from different places in the graph according to where it is used.
228
+
229
+ ![Fragment can be used in multiple places](/img/docs/tutorial/fragments-image-two-places-compiled.png)
230
+
231
+ ### Step 1 — Define the fragment
232
+
233
+ Open up `Image.tsx` and add a Fragment definition:
234
+
235
+ ```
236
+ import { graphql } from 'relay-runtime';
237
+
238
+ const ImageFragment = graphql`
239
+ fragment ImageFragment on Image {
240
+ url
241
+ }
242
+ `;
243
+ ```
244
+
245
+ ### Step 2 — Spread the fragment
246
+
247
+ Go back to `StoryFragment` and `PosterBylineFragment` and spread `ImageFragment` into it in each place where the `Image` component is what’s using the data:
248
+
249
+ <Tabs>
250
+ <TabItem value="1" label="Story.tsx" default>
251
+
252
+ ```
253
+ const StoryFragment = graphql`
254
+ fragment StoryFragment on Story {
255
+ title
256
+ summary
257
+ postedAt
258
+ poster {
259
+ ...PosterBylineFragment
260
+ }
261
+ thumbnail {
262
+ // change-line
263
+ ...ImageFragment
264
+ }
265
+ }
266
+ `;
267
+ ```
268
+
269
+ </TabItem>
270
+ <TabItem value="2" label="PosterByline.tsx">
271
+
272
+ ```
273
+ const PosterBylineFragment = graphql`
274
+ fragment PosterBylineFragment on Actor {
275
+ name
276
+ profilePicture {
277
+ // change-line
278
+ ...ImageFragment
279
+ }
280
+ }
281
+ `;
282
+ ```
283
+
284
+ </TabItem>
285
+ </Tabs>
286
+
287
+ ### Step 3 — Call useFragment
288
+
289
+ Modify the `Image` component to read the fields using its fragment, and also modify its Props to accept the fragment key:
290
+
291
+ ```
292
+ import { useFragment } from 'react-relay';
293
+ import type { ImageFragment$key } from "./__generated__/ImageFragment.graphql";
294
+
295
+ type Props = {
296
+ image: ImageFragment$key;
297
+ ...
298
+ };
299
+
300
+ function Image({image}: Props) {
301
+ const data = useFragment(ImageFragment, image);
302
+ return <img key={data.url} src={data.url} ... />
303
+ }
304
+ ```
305
+
306
+ ### Step 4 — Modify once, enjoy everywhere
307
+
308
+ Now that we’ve fragmentized Image’s data requirements and co-located them within the component, we can add new data dependencies to Image without modifying any of the components that use it.
309
+
310
+ For example, let’s add an `altText` label for accessibility to the `Image` component.
311
+
312
+ Edit `ImageFragment` as follows:
313
+
314
+ ```
315
+ const ImageFragment = graphql`
316
+ fragment ImageFragment on Image {
317
+ url
318
+ // change-line
319
+ altText
320
+ }
321
+ `;
322
+ ```
323
+
324
+ Now, without editing Story, Newsfeed, or any other component, all of the images within our query will have alt text fetched for them. So we just need to modify `Image` to use the new field:
325
+
326
+ ```
327
+ function Image({image}) {
328
+ // ...
329
+ <img
330
+ // change-line
331
+ alt={data.altText}
332
+ //...
333
+ }
334
+ ```
335
+
336
+ Now *both* the story thumbnail image and the poster’s profile pic will have an alt text. (You can use your browser’s Elements inspector to verify this.)
337
+
338
+ You can imagine how beneficial this is as your codebase gets larger. Each component is self-contained, no matter how many places it’s used in! Even if a component is used in hundreds of places, you can add or remove fields from its data dependencies at will. This is one of the main ways that Relay helps you scale with the size of your app.
339
+
340
+ ![Field added to one fragment is added in all places it's used](/img/docs/tutorial/fragment-image-add-once-compiled.png)
341
+
342
+ Fragments are the building blocks of Relay apps. As such, a lot of Relay features are based on fragments. We’ll look at a few of them in the next sections.
343
+
344
+ * * *
345
+
346
+ ## Fragment arguments and field arguments
347
+
348
+ Currently the `Image` component fetches images at their full size, even if they’ll be displayed at a smaller size. This is inefficient! The `Image` component takes a prop that says what size to show the image at, so it’s controlled by the component that uses `Image`. We’d like in a similar way for the component that uses `Image` to say what size of image to fetch within its fragment.
349
+
350
+ GraphQL fields can accept *arguments* that give the server additional information to fulfill our request. For example, the `url` field on the `Image` type accepts `height` and `width` arguments that the server incorporates into the URL — if we have this fragment:
351
+
352
+ ```
353
+ fragment Example1 on Image {
354
+ url
355
+ }
356
+ ```
357
+
358
+ we might get the URL such as `/images/abcde.jpeg`
359
+
360
+ — whereas if we have this fragment:
361
+
362
+ ```
363
+ fragment Example2 on Image {
364
+ url(height: 100, width: 100)
365
+ }
366
+ ```
367
+
368
+ we might get a URL like `/images/abcde.jpeg?height=100&width=100`
369
+
370
+ Now of course, we don’t want to just hard-code a specific size into `ImageFragment`, because we’d like the `Image` component to fetch a different size in different contexts. To do that, we can make the `ImageFragment` accept *fragment arguments* so that the parent component can specify how large of an image should be fetched. These *fragment arguments* can then be passed into specific fields (in this case `url`) as *field arguments*.
371
+
372
+ To do that, edit `ImageFragment` as follows:
373
+
374
+ ```
375
+ const ImageFragment = graphql`
376
+ fragment ImageFragment on Image
377
+ @argumentDefinitions(
378
+ // color1
379
+ width: {
380
+ // color2
381
+ type: "Int",
382
+ // color3
383
+ defaultValue: null
384
+ }
385
+ height: {
386
+ type: "Int",
387
+ defaultValue: null
388
+ }
389
+ )
390
+ {
391
+ url(
392
+ // color4
393
+ width: $width,
394
+ // color4
395
+ height: $height
396
+ )
397
+ altText
398
+ }
399
+ `;
400
+ ```
401
+
402
+ Let’s break this down:
403
+
404
+ * We’ve added an `@argumentDefinitions` directive to the fragment declaration. This says what arguments the fragment accepts. For each argument, we give:
405
+ * <span className="color1">The name of the argument</span>
406
+ * <span className="color2">Its type</span> (which can be any <a href="https://graphql.org/learn/schema/#scalar-types">GraphQL scalar type</a>)
407
+ * Optionally a <span className="color3">default value </span>— in this case, the default value is null, which lets us fetch the image at its inherent size. If no default value is given, then the argument is required at every place the fragment is used.
408
+ * Then we populate an <span className="color4">argument to a GraphQL field</span> by using the fragment argument as a variable. Here the field arguments and fragment arguments have the same name (as will often be the case), but note: `width:` is the field argument while `$width` is the variable created by the fragment argument.
409
+
410
+ Now the fragment accepts an argument that it passes along to the server via one of the fields it selects.
411
+
412
+ <details>
413
+
414
+ <summary>Deep dive: GraphQL Directives</summary>
415
+
416
+ The syntax for fragment arguments may look rather clumsy. This is because it is based on *directives*, a system for extending the GraphQL language. In GraphQL, any symbol starting with `@` is a directive. Their meaning isn't defined by the GraphQL spec, but is up to the specific client or server implementation.
417
+
418
+ Relay defines [several directives](../../api-reference/graphql-and-directives) to support its features — fragment arguments for one. These directives are not sent to the server, but give instructions to the Relay compiler at build time.
419
+
420
+ The GraphQL spec actually does define the meaning of three directives:
421
+
422
+ * `@deprecated` is used in schema definitions and marks a field as deprecated.
423
+ * `@include` and `@skip` can be used to make the inclusion of a field conditional.
424
+
425
+ Besides these, GraphQL servers can specify additional directives as part of their schemas. And Relay has its own build-time directives, which allow us to extend the language a bit without changing its grammar.
426
+
427
+ </details>
428
+
429
+ ***
430
+
431
+ Now the different fragments using `Image` can pass in the appropriate size for each image:
432
+
433
+ <Tabs>
434
+ <TabItem value="1" label="Story.tsx" default>
435
+
436
+ ```
437
+ const StoryFragment = graphql`
438
+ fragment StoryFragment on Story {
439
+ title
440
+ summary
441
+ postedAt
442
+ poster {
443
+ ...PosterBylineFragment
444
+ }
445
+ thumbnail {
446
+ // change-line
447
+ ...ImageFragment @arguments(width: 400)
448
+ }
449
+ }
450
+ `;
451
+ ```
452
+
453
+ </TabItem>
454
+ <TabItem value="2" label="PosterByline.tsx">
455
+
456
+ ```
457
+ const PosterBylineFragment = graphql`
458
+ fragment PosterBylineFragment on Actor {
459
+ name
460
+ profilePicture {
461
+ // change-line
462
+ ...ImageFragment @arguments(width: 60, height: 60)
463
+ }
464
+ }
465
+ `;
466
+ ```
467
+
468
+ </TabItem>
469
+ </Tabs>
470
+
471
+ Now if you look at the images that our app downloads, you’ll see they’re of the smaller size, saving network bandwidth. Note that although we used integer literals for the value of our fragment arguments, we can also use variables supplied at runtime, as we'll see in later sections.
472
+
473
+ Field arguments (e.g. `url(height: 100)`) are a feature of GraphQL itself, while fragment arguments (as in `@argumentDefinitions` and `@arguments`) are Relay-specific features. The Relay compiler processes these fragment arguments when it combines fragments into queries.
474
+
475
+ ---
476
+
477
+ ## Summary
478
+
479
+ Fragments are the most distinctive aspect of how Relay uses GraphQL. We recommend that every component that displays data and cares about the semantics of that data (so not just a typographic or formatting component) use a GraphQL fragment to declare its data dependencies.
480
+
481
+ * Fragments help you scale: No matter how many places a component is used, you can update its data dependencies in a single place.
482
+ * Fragment data needs to be read out with `useFragment`.
483
+ * `useFragment` takes a *fragment key* which says where in the graph to read from.
484
+ * Fragment keys come from places in a GraphQL response where that fragment was spread.
485
+ * Fragments can define arguments which are used at the point they’re spread. This allows them to be tailored to each situation they're used in.
486
+
487
+ We'll be revisiting many other features of fragments, such as how to refetch the contents of a single fragment without refetching the entire query. First, though, let's make this newsfeed app more newsfeed-like by learning about arrays.
@@ -0,0 +1,172 @@
1
+ # GraphQL and Relay
2
+
3
+ This section is an overview to situate Relay in relation to GraphQL, React, and the other parts of the stack. Don’t worry about understanding every detail, just try to get the gist and then proceed to the next section to start working with code. Much more specifics will be explained as we go through working examples throughout the tutorial.
4
+
5
+ import Tabs from '@theme/Tabs';
6
+ import TabItem from '@theme/TabItem';
7
+
8
+ * * *
9
+
10
+ GraphQL is a language for querying and modifying data on servers. The unique thing about GraphQL is that rather than having a fixed set of API endpoints, your server provides a palette of options that the client can use to request any combination of data that it may need. This allows front-end developers to move more quickly because there is no need to write and deploy new endpoints as data requirements change. It also means that when a new version of the client is released, it can request just the data it needs, without extra fields leftover for compatibility with older versions.
11
+
12
+ GraphQL provides a unified interface for querying data across any kind of back-end. Whether your data is in a relational SQL database, a graph-oriented database, or an armada of microservices, a GraphQL server can collect the data from multiple back-ends and send it to the client in a single response, which is more efficient than issuing separate queries to each service from the client.
13
+
14
+ In a traditional HTTP API, there are URLs that each respond with a fixed set of information:
15
+
16
+ ```
17
+ Request:
18
+ GET /person?id=24601
19
+
20
+ Response:
21
+ {"id": "24601", "name": "Jean Valjean", "age": 64, "occupation": "Mayor"}
22
+ ```
23
+
24
+ In GraphQL, the client asks for the specific information that it wants, and the server responds with just the information that was requested:
25
+
26
+ ```
27
+ Request:
28
+ query {
29
+ person(id: "24601") {
30
+ name
31
+ occupation
32
+ }
33
+ }
34
+
35
+ Response:
36
+ {
37
+ "person": {
38
+ "name": "Jean Valjean",
39
+ "occupation": "Mayor"
40
+ }
41
+ }
42
+ ```
43
+
44
+ Notice that only the specific fields that the client requested were included in the response.
45
+
46
+ As the name suggests, GraphQL organizes data into a *graph*. The graph consists of *nodes* (like objects or records) and *edges* (pointers from one node to another):
47
+
48
+ ![Nodes with fields connected by edges](/img/docs/tutorial/graphql-graph-detail.png)
49
+
50
+ GraphQL lets you follow those edges from one node to another and ask for information about each node that you visit. For example, here we go from a person to their city and get information about the city:
51
+
52
+ <Tabs>
53
+ <TabItem value="1" label="Request" default>
54
+
55
+ ```
56
+ query {
57
+ person(id: "24601") {
58
+ name
59
+ occupation
60
+ location {
61
+ name
62
+ population
63
+ }
64
+ }
65
+ }
66
+ ```
67
+
68
+ ![Query diagram](/img/docs/tutorial/graphql-request.png)
69
+
70
+ </TabItem>
71
+ <TabItem value="2" label="Response">
72
+
73
+ ```
74
+ {
75
+ "person": {
76
+ "name": "Jean Valjean",
77
+ "occupation": "Mayor",
78
+ "location": {
79
+ "name": "Montreuil-sur-Mer",
80
+ "population": 1935
81
+ }
82
+ }
83
+ }
84
+ ```
85
+
86
+ ![Response diagram](/img/docs/tutorial/graphql-response.png)
87
+
88
+ </TabItem>
89
+ </Tabs>
90
+
91
+ This means we can retrieve information about a whole panoply of objects all in one query — in other words, you can efficiently get all the data for a screen in a single request instead of sending many requests one after the other. But you achieve this without writing and maintaining a separate endpoint for each screen in your UI.
92
+
93
+ Instead, your GraphQL server provides a *schema*, which describes what kinds of nodes there are, how they’re connected, and what information each node contains. Then, you pick and choose from this schema to select the information you want.
94
+
95
+ The example app in this tutorial is a newsfeed app, so its schema consists of types such as
96
+
97
+ * `Story`, which represents a newsfeed story — it has fields such as its title, an image, and an *edge* to the person or organization who posted it
98
+ * `Person`, with information such as their name, email, and a list of friends (which are edges to other Persons).
99
+ * `Viewer`, which represents the person viewing the app and has information like their list of newsfeed stories
100
+ * `Image`, which has a URL for the image itself as well as an `alt` text description.
101
+
102
+ The GraphQL language includes a type system and language for specifying the schema. Here’s a snippet from the schema definition for our example app — don’t worry about every detail, it’s just to give you a general idea:
103
+
104
+ ```
105
+ // A newsfeed story. It has fields, some of which are scalars (e.g. strings
106
+ // and numbers) and some that are edges that point to other nodes in the graph,
107
+ // such as the 'thumbnail' and 'poster' fields:
108
+ type Story {
109
+ id: ID!
110
+ category: Category
111
+ title: String
112
+ summary: String
113
+ thumbnail: Image
114
+ poster: Actor
115
+ }
116
+
117
+ // An Actor is an entity that can do something on the site. This is an
118
+ // interface that multiple different types can implement, in this case
119
+ // Person and Organization:
120
+ interface Actor {
121
+ id: ID!
122
+ name: String
123
+ profilePicture: Image
124
+ }
125
+
126
+ // This is a specific type that implements that interface:
127
+ type Person implements Actor {
128
+ id: ID!
129
+ name: String
130
+ email: String
131
+ profilePicture: Image
132
+ location: Location
133
+ }
134
+
135
+ // The schema also lets you define enums, such as the category
136
+ // of a newsfeed story:
137
+ enum Category {
138
+ EDUCATION
139
+ NEWS
140
+ COOKING
141
+ }
142
+ ```
143
+
144
+ Besides queries, GraphQL also lets you send *mutations* that ask the server to update its data. If queries are analogous to HTTP GET requests, then mutations are the equivalent of POST requests. Like POSTs, they let the server respond with updated data. GraphQL also has *subscriptions* which allow for an open connection for realtime updates.
145
+
146
+ (GraphQL is usually implemented over HTTP, so queries and mutations are not only *analogous* to GET and POST, but may be sent as such as well.)
147
+
148
+ * * *
149
+
150
+ Now that we’ve talked about GraphQL, let’s talk about Relay. It has a few different parts and pieces that we’ll briefly go over before diving into the code.
151
+
152
+ Relay is a data management library for the client that’s oriented around GraphQL, but uses it in a very specific way that gets the most benefit from it.
153
+
154
+ For the best performance, you want your app to issue a single request at the beginning of each screen or page instead of having individual components issue their own requests. But the problem with that is that it couples components and screens together, creating a big maintenance problem: If you need some additional data in a specific component, you have to find every screen where that component is used and add the new field to that screen’s query. On the other hand, if you remove the need for a particular field, you have to remove that field from every query again — but this time, are you sure the field isn’t still in use by some *other* component? It becomes very difficult to maintain these big screen-wide queries.
155
+
156
+ Relay’s unique strength is to avoid this tradeoff by letting each component declare its own data requirements locally, but then stitching those requirements together into larger queries. That way you get both performance and maintainability.
157
+
158
+ Relay does this with a *compiler* that scans your JavaScript code for fragments of GraphQL, and then stitches those fragments together into complete queries.
159
+
160
+ ![The Relay Compiler combines fragments into a query](/img/docs/tutorial/graphql-compiler-combines-fragments.png)
161
+
162
+ Besides the compiler, Relay has runtime code that manages the fetching and processing of GraphQL. It maintains a local cache of all the data that has been retrieved (called the *Store*), and vends out to each component the data that belongs to it:
163
+
164
+ ![The Relay Runtime fetches the query and vends out the appropriate data to each component according to its fragment](/img/docs/tutorial/graphql-relay-runtime-fetches-query.png)
165
+
166
+ The advantage of having a centralized Store is that it lets you keep your data consistent when it’s updated. For instance, if your UI has a way for somebody to edit their name, then you can make that update in a single place and every component that displays that person’s name will see the new information, even if they’re on different screens and therefore used different queries to initially retrieve the data. This is because Relay *normalizes* the data as it comes in, meaning that it merges all the data it sees for a single graph node into one place, so it doesn’t have multiple copies of the same node.
167
+
168
+ Indeed, Relay doesn’t just query data, it provides functions to manage the entire lifecycle of querying and updating, including support for optimistic updates and rollbacks. You can paginate, refresh data — all of the basic operations you’ll need to create a UI. Whenever data in the Store is updated, Relay efficiently re-renders just those components that are displaying that particular data.
169
+
170
+ ## Summary
171
+
172
+ GraphQL is a language for modeling data as a graph and querying that data from a server (as well as updating the data). Relay is a React-based client library for GraphQL that lets you build up queries from individual fragments that are co-located with each React component. Once the data has been queried, Relay maintains consistency and re-renders components as the data is updated.