relay-runtime 20.1.1 → 21.0.1

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 (334) hide show
  1. package/experimental.d.ts +34 -0
  2. package/experimental.js +1 -1
  3. package/experimental.js.flow +11 -11
  4. package/handlers/RelayDefaultHandlerProvider.d.ts +12 -0
  5. package/handlers/connection/ConnectionHandler.d.ts +51 -0
  6. package/handlers/connection/ConnectionHandler.js.flow +5 -5
  7. package/handlers/connection/ConnectionInterface.d.ts +40 -0
  8. package/handlers/connection/ConnectionInterface.js.flow +1 -1
  9. package/handlers/connection/MutationHandlers.d.ts +17 -0
  10. package/index.d.ts +274 -0
  11. package/index.js +1 -1
  12. package/index.js.flow +125 -62
  13. package/lib/experimental.js +3 -3
  14. package/lib/index.js +105 -57
  15. package/lib/multi-actor-environment/ActorIdentifier.js +2 -2
  16. package/lib/multi-actor-environment/MultiActorEnvironment.js +3 -1
  17. package/lib/mutations/commitMutation.js +8 -8
  18. package/lib/mutations/validateMutation.js +4 -4
  19. package/lib/query/GraphQLTag.js +3 -3
  20. package/lib/query/fetchQuery.js +15 -3
  21. package/lib/store/DataChecker.js +38 -4
  22. package/lib/store/NormalizationEngine.js +373 -0
  23. package/lib/store/OperationExecutor.js +172 -113
  24. package/lib/store/RelayConcreteVariables.js +1 -1
  25. package/lib/store/RelayErrorTrie.js +2 -2
  26. package/lib/store/RelayExperimentalGraphResponseTransform.js +8 -8
  27. package/lib/store/RelayModernEnvironment.js +26 -19
  28. package/lib/store/RelayModernRecord.js +18 -8
  29. package/lib/store/RelayModernSelector.js +9 -9
  30. package/lib/store/RelayModernStore.js +152 -43
  31. package/lib/store/RelayPublishQueue.js +1 -1
  32. package/lib/store/RelayReader.js +76 -38
  33. package/lib/store/RelayRecordSource.js +6 -0
  34. package/lib/store/RelayReferenceMarker.js +2 -1
  35. package/lib/store/RelayResponseNormalizer.js +88 -55
  36. package/lib/store/RelayStoreSubscriptions.js +34 -10
  37. package/lib/store/RelayStoreUtils.js +8 -1
  38. package/lib/store/ResolverFragments.js +2 -2
  39. package/lib/store/live-resolvers/LiveResolverCache.js +25 -9
  40. package/lib/store/observeFragmentExperimental.js +17 -1
  41. package/lib/store/observeQueryExperimental.js +2 -2
  42. package/lib/subscription/requestSubscription.js +3 -3
  43. package/lib/util/RelayError.js +3 -0
  44. package/lib/util/RelayFeatureFlags.js +6 -2
  45. package/lib/util/RelayReplaySubject.js +4 -4
  46. package/lib/util/handlePotentialSnapshotErrors.js +2 -2
  47. package/lib/util/stableCopy.js +2 -2
  48. package/llm-docs/api-reference/entrypoint-apis/entrypoint-container.mdx +38 -0
  49. package/llm-docs/api-reference/entrypoint-apis/load-entrypoint.mdx +77 -0
  50. package/llm-docs/api-reference/entrypoint-apis/use-entrypoint-loader.mdx +99 -0
  51. package/llm-docs/api-reference/graphql/graphql-directives.mdx +378 -0
  52. package/llm-docs/api-reference/hooks/_use-lazy-load-query-extra.mdx +16 -0
  53. package/llm-docs/api-reference/hooks/load-query.mdx +84 -0
  54. package/llm-docs/api-reference/hooks/relay-environment-provider.mdx +78 -0
  55. package/llm-docs/api-reference/hooks/use-client-query.mdx +65 -0
  56. package/llm-docs/api-reference/hooks/use-fragment.mdx +69 -0
  57. package/llm-docs/api-reference/hooks/use-lazy-load-query.mdx +62 -0
  58. package/llm-docs/api-reference/hooks/use-mutation.mdx +94 -0
  59. package/llm-docs/api-reference/hooks/use-pagination-fragment.mdx +166 -0
  60. package/llm-docs/api-reference/hooks/use-prefetchable-forward-pagination-fragment.mdx +134 -0
  61. package/llm-docs/api-reference/hooks/use-preloaded-query.mdx +84 -0
  62. package/llm-docs/api-reference/hooks/use-query-loader.mdx +95 -0
  63. package/llm-docs/api-reference/hooks/use-refetchable-fragment.mdx +122 -0
  64. package/llm-docs/api-reference/hooks/use-relay-environment.mdx +37 -0
  65. package/llm-docs/api-reference/hooks/use-subscription.mdx +66 -0
  66. package/llm-docs/api-reference/relay-resolvers/docblock-format.mdx +321 -0
  67. package/llm-docs/api-reference/relay-resolvers/runtime-functions.mdx +94 -0
  68. package/llm-docs/api-reference/relay-runtime/commit-mutation.mdx +65 -0
  69. package/llm-docs/api-reference/relay-runtime/fetch-query.mdx +118 -0
  70. package/llm-docs/api-reference/relay-runtime/field-logger.mdx +170 -0
  71. package/llm-docs/api-reference/relay-runtime/observe-fragment.mdx +92 -0
  72. package/llm-docs/api-reference/relay-runtime/relay-environment.mdx +53 -0
  73. package/llm-docs/api-reference/relay-runtime/request-subscription.mdx +54 -0
  74. package/llm-docs/api-reference/relay-runtime/runtime-configuration.mdx +52 -0
  75. package/llm-docs/api-reference/relay-runtime/store.mdx +734 -0
  76. package/llm-docs/api-reference/relay-runtime/wait-for-fragment-data.mdx +89 -0
  77. package/llm-docs/api-reference/types/CacheConfig.mdx +8 -0
  78. package/llm-docs/api-reference/types/Disposable.mdx +4 -0
  79. package/llm-docs/api-reference/types/GraphQLSubscriptionConfig.mdx +17 -0
  80. package/llm-docs/api-reference/types/MutationConfig.mdx +31 -0
  81. package/llm-docs/api-reference/types/SelectorStoreUpdater.mdx +6 -0
  82. package/llm-docs/api-reference/types/UploadableMap.mdx +3 -0
  83. package/llm-docs/community/learning-resources.mdx +64 -0
  84. package/llm-docs/debugging/declarative-mutation-directives.mdx +34 -0
  85. package/llm-docs/debugging/disallowed-id-types-error.mdx +43 -0
  86. package/llm-docs/debugging/inconsistent-typename-error.mdx +47 -0
  87. package/llm-docs/debugging/relay-devtools.mdx +73 -0
  88. package/llm-docs/debugging/why-null.mdx +116 -0
  89. package/llm-docs/editor-support.mdx +55 -0
  90. package/llm-docs/error-reference/unknown-field.mdx +36 -0
  91. package/llm-docs/getting-started/babel-plugin.mdx +31 -0
  92. package/llm-docs/getting-started/compiler-config.mdx +25 -0
  93. package/llm-docs/getting-started/compiler.mdx +98 -0
  94. package/llm-docs/getting-started/lint-rules.mdx +87 -0
  95. package/llm-docs/getting-started/production.mdx +30 -0
  96. package/llm-docs/getting-started/quick-start.mdx +216 -0
  97. package/llm-docs/glossary/glossary.mdx +1040 -0
  98. package/llm-docs/guided-tour/list-data/advanced-pagination.mdx +157 -0
  99. package/llm-docs/guided-tour/list-data/connections.mdx +81 -0
  100. package/llm-docs/guided-tour/list-data/pagination.mdx +193 -0
  101. package/llm-docs/guided-tour/list-data/rendering-connections.mdx +112 -0
  102. package/llm-docs/guided-tour/list-data/streaming-pagination.mdx +87 -0
  103. package/llm-docs/guided-tour/managing-data-outside-react/retaining-queries.mdx +51 -0
  104. package/llm-docs/guided-tour/refetching/refetching-queries-with-different-data.mdx +337 -0
  105. package/llm-docs/guided-tour/refetching/refreshing-queries.mdx +350 -0
  106. package/llm-docs/guided-tour/rendering/environment.mdx +59 -0
  107. package/llm-docs/guided-tour/rendering/error-states.mdx +295 -0
  108. package/llm-docs/guided-tour/rendering/fragments.mdx +354 -0
  109. package/llm-docs/guided-tour/rendering/loading-states.mdx +245 -0
  110. package/llm-docs/guided-tour/rendering/queries.mdx +261 -0
  111. package/llm-docs/guided-tour/rendering/variables.mdx +233 -0
  112. package/llm-docs/guided-tour/reusing-cached-data/fetch-policies.mdx +56 -0
  113. package/llm-docs/guided-tour/reusing-cached-data/filling-in-missing-data.mdx +102 -0
  114. package/llm-docs/guided-tour/reusing-cached-data/introduction.mdx +22 -0
  115. package/llm-docs/guided-tour/reusing-cached-data/presence-of-data.mdx +93 -0
  116. package/llm-docs/guided-tour/reusing-cached-data/rendering-partially-cached-data.mdx +175 -0
  117. package/llm-docs/guided-tour/reusing-cached-data/staleness-of-data.mdx +116 -0
  118. package/llm-docs/guided-tour/updating-data/client-only-data.mdx +115 -0
  119. package/llm-docs/guided-tour/updating-data/graphql-mutations.mdx +334 -0
  120. package/llm-docs/guided-tour/updating-data/graphql-subscriptions.mdx +279 -0
  121. package/llm-docs/guided-tour/updating-data/imperatively-modifying-linked-fields.mdx +511 -0
  122. package/llm-docs/guided-tour/updating-data/imperatively-modifying-store-data-legacy.mdx +142 -0
  123. package/llm-docs/guided-tour/updating-data/imperatively-modifying-store-data.mdx +275 -0
  124. package/llm-docs/guided-tour/updating-data/introduction.mdx +25 -0
  125. package/llm-docs/guided-tour/updating-data/local-data-updates.mdx +71 -0
  126. package/llm-docs/guided-tour/updating-data/typesafe-updaters-faq.mdx +83 -0
  127. package/llm-docs/guided-tour/updating-data/updating-connections.mdx +592 -0
  128. package/llm-docs/guides/alias-directive.mdx +160 -0
  129. package/llm-docs/guides/catch-directive.mdx +167 -0
  130. package/llm-docs/guides/client-schema-extensions.mdx +208 -0
  131. package/llm-docs/guides/codemods.mdx +79 -0
  132. package/llm-docs/guides/data-driven-dependencies/client-3d.mdx +255 -0
  133. package/llm-docs/guides/data-driven-dependencies/configuration.mdx +127 -0
  134. package/llm-docs/guides/data-driven-dependencies/introduction.mdx +39 -0
  135. package/llm-docs/guides/data-driven-dependencies/server-3d.mdx +664 -0
  136. package/llm-docs/guides/document-comparison.mdx +106 -0
  137. package/llm-docs/guides/graphql-server-specification.mdx +453 -0
  138. package/llm-docs/guides/network-layer.mdx +69 -0
  139. package/llm-docs/guides/persisted-queries.mdx +328 -0
  140. package/llm-docs/guides/relay-resolvers/context.mdx +99 -0
  141. package/llm-docs/guides/relay-resolvers/defining-fields.mdx +151 -0
  142. package/llm-docs/guides/relay-resolvers/defining-types.mdx +164 -0
  143. package/llm-docs/guides/relay-resolvers/deprecated.mdx +27 -0
  144. package/llm-docs/guides/relay-resolvers/derived-fields.mdx +127 -0
  145. package/llm-docs/guides/relay-resolvers/descriptions.mdx +44 -0
  146. package/llm-docs/guides/relay-resolvers/enabling.mdx +41 -0
  147. package/llm-docs/guides/relay-resolvers/errors.mdx +64 -0
  148. package/llm-docs/guides/relay-resolvers/field-arguments.mdx +63 -0
  149. package/llm-docs/guides/relay-resolvers/introduction.mdx +62 -0
  150. package/llm-docs/guides/relay-resolvers/limitations.mdx +30 -0
  151. package/llm-docs/guides/relay-resolvers/live-fields.mdx +164 -0
  152. package/llm-docs/guides/relay-resolvers/return-types.mdx +161 -0
  153. package/llm-docs/guides/relay-resolvers/suspense.mdx +41 -0
  154. package/llm-docs/guides/required-directive.mdx +240 -0
  155. package/llm-docs/guides/semantic-nullability.mdx +93 -0
  156. package/llm-docs/guides/testing-relay-components.mdx +642 -0
  157. package/llm-docs/guides/testing-relay-with-preloaded-queries.mdx +160 -0
  158. package/llm-docs/guides/throw-on-field-error-directive.mdx +58 -0
  159. package/llm-docs/guides/type-emission.mdx +414 -0
  160. package/llm-docs/home.mdx +32 -0
  161. package/llm-docs/principles-and-architecture/architecture-overview.mdx +24 -0
  162. package/llm-docs/principles-and-architecture/compiler-architecture.mdx +106 -0
  163. package/llm-docs/principles-and-architecture/runtime-architecture.mdx +249 -0
  164. package/llm-docs/principles-and-architecture/thinking-in-graphql.mdx +309 -0
  165. package/llm-docs/principles-and-architecture/thinking-in-relay.mdx +104 -0
  166. package/llm-docs/principles-and-architecture/videos.mdx +50 -0
  167. package/llm-docs/tutorial/arrays-lists.mdx +126 -0
  168. package/llm-docs/tutorial/fragments-1.mdx +487 -0
  169. package/llm-docs/tutorial/graphql.mdx +172 -0
  170. package/llm-docs/tutorial/interfaces-polymorphism.mdx +161 -0
  171. package/llm-docs/tutorial/intro.mdx +58 -0
  172. package/llm-docs/tutorial/mutations-updates.mdx +624 -0
  173. package/llm-docs/tutorial/organizing-mutations-queries-and-subscriptions.mdx +13 -0
  174. package/llm-docs/tutorial/queries-1.mdx +267 -0
  175. package/llm-docs/tutorial/queries-2.mdx +389 -0
  176. package/llm-docs/tutorial/refetchable-fragments.mdx +352 -0
  177. package/multi-actor-environment/ActorIdentifier.d.ts +17 -0
  178. package/multi-actor-environment/ActorIdentifier.js.flow +2 -2
  179. package/multi-actor-environment/ActorSpecificEnvironment.js.flow +15 -15
  180. package/multi-actor-environment/ActorUtils.js.flow +1 -1
  181. package/multi-actor-environment/MultiActorEnvironment.d.ts +123 -0
  182. package/multi-actor-environment/MultiActorEnvironment.js.flow +32 -24
  183. package/multi-actor-environment/MultiActorEnvironmentTypes.d.ts +225 -0
  184. package/multi-actor-environment/MultiActorEnvironmentTypes.js.flow +6 -6
  185. package/multi-actor-environment/index.d.ts +14 -0
  186. package/multi-actor-environment.d.ts +8 -0
  187. package/mutations/RelayDeclarativeMutationConfig.d.ts +70 -0
  188. package/mutations/RelayDeclarativeMutationConfig.js.flow +9 -9
  189. package/mutations/RelayRecordProxy.js.flow +8 -11
  190. package/mutations/RelayRecordSourceMutator.js.flow +4 -4
  191. package/mutations/RelayRecordSourceProxy.js.flow +4 -4
  192. package/mutations/RelayRecordSourceSelectorProxy.js.flow +6 -6
  193. package/mutations/applyOptimisticMutation.d.ts +25 -0
  194. package/mutations/applyOptimisticMutation.js.flow +2 -2
  195. package/mutations/commitLocalUpdate.d.ts +10 -0
  196. package/mutations/commitMutation.d.ts +48 -0
  197. package/mutations/commitMutation.js.flow +21 -17
  198. package/mutations/createUpdatableProxy.js.flow +19 -19
  199. package/mutations/readUpdatableFragment.js.flow +3 -3
  200. package/mutations/readUpdatableQuery.js.flow +3 -3
  201. package/mutations/validateMutation.js.flow +7 -7
  202. package/network/RelayNetwork.d.ts +12 -0
  203. package/network/RelayNetworkTypes.d.ts +145 -0
  204. package/network/RelayNetworkTypes.js.flow +18 -18
  205. package/network/RelayObservable.d.ts +197 -0
  206. package/network/RelayObservable.js.flow +32 -30
  207. package/network/RelayQueryResponseCache.d.ts +16 -0
  208. package/network/RelayQueryResponseCache.js.flow +3 -3
  209. package/network/wrapNetworkWithLogObserver.js.flow +1 -1
  210. package/package.json +2 -1
  211. package/query/GraphQLTag.d.ts +45 -0
  212. package/query/GraphQLTag.js.flow +22 -10
  213. package/query/fetchQuery.d.ts +21 -0
  214. package/query/fetchQuery.js.flow +23 -10
  215. package/query/fetchQueryInternal.d.ts +26 -0
  216. package/query/fetchQueryInternal.js.flow +4 -4
  217. package/query/fetchQuery_DEPRECATED.d.ts +17 -0
  218. package/query/fetchQuery_DEPRECATED.js.flow +1 -1
  219. package/store/ClientID.d.ts +14 -0
  220. package/store/DataChecker.js.flow +51 -15
  221. package/store/NormalizationEngine.js.flow +782 -0
  222. package/store/OperationExecutor.d.ts +51 -0
  223. package/store/OperationExecutor.js.flow +204 -98
  224. package/store/RelayConcreteVariables.js.flow +5 -5
  225. package/store/RelayErrorTrie.js.flow +12 -12
  226. package/store/RelayExperimentalGraphResponseHandler.js.flow +3 -3
  227. package/store/RelayExperimentalGraphResponseTransform.js.flow +10 -10
  228. package/store/RelayModernEnvironment.d.ts +97 -0
  229. package/store/RelayModernEnvironment.js.flow +58 -43
  230. package/store/RelayModernFragmentSpecResolver.js.flow +1 -1
  231. package/store/RelayModernOperationDescriptor.d.ts +28 -0
  232. package/store/RelayModernOperationDescriptor.js.flow +1 -1
  233. package/store/RelayModernRecord.d.ts +92 -0
  234. package/store/RelayModernRecord.js.flow +44 -20
  235. package/store/RelayModernSelector.d.ts +123 -0
  236. package/store/RelayModernSelector.js.flow +21 -21
  237. package/store/RelayModernStore.d.ts +57 -0
  238. package/store/RelayModernStore.js.flow +219 -58
  239. package/store/RelayOperationTracker.d.ts +29 -0
  240. package/store/RelayOperationTracker.js.flow +2 -2
  241. package/store/RelayOptimisticRecordSource.js.flow +2 -2
  242. package/store/RelayPublishQueue.js.flow +29 -20
  243. package/store/RelayReader.js.flow +129 -57
  244. package/store/RelayRecordSource.d.ts +26 -0
  245. package/store/RelayRecordSource.js.flow +10 -0
  246. package/store/RelayRecordState.d.ts +28 -0
  247. package/store/RelayRecordState.js.flow +1 -1
  248. package/store/RelayReferenceMarker.js.flow +5 -4
  249. package/store/RelayResponseNormalizer.d.ts +28 -0
  250. package/store/RelayResponseNormalizer.js.flow +130 -62
  251. package/store/RelayStoreSubscriptions.js.flow +52 -8
  252. package/store/RelayStoreTypes.d.ts +1327 -0
  253. package/store/RelayStoreTypes.js.flow +371 -278
  254. package/store/RelayStoreUtils.d.ts +86 -0
  255. package/store/RelayStoreUtils.js.flow +16 -8
  256. package/store/ResolverCache.js.flow +2 -2
  257. package/store/ResolverFragments.d.ts +43 -0
  258. package/store/ResolverFragments.js.flow +22 -14
  259. package/store/StoreInspector.js.flow +7 -8
  260. package/store/ViewerPattern.d.ts +11 -0
  261. package/store/cloneRelayHandleSourceField.js.flow +1 -1
  262. package/store/cloneRelayScalarHandleSourceField.js.flow +1 -1
  263. package/store/createFragmentSpecResolver.d.ts +16 -0
  264. package/store/createRelayContext.js.flow +1 -1
  265. package/store/createRelayLoggingContext.js.flow +4 -4
  266. package/store/defaultGetDataID.js.flow +2 -2
  267. package/store/isRelayModernEnvironment.d.ts +8 -0
  268. package/store/isRelayModernEnvironment.js.flow +4 -2
  269. package/store/live-resolvers/LiveResolverCache.js.flow +55 -20
  270. package/store/live-resolvers/LiveResolverSuspenseSentinel.js.flow +3 -3
  271. package/store/live-resolvers/getOutputTypeRecordIDs.js.flow +1 -1
  272. package/store/live-resolvers/isLiveStateValue.js.flow +2 -2
  273. package/store/live-resolvers/resolverDataInjector.d.ts +27 -0
  274. package/store/live-resolvers/resolverDataInjector.js.flow +8 -5
  275. package/store/observeFragmentExperimental.d.ts +46 -0
  276. package/store/observeFragmentExperimental.js.flow +50 -21
  277. package/store/observeQueryExperimental.d.ts +30 -0
  278. package/store/observeQueryExperimental.js.flow +5 -5
  279. package/store/readInlineData.d.ts +19 -0
  280. package/store/readInlineData.js.flow +5 -5
  281. package/store/waitForFragmentExperimental.d.ts +49 -0
  282. package/store/waitForFragmentExperimental.js.flow +3 -3
  283. package/subscription/requestSubscription.d.ts +27 -0
  284. package/subscription/requestSubscription.js.flow +10 -10
  285. package/util/JSResourceTypes.flow.js.flow +4 -4
  286. package/util/NormalizationNode.d.ts +235 -0
  287. package/util/NormalizationNode.js.flow +127 -123
  288. package/util/ReaderNode.d.ts +264 -0
  289. package/util/ReaderNode.js.flow +156 -151
  290. package/util/RelayConcreteNode.d.ts +120 -0
  291. package/util/RelayConcreteNode.js.flow +32 -32
  292. package/util/RelayError.d.ts +13 -0
  293. package/util/RelayError.js.flow +4 -1
  294. package/util/RelayFeatureFlags.d.ts +40 -0
  295. package/util/RelayFeatureFlags.js.flow +21 -1
  296. package/util/RelayProfiler.d.ts +121 -0
  297. package/util/RelayProfiler.js.flow +1 -1
  298. package/util/RelayReplaySubject.d.ts +25 -0
  299. package/util/RelayReplaySubject.js.flow +3 -3
  300. package/util/RelayRuntimeTypes.d.ts +59 -0
  301. package/util/RelayRuntimeTypes.js.flow +36 -33
  302. package/util/createPayloadFor3DField.d.ts +17 -0
  303. package/util/createPayloadFor3DField.js.flow +9 -5
  304. package/util/deepFreeze.d.ts +8 -0
  305. package/util/deepFreeze.js.flow +2 -2
  306. package/util/getFragmentIdentifier.d.ts +10 -0
  307. package/util/getFragmentIdentifier.js.flow +1 -1
  308. package/util/getPaginationMetadata.d.ts +20 -0
  309. package/util/getPaginationMetadata.js.flow +1 -1
  310. package/util/getPaginationVariables.d.ts +20 -0
  311. package/util/getPaginationVariables.js.flow +1 -1
  312. package/util/getPendingOperationsForFragment.d.ts +18 -0
  313. package/util/getPendingOperationsForFragment.js.flow +2 -2
  314. package/util/getRefetchMetadata.d.ts +19 -0
  315. package/util/getRefetchMetadata.js.flow +6 -5
  316. package/util/getRelayHandleKey.d.ts +8 -0
  317. package/util/getRequestIdentifier.d.ts +17 -0
  318. package/util/getValueAtPath.d.ts +8 -0
  319. package/util/getValueAtPath.js.flow +3 -3
  320. package/util/handlePotentialSnapshotErrors.d.ts +14 -0
  321. package/util/handlePotentialSnapshotErrors.js.flow +5 -5
  322. package/util/isEmptyObject.js.flow +1 -1
  323. package/util/isPromise.d.ts +8 -0
  324. package/util/isPromise.js.flow +2 -2
  325. package/util/isScalarAndEqual.d.ts +8 -0
  326. package/util/isScalarAndEqual.js.flow +1 -1
  327. package/util/recycleNodesInto.d.ts +8 -0
  328. package/util/recycleNodesInto.js.flow +2 -2
  329. package/util/registerEnvironmentWithDevTools.js.flow +1 -1
  330. package/util/shallowFreeze.js.flow +1 -1
  331. package/util/stableCopy.d.ts +8 -0
  332. package/util/stableCopy.js.flow +5 -5
  333. package/util/withProvidedVariables.d.ts +19 -0
  334. package/util/withProvidedVariables.js.flow +14 -10
