relay-runtime 20.1.1 → 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 +44 -20
  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 +129 -57
  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 +125 -57
  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,160 @@
1
+ ---
2
+ id: testing-relay-with-preloaded-queries
3
+ title: Testing Relay with Preloaded Queries
4
+ slug: /guides/testing-relay-with-preloaded-queries/
5
+ description: Relay guide to testing with preloaded queries
6
+ keywords:
7
+ - testing
8
+ - preloaded
9
+ - usePreloadedQuery
10
+ - queueOperationResolver
11
+ - queuePendingOperation
12
+ ---
13
+
14
+ import DocsRating from '@site/src/core/DocsRating';
15
+ import {FbInternalOnly, OssOnly} from 'docusaurus-plugin-internaldocs-fb/internal';
16
+
17
+ Components that use preloaded queries (`useQueryLoader` and `usePreloadedQuery` hooks) require slightly different and more convoluted test setup.
18
+
19
+ In short, there are two steps that need to be performed **before rendering the component**
20
+
21
+ 1. Configure the query resolver to generate the response via `environment.mock.queueOperationResolver`
22
+ 2. Record a pending queue invocation via `environment.mock.queuePendingOperation`
23
+
24
+ ## Symptoms that something is wrong
25
+
26
+ 1. The test doesn't do what is expected from it.
27
+ 2. The query seems to be blocking instead of executing
28
+ 1. E.g. the `Suspend` doesn't switch from "waiting" to "data loaded" state
29
+ 3. If you add the `console.log` before and after `usePreloadedQuery`, only the "before" call is hit
30
+
31
+ ## TL;DR
32
+
33
+ ```javascript
34
+ const {RelayEnvironmentProvider} = require('react-relay');
35
+ const {MockPayloadGenerator, createMockEnvironment} = require('relay-test-utils');
36
+ const {act, render} = require('@testing-library/react');
37
+ test("...", () => {
38
+ // arrange
39
+ const environment = createMockEnvironment();
40
+ environment.mock.queueOperationResolver(operation => {
41
+ return MockPayloadGenerator.generate(operation, {
42
+ CurrencyAmount() {
43
+ return {
44
+ formatted_amount: "1234$",
45
+ };
46
+ },
47
+ });
48
+ });
49
+ const query = YourComponentGraphQLQueryGoesHere; // can be the same, or just identical
50
+ const variables = {
51
+ // ACTUAL variables for the invocation goes here
52
+ };
53
+ environment.mock.queuePendingOperation(YourComponentGraphQLQuery, variables);
54
+
55
+ // act
56
+ const {getByTestId, ..otherStuffYouMightNeed} = render(
57
+ <RelayEnvironmentProvider environment={environment}>
58
+ <YourComponent data-testid="1234" {...componentPropsIfAny}/>
59
+ </RelayEnvironmentProvider>
60
+ );
61
+ // trigger the loading - click a button, emit an event, etc. or ...
62
+ act(() => jest.runAllImmediates()); // ... if loadQuery is in the useEffect()
63
+ // assert
64
+ // your assertions go here
65
+ });
66
+ ```
67
+
68
+ ### Configure the query resolver to generate the response
69
+
70
+ This is done via `environment.mock.queueOperationResolver(operation)` call, but getting it right might be tricky.
71
+
72
+ The crux of this call is to return a mocked graphql result in a very particular format (as `MockResolvers` type, to be precise). This is done via a second parameter to `generate` - it is an object, whose keys are GraphQL types that we want to mock. (See [`mock-payload-generator`](../testing-relay-components/#mock-payload-generator-and-the-relay_test_operation-directive)).
73
+
74
+ Continuing on the above example:
75
+
76
+ ```js
77
+ return MockPayloadGenerator.generate(operation, {
78
+ CurrencyAmount() { // <-- the GraphQL type
79
+ return {
80
+ formatted_amount: "response_value" <-- CurrencyAmount fields, selected in the query
81
+ };
82
+ }
83
+ });
84
+ ```
85
+ The tricky thing here is to obtain the name of the GraphQL type and fields to return. This can be done in two ways:
86
+
87
+ * Call `console.log(JSON.stringify(operation, null, 2))` and look for the `concreteType` that corresponds to what we want to mock. Then look at the sibling `selections` array, which describes the fields that are selected from that object.
88
+
89
+ <FbInternalOnly>
90
+
91
+ * This is somewhat intense - P139017123 is the output for [this query](https://fburl.com/diffusion/irqurgj9). Rule of thumb - one nested call in the query produces one nested object in the output.
92
+ * Look up the type in the graphiql (bunnylol graphiql), then specify the fields listed on the query.
93
+
94
+ :::note
95
+ The type you need seems to be the type returned by the *innermost function call* (or calls, if you have multiple functions called in one query - see D23078476). This needs to be confirmed - in both example diffs the target types was also leafs.
96
+ :::
97
+
98
+ </FbInternalOnly>
99
+
100
+
101
+ It is **possible** to return different data for different query variables via [Mock Resolver Context](../testing-relay-components/#mock-resolver-context). The query variables will be available on the `context.args`, but only to the *innermost function call* (for the query above, only `offer_ids` are available)
102
+
103
+ ```javascript
104
+ CurrencyAmount(context) {
105
+ console.log(JSON.stringify(context, null, 2)); // <--
106
+ return { formatted_amount: mockResponse }
107
+ }
108
+ // <-- logs { ...snip..., "name": "subtotal_price_for_offers", args: { offer_ids: [...] } }
109
+ ```
110
+ ### Record a pending queue invocation
111
+
112
+ This is more straightforward - it is done via a call to `environment.mock.queuePendingOperation(query, variables)`
113
+
114
+ * `Query` needs to match the query issues by the component. Simplest (and most robust against query changes) is to export the query from the component module and use it in the test, but having an *identical* (but not the same) query works as well.
115
+ * `variables` has to match the variables that will be used in this test invocation.
116
+ * Beware of nested objects and arrays - they are compared via `areEqual` ([invocation code](https://github.com/facebook/relay/blob/046f758c6b411608371d4cc2f0a594ced331864e/packages/relay-test-utils/RelayModernMockEnvironment.js#L233))
117
+ * Arrays are compared by values (not by reference), but the order of elements matter
118
+ * Nested objects - performs deep compare, order of keys is not relevant (this is not confirmed - please update this doc if you used a graphql query with "deep" structure*)*
119
+
120
+ <FbInternalOnly>
121
+
122
+ ### Example diffs
123
+
124
+ * [D23078476](https://internalfb.com/intern/diff/D23078476)
125
+ * [D23101739](https://www.internalfb.com/diff/D23101739)
126
+
127
+ </FbInternalOnly>
128
+
129
+ ## Troubleshooting
130
+
131
+ * `console.log`, `console.log` everywhere! Recommended places:
132
+ * component: before and after `useQueryLoader, usePreloadedQuery, loadQuery`
133
+ * test: in `queueOperationResolver` callback
134
+ * library: in `RelayModernMockEnvironment.execute`, after the `const currentOperation = ...` call ([here](https://github.com/facebook/relay/blob/046f758c6b411608371d4cc2f0a594ced331864e/packages/relay-test-utils/RelayModernMockEnvironment.js#L230))
135
+ * If `loadQuery` is not called - make sure to issue the triggering event. Depending on your component implementation it could be a user-action (like button click or key press), javascript event (via event emitter mechanisms) or a simple "delayed execution" with `useEffect`.
136
+ * The `useEffect` case is probably easiest to miss - make sure to call `act(() => jest.runAllImmediates())` **after** rendering the component
137
+ * If "before" `usePreloadedQuery` is hit, but "after" is not - the query suspends. This entire guide is written to resolve it - you might want to re-read it. But most likely it is either:
138
+ * Used a different query - the query resolver would not be called, `currentOperation` will be `null`
139
+ * Query variables don't match - the query resolver would not be called, `currentOperation` will be `null` (make sure to inspect the `variables`).
140
+ * Also, make sure arrays are in the same order, if any (or better yet, use sets, if at all possible).
141
+ * If data returned from the query is not what you expect, make sure you're generating the right graphql type.
142
+ * You can tell you're mocking the wrong one if the return values look something like `<mock-value-for-field-"formatted_amount">`
143
+
144
+
145
+ :::note
146
+ Make sure the component and the test use the same environment (i.e. there's no `<RelayEnvironmentProvider environment={RelayFBEnvironment}>` somewhere nested in your test React tree.
147
+ :::
148
+
149
+
150
+ ## Epilogue
151
+
152
+ Examples here use `testing-library-react`, but it works with the `react-test-renderer` as well.
153
+
154
+ <FbInternalOnly>
155
+
156
+ See [D23078476](https://www.internalfb.com/diff/D23078476).
157
+
158
+ </FbInternalOnly>
159
+
160
+ <DocsRating />
@@ -0,0 +1,58 @@
1
+ ---
2
+ id: throw-on-field-error-directive
3
+ title: '@throwOnFieldError Directive'
4
+ slug: /guides/throw-on-field-error-directive/
5
+ description: Relay guide to @throwOnFieldError
6
+ keywords:
7
+ - directive
8
+ - optional
9
+ - errors
10
+ ---
11
+
12
+ import DocsRating from '@site/src/core/DocsRating';
13
+
14
+ :::tip
15
+ Both `@catch` and `@throwOnFieldError` only handle field errors in the
16
+ query/fragment/mutation in which they are used. They **do not** handle errors
17
+ related to fields in any spread fragments.
18
+ :::
19
+
20
+ The `@throwOnFieldError` directive can be added to fragments and queries. When
21
+ this directive is used, the Relay runtime will throw an exception if a field
22
+ with a field error is encountered while reading the fragment or query, or if
23
+ Relay is missing data due to a
24
+ [graph relationship change](../debugging/why-null.mdx#graph-relationship-change).
25
+
26
+ In addition to causing the Relay runtime to throw an exception if a field error
27
+ is encountered, the `@throwOnFieldError` directive also enables generation of
28
+ non-null Flow types for fields that have the `@semanticNonNull` directive in the
29
+ schema. This means that if a field has the `@semanticNonNull` directive, the
30
+ generated Flow type for that field will be non-nullable; if an error were to
31
+ occur while reading that field, the thrown exception will prevent your
32
+ application from receiving a null value. Making a previously-nullable field
33
+ non-null may make many existing `@required` directives unnecessary; you can use
34
+ the
35
+ [remove-unnecessary-required-directives codemod](../guides/codemods.mdx#remove-unnecessary-required-directives)
36
+ to clean these up.
37
+
38
+ To use the `@throwOnFieldError` directive, add it to a fragment or query in your
39
+ Relay code. For example:
40
+
41
+ ```
42
+ fragment MyFragment on User @throwOnFieldError {
43
+ id
44
+ name
45
+ }
46
+ ```
47
+
48
+ In this example, the `@throwOnFieldError` directive is added to the MyFragment
49
+ fragment. If any of the fields in this fragment (in this case, id and name) have
50
+ a field error, the Relay runtime will throw an exception at the time the
51
+ fragment is read.
52
+
53
+ If you wish to handle a specific field error locally within your
54
+ `@throwOnFieldError` fragment or query instead of having that error throw, you
55
+ can catch the error with [@catch](./catch-directive.mdx).
56
+
57
+ **Read more about Relay's experimental support for
58
+ [Semantic Nullability](./semantic-nullability.mdx).**
@@ -0,0 +1,414 @@
1
+ ---
2
+ id: type-emission
3
+ title: Type Emission
4
+ slug: /guides/type-emission/
5
+ description: Relay guide to type emission
6
+ keywords:
7
+ - type emission
8
+ ---
9
+
10
+ import DocsRating from '@site/src/core/DocsRating';
11
+ import {FbInternalOnly, OssOnly, fbContent} from 'docusaurus-plugin-internaldocs-fb/internal';
12
+ import Tabs from '@theme/Tabs';
13
+ import TabItem from '@theme/TabItem';
14
+
15
+ As part of its normal work, the [**Relay Compiler**](../compiler) will emit type information for your language of choice that helps you write type-safe application code. These types are included in the artifacts that `relay-compiler` generates to describe your operations and fragments.
16
+
17
+ ## Operation variables
18
+
19
+ The shape of the variables object used for query, mutation, or subscription operations.
20
+
21
+ In this example the emitted type-information would require the variables object to contain an `artistID` key with a non-null string.
22
+
23
+ <Tabs
24
+ defaultValue={fbContent({internal: 'Flow', external: 'TypeScript'})}
25
+ values={[
26
+ {label: 'Flow', value: 'Flow'},
27
+ {label: 'TypeScript', value: 'TypeScript'},
28
+ ]}>
29
+ <TabItem value="Flow">
30
+
31
+ ```javascript
32
+ /**
33
+ * export type ExampleQuery$variables = {
34
+ * +artistID: string,
35
+ * }
36
+ * export type ExampleQuery$data = {
37
+ * +artist: {
38
+ * +name: ?string,
39
+ * }
40
+ * }
41
+ * export type ExampleQuery = {
42
+ * +variables: ExampleQuery$variables,
43
+ * +response: ExampleQuery$data,
44
+ * }
45
+ */
46
+
47
+ const data = useLazyLoadQuery(
48
+ graphql`
49
+ query ExampleQuery($artistID: ID!) {
50
+ artist(id: $artistID) {
51
+ name
52
+ }
53
+ }
54
+ `,
55
+ // variables are expected to be of type ExampleQuery$variables
56
+ {artistID: 'banksy'},
57
+ );
58
+ ```
59
+
60
+ </TabItem>
61
+
62
+ <TabItem value="TypeScript">
63
+
64
+ ```javascript
65
+ /**
66
+ * export type ExampleQuery$variables = {
67
+ * readonly artistID: string
68
+ * }
69
+ * export type ExampleQuery$data = {
70
+ * readonly artist?: {
71
+ * readonly name?: string
72
+ * }
73
+ * }
74
+ * export type ExampleQuery = {
75
+ * readonly variables: ExampleQuery$variables
76
+ * readonly response: ExampleQuery$data
77
+ * }
78
+ */
79
+ const data = useLazyLoadQuery(
80
+ graphql`
81
+ query ExampleQuery($artistID: ID!) {
82
+ artist(id: $artistID) {
83
+ name
84
+ }
85
+ }
86
+ `,
87
+ // variables are expected to be of type ExampleQuery$variables
88
+ {artistID: 'banksy'},
89
+ );
90
+ ```
91
+
92
+ </TabItem>
93
+ </Tabs>
94
+
95
+ ## Operation and fragment data
96
+
97
+ The shape of the data selected in a operation or fragment, following the [data-masking] rules. That is, excluding any data selected by fragment spreads.
98
+
99
+ In this example the emitted type-information describes the response data which is returned by `useLazyLoadQuery` (or `usePreloadedQuery`).
100
+
101
+ <Tabs
102
+ defaultValue={fbContent({internal: 'Flow', external: 'TypeScript'})}
103
+ values={[
104
+ {label: 'Flow', value: 'Flow'},
105
+ {label: 'TypeScript', value: 'TypeScript'},
106
+ ]}>
107
+ <TabItem value="Flow">
108
+
109
+ ```javascript
110
+ /**
111
+ * export type ExampleQuery$variables = {
112
+ * +artistID: string,
113
+ * }
114
+ * export type ExampleQuery$data = {
115
+ * +artist: {
116
+ * +name: ?string,
117
+ * }
118
+ * }
119
+ * export type ExampleQuery = {
120
+ * +variables: ExampleQuery$variables,
121
+ * +response: ExampleQuery$data,
122
+ * }
123
+ */
124
+
125
+ // data is of type ExampleQuery$data
126
+ const data = useLazyLoadQuery(
127
+ graphql`
128
+ query ExampleQuery($artistID: ID!) {
129
+ artist(id: $artistID) {
130
+ name
131
+ }
132
+ }
133
+ `,
134
+ {artistID: 'banksy'},
135
+ );
136
+
137
+ return props.artist && <div>{props.artist.name} is great!</div>
138
+ ```
139
+
140
+ </TabItem>
141
+
142
+ <TabItem value="TypeScript">
143
+
144
+ ```javascript
145
+ /**
146
+ * export type ExampleQuery$variables = {
147
+ * readonly artistID: string
148
+ * }
149
+ * export type ExampleQuery$data = {
150
+ * readonly artist?: {
151
+ * readonly name?: string
152
+ * }
153
+ * }
154
+ * export type ExampleQuery = {
155
+ * readonly variables: ExampleQuery$variables
156
+ * readonly response: ExampleQuery$data
157
+ * }
158
+ */
159
+
160
+ // data is of type ExampleQuery$data
161
+ const data = useLazyLoadQuery(
162
+ graphql`
163
+ query ExampleQuery($artistID: ID!) {
164
+ artist(id: $artistID) {
165
+ name
166
+ }
167
+ }
168
+ `,
169
+ {artistID: 'banksy'},
170
+ );
171
+
172
+ return props.artist && <div>{props.artist.name} is great!</div>
173
+ ```
174
+
175
+ </TabItem>
176
+ </Tabs>
177
+
178
+
179
+ Similarly, in this example the emitted type-information describes the type of the prop to match the type of the fragment reference `useFragment` expects to receive.
180
+
181
+ <Tabs
182
+ defaultValue={fbContent({internal: 'Flow', external: 'TypeScript'})}
183
+ values={[
184
+ {label: 'Flow', value: 'Flow'},
185
+ {label: 'TypeScript', value: 'TypeScript'},
186
+ ]}>
187
+ <TabItem value="Flow">
188
+
189
+ ```javascript
190
+ /**
191
+ * export type ExampleFragmentComponent_artist$data = {
192
+ * +name: string
193
+ * }
194
+ *
195
+ * export type ExampleFragmentComponent_artist$key = { ... }
196
+ */
197
+
198
+ import type { ExampleFragmentComponent_artist$key } from "__generated__/ExampleFragmentComponent_artist.graphql"
199
+
200
+ type Props = {
201
+ artist: ExampleFragmentComponent_artist$key,
202
+ };
203
+
204
+ export default ExampleFragmentComponent(props) {
205
+ // data is of type ExampleFragmentComponent_artist$data
206
+ const data = useFragment(
207
+ graphql`
208
+ fragment ExampleFragmentComponent_artist on Artist {
209
+ biography
210
+ }
211
+ `,
212
+ props.artist,
213
+ );
214
+
215
+ return <div>About the artist: {props.artist.biography}</div>;
216
+ }
217
+ ```
218
+
219
+ </TabItem>
220
+
221
+ <TabItem value="TypeScript">
222
+
223
+ ```javascript
224
+ /**
225
+ * export type ExampleFragmentComponent_artist$data = {
226
+ * readonly name: string
227
+ * }
228
+ *
229
+ * export type ExampleFragmentComponent_artist$key = { ... }
230
+ */
231
+
232
+ import { ExampleFragmentComponent_artist$key } from "__generated__/ExampleFragmentComponent_artist.graphql"
233
+
234
+ interface Props {
235
+ artist: ExampleFragmentComponent_artist$key,
236
+ };
237
+
238
+ export default ExampleFragmentComponent(props: Props) {
239
+ // data is of type ExampleFragmentComponent_artist$data
240
+ const data = useFragment(
241
+ graphql`
242
+ fragment ExampleFragmentComponent_artist on Artist {
243
+ biography
244
+ }
245
+ `,
246
+ props.artist,
247
+ );
248
+
249
+ return <div>About the artist: {props.artist.biography}</div>;
250
+ }
251
+ ```
252
+
253
+ </TabItem>
254
+ </Tabs>
255
+
256
+ ## Fragment references
257
+
258
+ The opaque identifier described in [data-masking] that a child container expects to receive from its parent, which represents the child container’s fragment spread inside the parent’s fragment.
259
+
260
+ <OssOnly>
261
+
262
+ :::important
263
+ Please read [this important caveat](#single-artifact-directory) about actually enabling type-safe fragment reference checking.
264
+ :::
265
+
266
+ </OssOnly>
267
+
268
+ Consider a component that [composes](../../guided-tour/rendering/fragments/#composing-fragments) the above fragment component example. In this example, the emitted type-information of the child component receives a unique opaque identifier type, called a fragment reference, which the type-information emitted for the parent’s fragment references in the location where the child’s fragment is spread. Thus ensuring that the child’s fragment is spread into the parent’s fragment _and_ the correct fragment reference is passed to the child component at runtime.
269
+
270
+ <Tabs
271
+ defaultValue={fbContent({internal: 'Flow', external: 'TypeScript'})}
272
+ values={[
273
+ {label: 'Flow', value: 'Flow'},
274
+ {label: 'TypeScript', value: 'TypeScript'},
275
+ ]}>
276
+ <TabItem value="Flow">
277
+
278
+ ```javascript
279
+ import { ExampleFragmentComponent } from "./ExampleFragmentComponent"
280
+
281
+ /**
282
+ * import type { ExampleFragmentComponent_artist$fragmentType } from "ExampleFragmentComponent_artist.graphql";
283
+ *
284
+ * export type ExampleQuery$data = {
285
+ * +artist: ?{
286
+ * +name: ?string,
287
+ * +$fragmentSpreads: ExampleFragmentComponent_artist$fragmentType,
288
+ * }
289
+ * };
290
+ * export type ExampleQuery$variables = {
291
+ * +artistID: string,
292
+ * }
293
+ * export type ExampleQuery = {
294
+ * +variables: ExampleQuery$variables,
295
+ * +response: ExampleQuery$data,
296
+ * }
297
+ */
298
+
299
+ // data is of type ExampleQuery$data
300
+ const data = useLazyLoadQuery(
301
+ graphql`
302
+ query ExampleQuery($artistID: ID!) {
303
+ artist(id: $artistID) {
304
+ name
305
+ ...ExampleFragmentComponent_artist
306
+ }
307
+ }
308
+ `,
309
+ {artistID: 'banksy'},
310
+ );
311
+
312
+ // Here only `data.artist.name` is directly visible,
313
+ // the marker prop $fragmentSpreads indicates that `data.artist`
314
+ // can be used for the component expecting this fragment spread.
315
+ return <ExampleFragmentComponent artist={data.artist} />;
316
+ ```
317
+
318
+ </TabItem>
319
+
320
+ <TabItem value="TypeScript">
321
+
322
+ ```javascript
323
+ import { ExampleFragmentComponent } from "./ExampleFragmentComponent"
324
+
325
+ /**
326
+ * import { ExampleFragmentComponent_artist$fragmentType } from "ExampleFragmentComponent_artist.graphql";
327
+ *
328
+ * export type ExampleQuery$data = {
329
+ * readonly artist?: {
330
+ * readonly name: ?string,
331
+ * readonly " $fragmentSpreads": ExampleFragmentComponent_artist$fragmentType
332
+ * }
333
+ * }
334
+ * export type ExampleQuery$variables = {
335
+ * readonly artistID: string
336
+ * }
337
+ * export type ExampleQuery = {
338
+ * readonly variables: ExampleQuery$variables
339
+ * readonly response: ExampleQuery$data
340
+ * }
341
+ */
342
+
343
+ // data is of type ExampleQuery$data
344
+ const data = useLazyLoadQuery(
345
+ graphql`
346
+ query ExampleQuery($artistID: ID!) {
347
+ artist(id: $artistID) {
348
+ name
349
+ ...ExampleFragmentComponent_artist
350
+ }
351
+ }
352
+ `,
353
+ {artistID: 'banksy'},
354
+ );
355
+
356
+ // Here only `data.artist.name` is directly visible,
357
+ // the marker prop $fragmentSpreads indicates that `data.artist`
358
+ // can be used for the component expecting this fragment spread.
359
+ return <ExampleFragmentComponent artist={data.artist} />;
360
+ ```
361
+
362
+ </TabItem>
363
+ </Tabs>
364
+
365
+ <OssOnly>
366
+
367
+ ## Single artifact directory
368
+
369
+ An important caveat to note is that by default strict fragment reference type-information will _not_ be emitted, instead they will be typed as `any` and would allow you to pass in any data to the child component.
370
+
371
+ To enable this feature, you will have to tell the compiler to store all the artifacts in a single directory, by specifying the `artifactDirectory` in the
372
+ compiler configuration:
373
+
374
+ ```
375
+ {
376
+ // package.json
377
+ "relay": {
378
+ "artifactDirectory": "./src/__generated__",
379
+ ...
380
+ },
381
+ ...
382
+ }
383
+ ```
384
+
385
+ …and additionally inform the babel plugin in your `.babelrc` config where to look for the artifacts:
386
+
387
+ ```json
388
+ {
389
+ "plugins": [
390
+ ["relay", { "artifactDirectory": "./src/__generated__" }]
391
+ ]
392
+ }
393
+ ```
394
+
395
+ It is recommended to alias this directory in your module resolution configuration such that you don’t need to specify relative paths in your source files. This is what is also done in the above examples, where artifacts are imported from a `__generated__` alias, rather than relative paths like `../../../../__generated__`.
396
+
397
+ ### Background information
398
+
399
+ The reason is that `relay-compiler` and its artifact emission is stateless. Meaning that it does not keep track of locations of original source files and where the compiler previously saved the accompanying artifact on disk. Thus, if the compiler were to emit artifacts that try to import fragment reference types from _other_ artifacts, the compiler would:
400
+
401
+ - first need to know where on disk that other artifact exists;
402
+ - and update imports when the other artifact changes location on disk.
403
+
404
+ Facebook uses a module system called [Haste], in which all source files are considered in a flat namespace. This means that an import declaration does not need to specify the path to another module and thus there is no need for the compiler to ever consider the above issues. I.e. an import only needs to specify the basename of the module filename and Haste takes care of actually finding the right module at import time. Outside of Facebook, however, usage of the Haste module system is non-existent nor encouraged, thus the decision to not import fragment reference types but instead type them as `any`.
405
+
406
+ At its simplest, we can consider Haste as a single directory that contains all module files, thus all module imports always being safe to import using relative sibling paths. This is what is achieved by the single artifact directory feature. Rather than co-locating artifacts with their source files, all artifacts are stored in a single directory, allowing the compiler to emit imports of fragment reference types.
407
+
408
+ </OssOnly>
409
+
410
+ [data-masking]: ../../principles-and-architecture/thinking-in-relay#data-masking
411
+
412
+ [Haste]: https://twitter.com/dan_abramov/status/758655309212704768
413
+
414
+ <DocsRating />
@@ -0,0 +1,32 @@
1
+ ---
2
+ id: home
3
+ title: Home
4
+ slug: /
5
+ description: Relay documentation landing page
6
+ keywords:
7
+ - relay
8
+ - graphql
9
+ - data
10
+ - introduction
11
+ - home
12
+ ---
13
+
14
+ # Relay Docs
15
+
16
+ import DocsRating from '@site/src/core/DocsRating';
17
+ import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/internal';
18
+
19
+ Relay is a powerful [GraphQL](https://graphql.org/) client for [React](https://react.dev/). It embodies years of learning to give you **outstanding performance by default** while keeping your code **scalable and maintainable**.
20
+
21
+ Relay brings the composability of React components to data fetching. Each component declares its own data needs, and Relay combines them into efficient pre-loadable queries. Every aspect of its design is to make the natural way of writing components also the most performant.
22
+
23
+ ## Features
24
+
25
+ * **Declarative**: Just declare what data each component needs and Relay will handle generating [optimal queries](https://relay.dev/blog/2023/10/24/how-relay-enables-optimal-data-fetching/) for each surface.
26
+ * **Composable**: Components act like building bricks that can click into place anywhere in your app without needing to manually update queries.
27
+ * **Pre-fetchable**: Relay's generated queries allow you to start fetching data for your surface before your code even downloads or runs.
28
+ * **Built-in UI patterns**: Relay implements loading states, pagination, refetching, optimistic updates, rollbacks, and other common UI behaviors that are tricky to get right.
29
+ * **Consistent state**: Relay maintains a normalized data store, so components that observe the same data stay in sync even if they reach it by different queries.
30
+ * **Type safe**: Relay generates TypeScript types for each GraphQL snippet so that errors are caught statically, not at runtime.
31
+ * **Streaming/deferred data**: Declaratively defer parts of your query and Relay will progressively re-render your UI as the data streams in.
32
+ * **Developer experience**: Relay's [editor support](./editor-support.mdx) provides autocompletion and go-to-definition for your GraphQL schema.
@@ -0,0 +1,24 @@
1
+ ---
2
+ id: architecture-overview
3
+ title: Architecture Overview
4
+ slug: /principles-and-architecture/architecture-overview/
5
+ description: Relay architecture overview guide
6
+ keywords:
7
+ - architecture
8
+ ---
9
+
10
+ import DocsRating from '@site/src/core/DocsRating';
11
+
12
+ This document, together with [Runtime Architecture](../runtime-architecture/) and [Compiler Architecture](../compiler-architecture/), describes the high-level architecture of Relay. The intended audience includes developers interested in contributing to Relay, developers hoping to utilize the building blocks of Relay to create higher-level APIs, and anyone interested in understanding more about Relay internals. For developers wanting to learn more about _using_ Relay to build products, the [Tutorial](../tutorial/intro.mdx) is the best resource.
13
+
14
+ ## Core Modules
15
+
16
+ Relay is composed of three core parts:
17
+
18
+ - **Relay Compiler:** A GraphQL to GraphQL optimizing _compiler_, providing general utilities for transforming and optimizing queries as well as generating build artifacts. A novel feature of the compiler is that it facilitates experimentation with new GraphQL features - in the form of custom directives - by making it easy to translate code using these directives into standard, spec-compliant GraphQL.
19
+ - **Relay Runtime:** A full-featured, high-performance GraphQL _runtime_ that can be used to build higher-level client APIs. The runtime features a normalized object cache, optimized "write" and "read" operations, a generic abstraction for incrementally fetching field data (such as for pagination), garbage collection for removing unreferenced cache entries, optimistic mutations with arbitrary logic, support for building subscriptions and live queries, and more.
20
+ - **React/Relay:** A high-level _product API_ that integrates the Relay Runtime with React. This is the primary public interface to Relay for most product developers, featuring APIs to fetch the data for a query or define data dependencies for reusable components (e.g. `useFragment`).
21
+
22
+ Note that these modules are _loosely coupled_. For example, the compiler emits representations of queries in a well-defined format that the runtime consumes, such that the compiler implementation can be swapped out if desired. React/Relay relies only on the well-documented public interface of the runtime, such that the actual implementation can be swapped out. We hope that this loose coupling will allow the community to explore new use-cases such as the development of specialized product APIs using the Relay runtime or integrations of the runtime with view libraries other than React.
23
+
24
+ <DocsRating />