@@ -0,0 +1,309 @@
1
+ ---
2
+ id: thinking-in-graphql
3
+ title: Thinking in GraphQL
4
+ slug: /principles-and-architecture/thinking-in-graphql/
5
+ description: Relay guide to thinking in GraphQL
6
+ keywords:
7
+ - GraphQL
8
+ ---
9
+
10
+ import DocsRating from '@site/src/core/DocsRating';
11
+
12
+ GraphQL presents new ways for clients to fetch data by focusing on the needs of product developers and client applications. It provides a way for developers to specify the precise data needed for a view and enables a client to fetch that data in a single network request. Compared to traditional approaches such as REST, GraphQL helps applications to fetch data more efficiently (compared to resource-oriented REST approaches) and avoid duplication of server logic (which can occur with custom endpoints). Furthermore, GraphQL helps developers to decouple product code and server logic. For example, a product can fetch more or less information without requiring a change to every relevant server endpoint. It's a great way to fetch data.
13
+
14
+ In this article we'll explore what it means to build a GraphQL client framework and how this compares to clients for more traditional REST systems. Along the way we'll look at the design decisions behind Relay and see that it's not just a GraphQL client but also a framework for _declarative data-fetching_. Let's start at the beginning and fetch some data!
15
+
16
+ ## Fetching Data
17
+
18
+ Imagine we have a simple application that fetches a list of stories, and some details about each one. Here's how that might look in resource-oriented REST:
19
+
20
+ ```javascript
21
+ // Fetch the list of story IDs but not their details:
22
+ rest.get('/stories').then(stories =>
23
+ // This resolves to a list of items with linked resources:
24
+ // `[ { href: "http://.../story/1" }, ... ]`
25
+ Promise.all(stories.map(story =>
26
+ rest.get(story.href) // Follow the links
27
+ ))
28
+ ).then(stories => {
29
+ // This resolves to a list of story items:
30
+ // `[ { id: "...", text: "..." } ]`
31
+ console.log(stories);
32
+ });
33
+ ```
34
+
35
+ Note that this approach requires _n+1_ requests to the server: 1 to fetch the list, and _n_ to fetch each item. With GraphQL we can fetch the same data in a single network request to the server (without creating a custom endpoint that we'd then have to maintain):
36
+
37
+ ```javascript
38
+ graphql.get(`query { stories { id, text } }`).then(
39
+ stories => {
40
+ // A list of story items:
41
+ // `[ { id: "...", text: "..." } ]`
42
+ console.log(stories);
43
+ }
44
+ );
45
+ ```
46
+
47
+ So far we're just using GraphQL as a more efficient version of typical REST approaches. Note two important benefits in the GraphQL version:
48
+
49
+ - All data is fetched in a single round trip.
50
+ - The client and server are decoupled: the client specifies the data needed instead of _relying on_ the server endpoint to return the correct data.
51
+
52
+ For a simple application that's already a nice improvement.
53
+
54
+ ## Client Caching
55
+
56
+ Repeatedly refetching information from the server can get quite slow. For example, navigating from the list of stories, to a list item, and back to the list of stories means we have to refetch the whole list. We'll solve this with the standard solution: _caching_.
57
+
58
+ In a resource-oriented REST system, we can maintain a **response cache** based on URIs:
59
+
60
+ ```javascript
61
+ var _cache = new Map();
62
+ rest.get = uri => {
63
+ if (!_cache.has(uri)) {
64
+ _cache.set(uri, fetch(uri));
65
+ }
66
+ return _cache.get(uri);
67
+ };
68
+ ```
69
+
70
+ Response-caching can also be applied to GraphQL. A basic approach would work similarly to the REST version. The text of the query itself can be used as a cache key:
71
+
72
+ ```javascript
73
+ var _cache = new Map();
74
+ graphql.get = queryText => {
75
+ if (!_cache.has(queryText)) {
76
+ _cache.set(queryText, fetchGraphQL(queryText));
77
+ }
78
+ return _cache.get(queryText);
79
+ };
80
+ ```
81
+
82
+ Now, requests for previously cached data can be answered immediately without making a network request. This is a practical approach to improving the perceived performance of an application. However, this method of caching can cause problems with data consistency.
83
+
84
+ ## Cache Consistency
85
+
86
+ With GraphQL it is very common for the results of multiple queries to overlap. However, our response cache from the previous section doesn't account for this overlap — it caches based on distinct queries. For example, if we issue a query to fetch stories:
87
+
88
+ ```graphql
89
+ query { stories { id, text, likeCount } }
90
+ ```
91
+
92
+ and then later refetch one of the stories whose `likeCount` has since been incremented:
93
+
94
+ ```graphql
95
+ query { story(id: "123") { id, text, likeCount } }
96
+ ```
97
+
98
+ We'll now see different `likeCount`s depending on how the story is accessed. A view that uses the first query will see an outdated count, while a view using the second query will see the updated count.
99
+
100
+ ### Caching A Graph
101
+
102
+ The solution to caching GraphQL is to normalize the hierarchical response into a flat collection of **records**. Relay implements this cache as a map from IDs to records. Each record is a map from field names to field values. Records may also link to other records (allowing it to describe a cyclic graph), and these links are stored as a special value type that references back into the top-level map. With this approach each server record is stored _once_ regardless of how it is fetched.
103
+
104
+ Here's an example query that fetches a story's text and its author's name:
105
+
106
+ ```graphql
107
+ query {
108
+ story(id: "1") {
109
+ text,
110
+ author {
111
+ name
112
+ }
113
+ }
114
+ }
115
+ ```
116
+
117
+ And here's a possible response:
118
+
119
+ ```json
120
+ {
121
+ "query": {
122
+ "story": {
123
+ "text": "Relay is open-source!",
124
+ "author": {
125
+ "name": "Jan"
126
+ }
127
+ }
128
+ }
129
+ }
130
+ ```
131
+
132
+ Although the response is hierarchical, we'll cache it by flattening all the records. Here is an example of how Relay would cache this query response:
133
+
134
+ ```javascript
135
+ Map {
136
+ // `story(id: "1")`
137
+ 1: Map {
138
+ text: 'Relay is open-source!',
139
+ author: Link(2),
140
+ },
141
+ // `story.author`
142
+ 2: Map {
143
+ name: 'Jan',
144
+ },
145
+ };
146
+ ```
147
+
148
+ This is only a simple example: in reality the cache must handle one-to-many associations and pagination (among other things).
149
+
150
+ ### Using The Cache
151
+
152
+ So how do we use this cache? Let's look at two operations: writing to the cache when a response is received, and reading from the cache to determine if a query can be fulfilled locally (the equivalent to `_cache.has(key)` above, but for a graph).
153
+
154
+ ### Populating The Cache
155
+
156
+ Populating the cache involves walking a hierarchical GraphQL response and creating or updating normalized cache records. At first it may seem that the response alone is sufficient to process the response, but in fact this is only true for very simple queries. Consider `user(id: "456") { photo(size: 32) { uri } }` — how should we store `photo`? Using `photo` as the field name in the cache won't work because a different query might fetch the same field but with different argument values (e.g. `photo(size: 64) {...}`). A similar issue occurs with pagination. If we fetch the 11th to 20th stories with `stories(first: 10, offset: 10)`, these new results should be _appended_ to the existing list.
157
+
158
+ Therefore, a normalized response cache for GraphQL requires processing payloads and queries in parallel. For example, the `photo` field from above might be cached with a generated field name such as `photo_size(32)` in order to uniquely identify the field and its argument values.
159
+
160
+ ### Reading From Cache
161
+
162
+ To read from the cache we can walk a query and resolve each field. But wait: that sounds _exactly_ like what a GraphQL server does when it processes a query. And it is! Reading from the cache is a special case of an executor where a) there's no need for user-defined field functions because all results come from a fixed data structure and b) results are always synchronous — we either have the data cached or we don't.
163
+
164
+ Relay implements several variations of **query traversal**: operations that walk a query alongside some other data such as the cache or a response payload. For example, when a query is fetched Relay performs a "diff" traversal to determine what fields are missing (much like React diffs virtual DOM trees). This can reduce the amount of data fetched in many common cases and even allow Relay to avoid network requests at all when queries are fully cached.
165
+
166
+ ### Cache Updates
167
+
168
+ Note that this normalized cache structure allows overlapping results to be cached without duplication. Each record is stored once regardless of how it is fetched. Let's return to the earlier example of inconsistent data and see how this cache helps in that scenario.
169
+
170
+ The first query was for a list of stories:
171
+
172
+ ```graphql
173
+ query { stories { id, text, likeCount } }
174
+ ```
175
+
176
+ With a normalized response cache, a record would be created for each story in the list. The `stories` field would store links to each of these records.
177
+
178
+ The second query refetched the information for one of those stories:
179
+
180
+ ```graphql
181
+ query { story(id: "123") { id, text, likeCount } }
182
+ ```
183
+
184
+ When this response is normalized, Relay can detect that this result overlaps with existing data based on its `id`. Rather than create a new record, Relay will update the existing `123` record. The new `likeCount` is therefore available to _both_ queries, as well as any other query that might reference this story.
185
+
186
+ ## Data/View Consistency
187
+
188
+ A normalized cache ensures that the _cache_ is consistent. But what about our views? Ideally, our React views would always reflect the current information from the cache.
189
+
190
+ Consider rendering the text and comments of a story along with the corresponding author names and photos. Here's the GraphQL query:
191
+
192
+ ```graphql
193
+ query {
194
+ story(id: "1") {
195
+ text,
196
+ author { name, photo },
197
+ comments {
198
+ text,
199
+ author { name, photo }
200
+ }
201
+ }
202
+ }
203
+ ```
204
+
205
+ After initially fetching this story our cache might be as follows. Note that the story and comment both link to the same record as `author`:
206
+
207
+ ```
208
+ // Note: This is pseudo-code for `Map` initialization to make the structure
209
+ // more obvious.
210
+ Map {
211
+ // `story(id: "1")`
212
+ 1: Map {
213
+ text: 'got GraphQL?',
214
+ author: Link(2),
215
+ comments: [Link(3)],
216
+ },
217
+ // `story.author`
218
+ 2: Map {
219
+ name: 'Yuzhi',
220
+ photo: 'http://.../photo1.jpg',
221
+ },
222
+ // `story.comments[0]`
223
+ 3: Map {
224
+ text: 'Here\'s how to get one!',
225
+ author: Link(2),
226
+ },
227
+ }
228
+ ```
229
+
230
+ The author of this story also commented on it — quite common. Now imagine that some other view fetches new information about the author, and her profile photo has changed to a new URI. Here's the _only_ part of our cached data that changes:
231
+
232
+ ```
233
+ Map {
234
+ ...
235
+ 2: Map {
236
+ ...
237
+ photo: 'http://.../photo2.jpg',
238
+ },
239
+ }
240
+ ```
241
+
242
+ The value of the `photo` field has changed; and therefore the record `2` has also changed. And that's it. Nothing else in the _cache_ is affected. But clearly our _view_ needs to reflect the update: both instances of the author in the UI (as story author and comment author) need to show the new photo.
243
+
244
+ A standard response is to "just use immutable data structures" — but let's see what would happen if we did:
245
+
246
+ ```
247
+ ImmutableMap {
248
+ 1: ImmutableMap // same as before
249
+ 2: ImmutableMap {
250
+ ... // other fields unchanged
251
+ photo: 'http://.../photo2.jpg',
252
+ },
253
+ 3: ImmutableMap // same as before
254
+ }
255
+ ```
256
+
257
+ If we replace `2` with a new immutable record, we'll also get a new immutable instance of the cache object. However, records `1` and `3` are untouched. Because the data is normalized, we can't tell that `story`'s contents have changed just by looking at the `story` record alone.
258
+
259
+ ### Achieving View Consistency
260
+
261
+ There are a variety of solutions for keeping views up to date with a flattened cache. The approach that Relay takes is to maintain a mapping from each UI view to the set of IDs it references. In this case, the story view would subscribe to updates on the story (`1`), the author (`2`), and the comments (`3` and any others). When writing data into the cache, Relay tracks which IDs are affected and notifies _only_ the views that are subscribed to those IDs. The affected views re-render, and unaffected views opt-out of re-rendering for better performance (Relay provides a safe but effective default `shouldComponentUpdate`). Without this strategy, every view would re-render for even the tiniest change.
262
+
263
+ Note that this solution will also work for _writes_: any update to the cache will notify the affected views, and writes are just another thing that updates the cache.
264
+
265
+ ## Mutations
266
+
267
+ So far we've looked at the process of querying data and keeping views up to date, but we haven't looked at writes. In GraphQL, writes are called **mutations**. We can think of them as queries with side effects. Here's an example of calling a mutation that might mark a given story as being liked by the current user:
268
+
269
+ ```graphql
270
+ // Give a human-readable name and define the types of the inputs,
271
+ // in this case the id of the story to mark as liked.
272
+ mutation StoryLike($storyID: String) {
273
+ // Call the mutation field and trigger its side effects
274
+ storyLike(storyID: $storyID) {
275
+ // Define fields to re-fetch after the mutation completes
276
+ likeCount
277
+ }
278
+ }
279
+ ```
280
+
281
+ Notice that we're querying for data that _may_ have changed as a result of the mutation. An obvious question is: why can't the server just tell us what changed? The answer is: it's complicated. GraphQL abstracts over _any_ data storage layer (or an aggregation of multiple sources), and works with any programming language. Furthermore, the goal of GraphQL is to provide data in a form that is useful to product developers building a view.
282
+
283
+ We've found that it's common for the GraphQL schema to differ slightly or even substantially from the form in which data is stored on disk. Put simply: there isn't always a 1:1 correspondence between data changes in your underlying _data storage_ (disk) and data changes in your _product-visible schema_ (GraphQL). The perfect example of this is privacy: returning a user-facing field such as `age` might require accessing numerous records in our data-storage layer to determine if the active user is even allowed to _see_ that `age` (Are we friends? Is my age shared? Did I block you? etc.).
284
+
285
+ Given these real-world constraints, the approach in GraphQL is for clients to query for things that may change after a mutation. But what exactly do we put in that query? During the development of Relay we explored several ideas — let's look at them briefly in order to understand why Relay uses the approach that it does:
286
+
287
+ - Option 1: Re-fetch everything that the app has ever queried. Even though only a small subset of this data will actually change, we'll still have to wait for the server to execute the _entire_ query, wait to download the results, and wait to process them again. This is very inefficient.
288
+
289
+ - Option 2: Re-fetch only the queries required by actively rendered views. This is a slight improvement over option 1. However, cached data that _isn't_ currently being viewed won't be updated. Unless this data is somehow marked as stale or evicted from the cache subsequent queries will read outdated information.
290
+
291
+ - Option 3: Re-fetch a fixed list of fields that _may_ change after the mutation. We'll call this list a **fat query**. We found this to also be inefficient because typical applications only render a subset of the fat query, but this approach would require fetching all of those fields.
292
+
293
+ - Option 4 (Relay): Re-fetch the intersection of what may change (the fat query) and the data in the cache. In addition to the cache of data Relay also remembers the queries used to fetch each item. These are called **tracked queries**. By intersecting the tracked and fat queries, Relay can query exactly the set of information the application needs to update and nothing more.
294
+
295
+ ## Data-Fetching APIs
296
+
297
+ So far we looked at the lower-level aspects of data-fetching and saw how various familiar concepts translate to GraphQL. Next, let's step back and look at some higher-level concerns that product developers often face around data-fetching:
298
+
299
+ - Fetching all the data for a view hierarchy.
300
+ - Managing asynchronous state transitions and coordinating concurrent requests.
301
+ - Managing errors.
302
+ - Retrying failed requests.
303
+ - Updating the local cache after receiving query/mutation responses.
304
+ - Queuing mutations to avoid race conditions.
305
+ - Optimistically updating the UI while waiting for the server to respond to mutations.
306
+
307
+ We've found that typical approaches to data-fetching — with imperative APIs — force developers to deal with too much of this non-essential complexity. For example, consider _optimistic UI updates_. This is a way of giving the user feedback while waiting for a server response. The logic of _what_ to do can be quite clear: when the user clicks "like", mark the story as being liked and send the request to the server. But the implementation is often much more complex. Imperative approaches require us to implement all of those steps: reach into the UI and toggle the button, initiate a network request, retry it if necessary, show an error if it fails (and untoggle the button), etc. The same goes for data-fetching: specifying _what_ data we need often dictates _how_ and _when_ it is fetched. Next, we'll explore our approach to solving these concerns with **Relay**.
308
+
309
+ <DocsRating />
@@ -0,0 +1,104 @@
1
+ ---
2
+ id: thinking-in-relay
3
+ title: Thinking in Relay
4
+ slug: /principles-and-architecture/thinking-in-relay/
5
+ description: Relay guide to thinking in Relay
6
+ ---
7
+
8
+ import DocsRating from '@site/src/core/DocsRating';
9
+
10
+ Relay's approach to data-fetching is heavily inspired by our experience with React. In particular, React breaks complex interfaces into reusable **components**, allowing developers to reason about discrete units of an application in isolation, and reducing the coupling between disparate parts of an application. Even more important is that these components are **declarative**: they allow developers to specify _what_ the UI should look like for a given state, and not have to worry about _how_ to show that UI. Unlike previous approaches that used imperative commands to manipulate native views (e.g. the DOM), React uses a UI description to automatically determine the necessary commands.
11
+
12
+ Let's look at some product use-cases to understand how we incorporated these ideas into Relay. We'll assume a basic familiarity with React.
13
+
14
+ ## Fetching Data For a View
15
+
16
+ In our experience, the overwhelming majority of products want one specific behavior: fetch all the data for a view hierarchy while displaying a loading indicator, and then render the entire view once the data is available.
17
+
18
+ One solution is to have a root component declare and fetch the data required by it and all of its children. However, this would introduce coupling: any change to a child component would require changing any root component that might render it! This coupling could mean a greater chance for bugs and slow the pace of development.
19
+
20
+ Another logical approach is to have each component declare and fetch the data it requires. This sounds great. However, the problem is that a component may render different children based on the data it received. So, nested components will be unable to render and begin fetching their data until parent components' queries have completed. In other words, *this forces data fetching to proceed in stages:* first render the root and fetch the data it needs, then render its children and fetch their data, and so on until you reach leaf components. Rendering would require multiple slow, serial roundtrips.
21
+
22
+ Relay combines the advantages of both of these approaches by allowing components to specify what data they require, but to coalesce those requirements into a single query that fetches the data for an entire subtree of components. In other words, it determines *statically* (i.e. before your application runs; at the time you write your code) the requirements for an entire view!
23
+
24
+ This is achieved with the help of GraphQL. Functional components use one or more GraphQL <a href="../../guided-tour/rendering/fragments/">fragments</a> to describe their data requirements. These fragments are then nested within other fragments, and ultimately within queries. And when such a query is fetched, Relay will make a single network request for it and all of its nested fragments. In other words, the Relay runtime is then able to make a *single network request* for all of the data required by a view!
25
+
26
+ Let's dive deeper to understand how Relay achieves this feat.
27
+
28
+ ## Specifying the data requirements of a component
29
+
30
+ With Relay, the data requirements for a component are specified with <a href="../../guided-tour/rendering/fragments/">fragments</a>. Fragments are named snippets of GraphQL that specify which fields to select from an object of a particular type. Fragments are written within GraphQL literals. For example, the following declares a GraphQL literal containing a fragment which selects an author's name and photo url:
31
+
32
+ ```javascript
33
+ // AuthorDetails.react.js
34
+ const authorDetailsFragment = graphql`
35
+ fragment AuthorDetails_author on Author {
36
+ name
37
+ photo {
38
+ url
39
+ }
40
+ }
41
+ `;
42
+ ```
43
+
44
+ This data is then read out from the store by calling the `useFragment(...)` hook in a functional React component. The actual author from which to read this data is determined by the second parameter passed to `useFragment`. For example:
45
+
46
+ ```javascript
47
+ // AuthorDetails.react.js
48
+ export default function AuthorDetails(props) {
49
+ const data = useFragment(authorDetailsFragment, props.author);
50
+ // ...
51
+ }
52
+ ```
53
+
54
+ This second parameter (`props.author`) is a fragment reference. Fragment references are obtained by **spreading** a fragment into another fragment or query. Fragments cannot be fetched directly. Instead, all fragments must ultimately be spread (either directly or transitively) into a query for the data to be fetched.
55
+
56
+ Let's take a look at one such query.
57
+
58
+ ## Queries
59
+
60
+ In order to fetch that data, we might declare a query which spreads `AuthorDetails_author` as follows:
61
+
62
+ ```javascript
63
+ // Story.react.js
64
+ const storyQuery = graphql`
65
+ query StoryQuery($storyID: ID!) {
66
+ story(id: $storyID) {
67
+ title
68
+ author {
69
+ ...AuthorDetails_author
70
+ }
71
+ }
72
+ }
73
+ `;
74
+ ```
75
+
76
+ Now, we can fetch the query by calling `const data = useLazyLoadQuery(storyQuery, {storyID})`. At this point, `data.story.author` (if it is present; all fields are nullable by default) will be a fragment reference that we can pass to `AuthorDetails`. For example:
77
+
78
+ ```javascript
79
+ // Story.react.js
80
+ function Story(props) {
81
+ const data = useLazyLoadQuery(storyQuery, props.storyId);
82
+
83
+ return (<>
84
+ <Heading>{data?.story.title}</Heading>
85
+ {data?.story?.author && <AuthorDetails author={data.story.author} />}
86
+ </>);
87
+ }
88
+ ```
89
+
90
+ Note what has happened here. We made a single network request which contained the data required by *both* the `Story` component *and* the `AuthorDetails` component! When that data was available, the entire view could render in a single pass.
91
+
92
+ ## Data Masking
93
+
94
+ With typical approaches to data-fetching we found that it was common for two components to have _implicit dependencies_. For example `<Story />` might use some data without directly ensuring that the data was fetched. This data would often be fetched by some other part of the system, such as `<AuthorDetails />`. Then when we changed `<AuthorDetails />` and removed that data-fetching logic, `<Story />` would suddenly and inexplicably break. These types of bugs are not always immediately apparent, especially in larger applications developed by larger teams. Manual and automated testing can only help so much: this is exactly the type of systematic problem that is better solved by a framework.
95
+
96
+ We've seen that Relay ensures that the data for a view is fetched all at once. But Relay also provide another benefit that isn't immediately obvious: **data masking**. Relay only allows components to access data they specifically ask for in GraphQL fragments, and nothing more. So if one component queries for a Story's `title`, and another for its `text`, each can see _only_ the field that they asked for. In fact, components can't even see the data requested by their _children_: that would also break encapsulation.
97
+
98
+ Relay also goes further: it uses opaque identifiers on `props` to validate that we've explicitly fetched the data for a component before rendering it. If `<Story />` renders `<AuthorDetails />` but forgets to spread its fragment, Relay will warn that the data for `<AuthorDetails />` is missing. In fact, Relay will warn _even if_ some other component happened to fetch the same data required by `<AuthorDetails />`. This warning tells us that although things _might_ work now, they're highly likely to break later.
99
+
100
+ # Conclusion
101
+
102
+ GraphQL provides a powerful tool for building efficient, decoupled client applications. Relay builds on this functionality to provide a framework for **declarative data-fetching**. By separating _what_ data to fetch from _how_ it is fetched, Relay helps developers build applications that are robust, transparent, and performant by default. It's a great complement to the component-centric way of thinking championed by React. While each of these technologies — React, Relay, and GraphQL — are powerful on their own, the combination is a **UI platform** that allows us to _move fast_ and _ship high-quality apps at scale_.
103
+
104
+ <DocsRating />
@@ -0,0 +1,50 @@
1
+ ---
2
+ id: videos
3
+ title: Videos
4
+ slug: /principles-and-architecture/videos/
5
+ description: Relay videos
6
+ ---
7
+
8
+ import DocsRating from '@site/src/core/DocsRating';
9
+
10
+ ## React Conf 2021
11
+
12
+ ### Re-introducing Relay | Robert Balicki
13
+
14
+ <iframe src="https://www.youtube-nocookie.com/embed/lhVGdErZuN4" width={640} height={360} allowFullScreen={true} frameBorder="0" />
15
+
16
+ ## React Conf 2019
17
+
18
+ ### Data Fetching With Suspense In Relay | Joe Savona
19
+
20
+ <iframe src="https://www.youtube-nocookie.com/embed/Tl0S7QkxFE4" width={640} height={360} allowFullScreen={true} frameBorder="0" />
21
+
22
+ ## Facebook F8 2019
23
+
24
+ ### [Building the new Facebook.com with React, GraphQL and Relay](https://developers.facebook.com/videos/2019/building-the-new-facebookcom-with-react-graphql-and-relay/)
25
+
26
+ <iframe src="https://www.youtube-nocookie.com/embed/WxPtYJRjLL0?start=215" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen width="640" height="360" frameBorder="0"></iframe>
27
+
28
+ ## Facebook F8 2017
29
+
30
+ ### [The Evolution of React and GraphQL at Facebook and Beyond](https://developers.facebook.com/videos/f8-2017/the-evolution-of-react-and-graphql-at-facebook-and-beyond/)
31
+
32
+ <iframe src="https://www.facebook.com/plugins/video.php?href=https%3A%2F%2Fwww.facebook.com%2FFacebookforDevelopers%2Fvideos%2F10154614710193553%2F&show_text=0&width=640" width={640} height={360} frameBorder="0" allowFullScreen={true} />
33
+
34
+ ## [Silicon Valley ReactJS Meetup](http://www.meetup.com/Silicon-Valley-ReactJS-Meetup/)
35
+
36
+ ### Relay Modern: simpler, faster, more predictable ([slides](https://speakerdeck.com/wincent/relay-2-simpler-faster-more-predictable))
37
+
38
+ <iframe width={640} height={360} src="https://www.youtube-nocookie.com/embed/OEfUBN9dAI8" frameBorder="0" allowFullScreen={true} />
39
+
40
+ ### Zero to GraphQL in 30 minutes
41
+
42
+ <iframe width={640} height={360} src="https://www.youtube-nocookie.com/embed/UBGzsb2UkeY" frameBorder="0" allowFullScreen={true} />
43
+
44
+ ## [GraphQL Europe 2018](https://www.graphql-europe.org/)
45
+
46
+ ### Data Masking in GraphQL Clients
47
+
48
+ <iframe width={640} height={360} src="https://www.youtube-nocookie.com/embed/ww5UQ50oHok" frameBorder="0" allowFullScreen={true} />
49
+
50
+ <DocsRating />
@@ -0,0 +1,126 @@
1
+ # Arrays and Lists
2
+
3
+ So far we’ve only dealt with components that have a single instance of the components they’re composed from. For example, we’re only showing a single Newsfeed story, and within that story there’s just a single author with a single profile picture. Let’s look at how to handle more than one of something.
4
+
5
+ GraphQL includes support for arrays, which in GraphQL are called *lists.* A field can be not only a single scalar value but an array of them, or not only a single edge but an array of edges. The schema specifies whether each field is a list or not, but, oddly, the GraphQL query syntax doesn’t distinguish between selecting a singular field and selecting a list — a quirky exception to the design principle that a GraphQL response should have the same shape as the query.
6
+
7
+ Request:
8
+
9
+ ```
10
+ query MyQuery {
11
+ viewer {
12
+ contacts { // List of edges
13
+ id // field on a single item
14
+ name
15
+ }
16
+ }
17
+ }
18
+ ```
19
+
20
+ Response:
21
+
22
+ ```
23
+ {
24
+ viewer: {
25
+ contacts: [ // array in response
26
+ {
27
+ id: "123",
28
+ name: "Chris",
29
+ },
30
+ {
31
+ id: "789",
32
+ name: "Sue",
33
+ }
34
+ ]
35
+ }
36
+ }
37
+ ```
38
+
39
+ As it happens, the schema in our example app has a `topStories` field that returns a list of Stories, as opposed to the `topStory` field we're currently using that returns just one.
40
+
41
+ To show multiple stories on our newsfeed, we just need to modify `Newsfeed.tsx` to use `topStories`.
42
+
43
+ ### Step 1 — Select a list in the fragment
44
+
45
+ Open `Newsfeed.tsx` and find `NewsfeedQuery`. Replace `topStory` with `topStories`, and run `npm run relay`.
46
+
47
+ ```
48
+ const NewsfeedQuery = graphql`
49
+ query NewsfeedQuery {
50
+ // change-line
51
+ topStories {
52
+ ...StoryFragment
53
+ }
54
+ }
55
+ `;
56
+ ```
57
+
58
+ ### Step 2 — Map over the list in the component
59
+
60
+ In the `Newsfeed` component, `data.topStories` will now be an array of fragment refs, each of which can be passed to a `Story` child component to render that story:
61
+
62
+ ```
63
+ export default function Newsfeed({}) {
64
+ const data = useLazyLoadQuery<NewsfeedQueryType>(NewsfeedQuery, {});
65
+ // change-line
66
+ const stories = data.topStories;
67
+ return (
68
+ <div className="newsfeed">
69
+ // change-line
70
+ {stories.map(story => <Story story={story} />)}
71
+ </div>
72
+ );
73
+ }
74
+ ```
75
+
76
+ ### Step 3 — Add a React key based on the node ID
77
+
78
+ At this point, you should see multiple stories on the screen! It's beginning to look like a proper newsfeed app.
79
+
80
+ ![Multiple stories](/img/docs/tutorial/arrays-top-stories-screenshot.png)
81
+
82
+ However, we're also getting a React warning that we're rendering an array of components without [providing a key attribute](https://reactjs.org/docs/lists-and-keys.html).
83
+
84
+ ![React missing key warning](/img/docs/tutorial/arrays-keys-warning-screenshot.png)
85
+
86
+ It's always important to heed this warning, and more specifically to base keys on the identity of the things being displayed, rather than simply their indices in the array. This allows React to handle reordering and deleting items from the list correctly, since it knows which items are which even if their order changes.
87
+
88
+ Luckily, GraphQL nodes generally have IDs. We can simply select the `id` field of `story` and use it as a key:
89
+
90
+ ```
91
+ const NewsfeedQuery = graphql`
92
+ query NewsfeedQuery {
93
+ topStories {
94
+ // change-line
95
+ id
96
+ ...StoryFragment
97
+ }
98
+ }
99
+ `;
100
+
101
+ ...
102
+
103
+ export default function Newsfeed({}) {
104
+ const data = useLazyLoadQuery<NewsfeedQueryType>(NewsfeedQuery, {});
105
+ const stories = data.topStories;
106
+ return (
107
+ <div className="newsfeed">
108
+ {stories.map(story => (
109
+ <Story
110
+ // change-line
111
+ key={story.id}
112
+ story={story}
113
+ />
114
+ ))}
115
+ </div>
116
+ );
117
+ }
118
+ ```
119
+
120
+ With that, we've got a collection of Stories on the screen. It's worth pointing out that here we're mixing individual fields with fragment spreads in the same place in our query. This means that Newsfeed can read the fields it cares about (directly from `useLazyLoadQuery`) while Story can read the fields it cares about (via `useFragment`). The *same object* both contains Newsfeed's selected field `id` and is also a fragment key for `StoryFragment`.
121
+
122
+ :::tip
123
+ GraphQL Lists are only the most basic way of dealing with collections of things. We’ll build on them to do pagination and infinite scrolling later in the tutorial, using a special system called Connections. You’ll want to use Connections in most situations where you have a collection of items — although you’ll still use GraphQL Lists as a building block.
124
+ :::
125
+
126
+ Onward!