relay-runtime 20.1.0 → 21.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (262) hide show
  1. package/experimental.js +1 -1
  2. package/experimental.js.flow +8 -8
  3. package/handlers/connection/ConnectionHandler.js.flow +5 -5
  4. package/handlers/connection/ConnectionInterface.js.flow +1 -1
  5. package/index.js +1 -1
  6. package/index.js.flow +125 -62
  7. package/lib/experimental.js +3 -3
  8. package/lib/index.js +105 -57
  9. package/lib/multi-actor-environment/ActorIdentifier.js +2 -2
  10. package/lib/multi-actor-environment/MultiActorEnvironment.js +3 -1
  11. package/lib/mutations/commitMutation.js +8 -8
  12. package/lib/mutations/validateMutation.js +4 -4
  13. package/lib/query/GraphQLTag.js +3 -3
  14. package/lib/query/fetchQuery.js +15 -3
  15. package/lib/store/DataChecker.js +38 -4
  16. package/lib/store/NormalizationEngine.js +373 -0
  17. package/lib/store/OperationExecutor.js +172 -113
  18. package/lib/store/RelayConcreteVariables.js +1 -1
  19. package/lib/store/RelayErrorTrie.js +2 -2
  20. package/lib/store/RelayExperimentalGraphResponseTransform.js +8 -8
  21. package/lib/store/RelayModernEnvironment.js +26 -19
  22. package/lib/store/RelayModernRecord.js +18 -8
  23. package/lib/store/RelayModernSelector.js +9 -9
  24. package/lib/store/RelayModernStore.js +152 -43
  25. package/lib/store/RelayPublishQueue.js +1 -1
  26. package/lib/store/RelayReader.js +76 -38
  27. package/lib/store/RelayRecordSource.js +6 -0
  28. package/lib/store/RelayReferenceMarker.js +2 -1
  29. package/lib/store/RelayResponseNormalizer.js +88 -55
  30. package/lib/store/RelayStoreSubscriptions.js +34 -10
  31. package/lib/store/RelayStoreUtils.js +8 -1
  32. package/lib/store/ResolverFragments.js +2 -2
  33. package/lib/store/live-resolvers/LiveResolverCache.js +25 -9
  34. package/lib/store/observeFragmentExperimental.js +17 -1
  35. package/lib/store/observeQueryExperimental.js +2 -2
  36. package/lib/subscription/requestSubscription.js +3 -3
  37. package/lib/util/RelayError.js +3 -0
  38. package/lib/util/RelayFeatureFlags.js +6 -2
  39. package/lib/util/RelayReplaySubject.js +4 -4
  40. package/lib/util/handlePotentialSnapshotErrors.js +2 -2
  41. package/lib/util/stableCopy.js +2 -2
  42. package/llm-docs/api-reference/entrypoint-apis/entrypoint-container.mdx +38 -0
  43. package/llm-docs/api-reference/entrypoint-apis/load-entrypoint.mdx +77 -0
  44. package/llm-docs/api-reference/entrypoint-apis/use-entrypoint-loader.mdx +99 -0
  45. package/llm-docs/api-reference/graphql/graphql-directives.mdx +378 -0
  46. package/llm-docs/api-reference/hooks/_use-lazy-load-query-extra.mdx +16 -0
  47. package/llm-docs/api-reference/hooks/load-query.mdx +84 -0
  48. package/llm-docs/api-reference/hooks/relay-environment-provider.mdx +78 -0
  49. package/llm-docs/api-reference/hooks/use-client-query.mdx +65 -0
  50. package/llm-docs/api-reference/hooks/use-fragment.mdx +69 -0
  51. package/llm-docs/api-reference/hooks/use-lazy-load-query.mdx +62 -0
  52. package/llm-docs/api-reference/hooks/use-mutation.mdx +94 -0
  53. package/llm-docs/api-reference/hooks/use-pagination-fragment.mdx +166 -0
  54. package/llm-docs/api-reference/hooks/use-prefetchable-forward-pagination-fragment.mdx +134 -0
  55. package/llm-docs/api-reference/hooks/use-preloaded-query.mdx +84 -0
  56. package/llm-docs/api-reference/hooks/use-query-loader.mdx +95 -0
  57. package/llm-docs/api-reference/hooks/use-refetchable-fragment.mdx +122 -0
  58. package/llm-docs/api-reference/hooks/use-relay-environment.mdx +37 -0
  59. package/llm-docs/api-reference/hooks/use-subscription.mdx +66 -0
  60. package/llm-docs/api-reference/relay-resolvers/docblock-format.mdx +321 -0
  61. package/llm-docs/api-reference/relay-resolvers/runtime-functions.mdx +94 -0
  62. package/llm-docs/api-reference/relay-runtime/commit-mutation.mdx +65 -0
  63. package/llm-docs/api-reference/relay-runtime/fetch-query.mdx +113 -0
  64. package/llm-docs/api-reference/relay-runtime/field-logger.mdx +170 -0
  65. package/llm-docs/api-reference/relay-runtime/observe-fragment.mdx +92 -0
  66. package/llm-docs/api-reference/relay-runtime/relay-environment.mdx +53 -0
  67. package/llm-docs/api-reference/relay-runtime/request-subscription.mdx +54 -0
  68. package/llm-docs/api-reference/relay-runtime/runtime-configuration.mdx +52 -0
  69. package/llm-docs/api-reference/relay-runtime/store.mdx +734 -0
  70. package/llm-docs/api-reference/relay-runtime/wait-for-fragment-data.mdx +89 -0
  71. package/llm-docs/api-reference/types/CacheConfig.mdx +8 -0
  72. package/llm-docs/api-reference/types/Disposable.mdx +4 -0
  73. package/llm-docs/api-reference/types/GraphQLSubscriptionConfig.mdx +17 -0
  74. package/llm-docs/api-reference/types/MutationConfig.mdx +31 -0
  75. package/llm-docs/api-reference/types/SelectorStoreUpdater.mdx +6 -0
  76. package/llm-docs/api-reference/types/UploadableMap.mdx +3 -0
  77. package/llm-docs/community/learning-resources.mdx +64 -0
  78. package/llm-docs/debugging/declarative-mutation-directives.mdx +34 -0
  79. package/llm-docs/debugging/disallowed-id-types-error.mdx +43 -0
  80. package/llm-docs/debugging/inconsistent-typename-error.mdx +47 -0
  81. package/llm-docs/debugging/relay-devtools.mdx +73 -0
  82. package/llm-docs/debugging/why-null.mdx +116 -0
  83. package/llm-docs/editor-support.mdx +55 -0
  84. package/llm-docs/error-reference/unknown-field.mdx +36 -0
  85. package/llm-docs/getting-started/babel-plugin.mdx +31 -0
  86. package/llm-docs/getting-started/compiler-config.mdx +25 -0
  87. package/llm-docs/getting-started/compiler.mdx +82 -0
  88. package/llm-docs/getting-started/lint-rules.mdx +87 -0
  89. package/llm-docs/getting-started/production.mdx +30 -0
  90. package/llm-docs/getting-started/quick-start.mdx +213 -0
  91. package/llm-docs/glossary/glossary.mdx +1040 -0
  92. package/llm-docs/guided-tour/list-data/advanced-pagination.mdx +157 -0
  93. package/llm-docs/guided-tour/list-data/connections.mdx +81 -0
  94. package/llm-docs/guided-tour/list-data/pagination.mdx +193 -0
  95. package/llm-docs/guided-tour/list-data/rendering-connections.mdx +112 -0
  96. package/llm-docs/guided-tour/list-data/streaming-pagination.mdx +87 -0
  97. package/llm-docs/guided-tour/managing-data-outside-react/retaining-queries.mdx +51 -0
  98. package/llm-docs/guided-tour/refetching/refetching-queries-with-different-data.mdx +337 -0
  99. package/llm-docs/guided-tour/refetching/refreshing-queries.mdx +350 -0
  100. package/llm-docs/guided-tour/rendering/environment.mdx +59 -0
  101. package/llm-docs/guided-tour/rendering/error-states.mdx +295 -0
  102. package/llm-docs/guided-tour/rendering/fragments.mdx +354 -0
  103. package/llm-docs/guided-tour/rendering/loading-states.mdx +245 -0
  104. package/llm-docs/guided-tour/rendering/queries.mdx +261 -0
  105. package/llm-docs/guided-tour/rendering/variables.mdx +233 -0
  106. package/llm-docs/guided-tour/reusing-cached-data/fetch-policies.mdx +56 -0
  107. package/llm-docs/guided-tour/reusing-cached-data/filling-in-missing-data.mdx +102 -0
  108. package/llm-docs/guided-tour/reusing-cached-data/introduction.mdx +22 -0
  109. package/llm-docs/guided-tour/reusing-cached-data/presence-of-data.mdx +93 -0
  110. package/llm-docs/guided-tour/reusing-cached-data/rendering-partially-cached-data.mdx +175 -0
  111. package/llm-docs/guided-tour/reusing-cached-data/staleness-of-data.mdx +116 -0
  112. package/llm-docs/guided-tour/updating-data/client-only-data.mdx +115 -0
  113. package/llm-docs/guided-tour/updating-data/graphql-mutations.mdx +334 -0
  114. package/llm-docs/guided-tour/updating-data/graphql-subscriptions.mdx +279 -0
  115. package/llm-docs/guided-tour/updating-data/imperatively-modifying-linked-fields.mdx +511 -0
  116. package/llm-docs/guided-tour/updating-data/imperatively-modifying-store-data-legacy.mdx +142 -0
  117. package/llm-docs/guided-tour/updating-data/imperatively-modifying-store-data.mdx +275 -0
  118. package/llm-docs/guided-tour/updating-data/introduction.mdx +25 -0
  119. package/llm-docs/guided-tour/updating-data/local-data-updates.mdx +71 -0
  120. package/llm-docs/guided-tour/updating-data/typesafe-updaters-faq.mdx +83 -0
  121. package/llm-docs/guided-tour/updating-data/updating-connections.mdx +592 -0
  122. package/llm-docs/guides/alias-directive.mdx +160 -0
  123. package/llm-docs/guides/catch-directive.mdx +167 -0
  124. package/llm-docs/guides/client-schema-extensions.mdx +208 -0
  125. package/llm-docs/guides/codemods.mdx +66 -0
  126. package/llm-docs/guides/data-driven-dependencies/client-3d.mdx +255 -0
  127. package/llm-docs/guides/data-driven-dependencies/configuration.mdx +127 -0
  128. package/llm-docs/guides/data-driven-dependencies/introduction.mdx +39 -0
  129. package/llm-docs/guides/data-driven-dependencies/server-3d.mdx +664 -0
  130. package/llm-docs/guides/document-comparison.mdx +106 -0
  131. package/llm-docs/guides/graphql-server-specification.mdx +453 -0
  132. package/llm-docs/guides/network-layer.mdx +69 -0
  133. package/llm-docs/guides/persisted-queries.mdx +328 -0
  134. package/llm-docs/guides/relay-resolvers/context.mdx +99 -0
  135. package/llm-docs/guides/relay-resolvers/defining-fields.mdx +151 -0
  136. package/llm-docs/guides/relay-resolvers/defining-types.mdx +164 -0
  137. package/llm-docs/guides/relay-resolvers/deprecated.mdx +27 -0
  138. package/llm-docs/guides/relay-resolvers/derived-fields.mdx +127 -0
  139. package/llm-docs/guides/relay-resolvers/descriptions.mdx +44 -0
  140. package/llm-docs/guides/relay-resolvers/enabling.mdx +41 -0
  141. package/llm-docs/guides/relay-resolvers/errors.mdx +64 -0
  142. package/llm-docs/guides/relay-resolvers/field-arguments.mdx +63 -0
  143. package/llm-docs/guides/relay-resolvers/introduction.mdx +62 -0
  144. package/llm-docs/guides/relay-resolvers/limitations.mdx +30 -0
  145. package/llm-docs/guides/relay-resolvers/live-fields.mdx +164 -0
  146. package/llm-docs/guides/relay-resolvers/return-types.mdx +161 -0
  147. package/llm-docs/guides/relay-resolvers/suspense.mdx +41 -0
  148. package/llm-docs/guides/required-directive.mdx +240 -0
  149. package/llm-docs/guides/semantic-nullability.mdx +93 -0
  150. package/llm-docs/guides/testing-relay-components.mdx +642 -0
  151. package/llm-docs/guides/testing-relay-with-preloaded-queries.mdx +160 -0
  152. package/llm-docs/guides/throw-on-field-error-directive.mdx +58 -0
  153. package/llm-docs/guides/type-emission.mdx +414 -0
  154. package/llm-docs/home.mdx +32 -0
  155. package/llm-docs/principles-and-architecture/architecture-overview.mdx +24 -0
  156. package/llm-docs/principles-and-architecture/compiler-architecture.mdx +106 -0
  157. package/llm-docs/principles-and-architecture/runtime-architecture.mdx +249 -0
  158. package/llm-docs/principles-and-architecture/thinking-in-graphql.mdx +309 -0
  159. package/llm-docs/principles-and-architecture/thinking-in-relay.mdx +104 -0
  160. package/llm-docs/principles-and-architecture/videos.mdx +50 -0
  161. package/llm-docs/tutorial/arrays-lists.mdx +126 -0
  162. package/llm-docs/tutorial/fragments-1.mdx +487 -0
  163. package/llm-docs/tutorial/graphql.mdx +172 -0
  164. package/llm-docs/tutorial/interfaces-polymorphism.mdx +161 -0
  165. package/llm-docs/tutorial/intro.mdx +58 -0
  166. package/llm-docs/tutorial/mutations-updates.mdx +624 -0
  167. package/llm-docs/tutorial/organizing-mutations-queries-and-subscriptions.mdx +13 -0
  168. package/llm-docs/tutorial/queries-1.mdx +267 -0
  169. package/llm-docs/tutorial/queries-2.mdx +389 -0
  170. package/llm-docs/tutorial/refetchable-fragments.mdx +352 -0
  171. package/multi-actor-environment/ActorIdentifier.js.flow +2 -2
  172. package/multi-actor-environment/ActorSpecificEnvironment.js.flow +7 -7
  173. package/multi-actor-environment/ActorUtils.js.flow +1 -1
  174. package/multi-actor-environment/MultiActorEnvironment.js.flow +12 -8
  175. package/multi-actor-environment/MultiActorEnvironmentTypes.js.flow +3 -3
  176. package/mutations/RelayDeclarativeMutationConfig.js.flow +9 -9
  177. package/mutations/RelayRecordProxy.js.flow +8 -11
  178. package/mutations/RelayRecordSourceMutator.js.flow +4 -4
  179. package/mutations/RelayRecordSourceProxy.js.flow +4 -4
  180. package/mutations/RelayRecordSourceSelectorProxy.js.flow +6 -6
  181. package/mutations/applyOptimisticMutation.js.flow +2 -2
  182. package/mutations/commitMutation.js.flow +20 -16
  183. package/mutations/createUpdatableProxy.js.flow +19 -19
  184. package/mutations/readUpdatableFragment.js.flow +3 -3
  185. package/mutations/readUpdatableQuery.js.flow +3 -3
  186. package/mutations/validateMutation.js.flow +7 -7
  187. package/network/RelayNetworkTypes.js.flow +4 -4
  188. package/network/RelayObservable.js.flow +16 -14
  189. package/network/RelayQueryResponseCache.js.flow +3 -3
  190. package/network/wrapNetworkWithLogObserver.js.flow +1 -1
  191. package/package.json +2 -1
  192. package/query/GraphQLTag.js.flow +22 -10
  193. package/query/fetchQuery.js.flow +23 -10
  194. package/query/fetchQuery_DEPRECATED.js.flow +1 -1
  195. package/store/DataChecker.js.flow +43 -9
  196. package/store/NormalizationEngine.js.flow +779 -0
  197. package/store/OperationExecutor.js.flow +173 -70
  198. package/store/RelayConcreteVariables.js.flow +5 -5
  199. package/store/RelayErrorTrie.js.flow +12 -12
  200. package/store/RelayExperimentalGraphResponseHandler.js.flow +3 -3
  201. package/store/RelayExperimentalGraphResponseTransform.js.flow +10 -10
  202. package/store/RelayModernEnvironment.js.flow +41 -26
  203. package/store/RelayModernFragmentSpecResolver.js.flow +1 -1
  204. package/store/RelayModernOperationDescriptor.js.flow +1 -1
  205. package/store/RelayModernRecord.js.flow +46 -22
  206. package/store/RelayModernSelector.js.flow +21 -21
  207. package/store/RelayModernStore.js.flow +219 -58
  208. package/store/RelayOperationTracker.js.flow +2 -2
  209. package/store/RelayOptimisticRecordSource.js.flow +2 -2
  210. package/store/RelayPublishQueue.js.flow +21 -12
  211. package/store/RelayReader.js.flow +130 -58
  212. package/store/RelayRecordSource.js.flow +10 -0
  213. package/store/RelayRecordState.js.flow +1 -1
  214. package/store/RelayReferenceMarker.js.flow +5 -4
  215. package/store/RelayResponseNormalizer.js.flow +130 -54
  216. package/store/RelayStoreSubscriptions.js.flow +52 -8
  217. package/store/RelayStoreTypes.js.flow +153 -64
  218. package/store/RelayStoreUtils.js.flow +15 -7
  219. package/store/ResolverCache.js.flow +2 -2
  220. package/store/ResolverFragments.js.flow +12 -12
  221. package/store/StoreInspector.js.flow +6 -7
  222. package/store/cloneRelayHandleSourceField.js.flow +1 -1
  223. package/store/cloneRelayScalarHandleSourceField.js.flow +1 -1
  224. package/store/createRelayContext.js.flow +1 -1
  225. package/store/createRelayLoggingContext.js.flow +4 -4
  226. package/store/defaultGetDataID.js.flow +2 -2
  227. package/store/isRelayModernEnvironment.js.flow +4 -2
  228. package/store/live-resolvers/LiveResolverCache.js.flow +55 -20
  229. package/store/live-resolvers/LiveResolverSuspenseSentinel.js.flow +3 -3
  230. package/store/live-resolvers/getOutputTypeRecordIDs.js.flow +1 -1
  231. package/store/live-resolvers/isLiveStateValue.js.flow +2 -2
  232. package/store/live-resolvers/resolverDataInjector.js.flow +8 -5
  233. package/store/observeFragmentExperimental.js.flow +49 -20
  234. package/store/observeQueryExperimental.js.flow +5 -5
  235. package/store/readInlineData.js.flow +4 -4
  236. package/store/waitForFragmentExperimental.js.flow +3 -3
  237. package/subscription/requestSubscription.js.flow +7 -7
  238. package/util/NormalizationNode.js.flow +34 -32
  239. package/util/ReaderNode.js.flow +32 -30
  240. package/util/RelayConcreteNode.js.flow +5 -5
  241. package/util/RelayError.js.flow +4 -1
  242. package/util/RelayFeatureFlags.js.flow +21 -1
  243. package/util/RelayProfiler.js.flow +1 -1
  244. package/util/RelayReplaySubject.js.flow +3 -3
  245. package/util/RelayRuntimeTypes.js.flow +11 -11
  246. package/util/createPayloadFor3DField.js.flow +9 -5
  247. package/util/deepFreeze.js.flow +2 -2
  248. package/util/getFragmentIdentifier.js.flow +1 -1
  249. package/util/getPaginationMetadata.js.flow +1 -1
  250. package/util/getPaginationVariables.js.flow +1 -1
  251. package/util/getPendingOperationsForFragment.js.flow +2 -2
  252. package/util/getRefetchMetadata.js.flow +6 -5
  253. package/util/getValueAtPath.js.flow +3 -3
  254. package/util/handlePotentialSnapshotErrors.js.flow +5 -5
  255. package/util/isEmptyObject.js.flow +1 -1
  256. package/util/isPromise.js.flow +2 -2
  257. package/util/isScalarAndEqual.js.flow +1 -1
  258. package/util/recycleNodesInto.js.flow +2 -2
  259. package/util/registerEnvironmentWithDevTools.js.flow +1 -1
  260. package/util/shallowFreeze.js.flow +1 -1
  261. package/util/stableCopy.js.flow +5 -5
  262. package/util/withProvidedVariables.js.flow +14 -10
@@ -0,0 +1,160 @@
1
+ ---
2
+ id: alias-directive
3
+ title: "@alias Directive"
4
+ slug: /guides/alias-directive/
5
+ description: Relay guide to @alias
6
+ keywords:
7
+ - alias
8
+ - directive
9
+ - fragment
10
+ ---
11
+
12
+ The `@alias` directive allows you to expose a spread fragment — either a named fragment spread or an inline fragment — as a named field within your selection. This allows Relay to provide additional type safety in the case where your fragment’s type may not match the parent selection.
13
+
14
+ :::info
15
+ This document describes why the `@alias` directive was introduced, and how it can be used to improve type safety in your Relay applications. **To learn about its API, see the [API Reference](../api-reference/graphql/graphql-directives.mdx#alias).**
16
+ :::
17
+
18
+ Let's look at an example where `@alias` can be useful:
19
+
20
+ ## Abstract Types
21
+
22
+ Imagine you have a component that renders information about a Viewer:
23
+
24
+ ```ts
25
+ function MyViewer({viewerKey}) {
26
+ const {name} = useFragment(graphql`
27
+ fragment MyViewer on Viewer {
28
+ name @required(action: THROW)
29
+ }`, viewerKey);
30
+
31
+ return `My name is ${name}. That's ${name.length} letters long!`;
32
+ }
33
+ ```
34
+
35
+ To use that component in a component that has a fragment on Node (which Viewer implements), you could write something like this:
36
+
37
+ ```ts
38
+ function MyNode({nodeKey}) {
39
+ const node = useFragment(graphql`
40
+ fragment MyFragment on Node {
41
+ ...MyViewer
42
+ }`, nodeKey);
43
+
44
+ return <MyViewer viewerKey={node} />
45
+ }
46
+ ```
47
+
48
+ Can you spot the problem? We don’t actually know that the node we are passing to `<MyViewer />` is actually a Viewer `<MyViewer />`. If `<MyNode />` tries to render a Comment — which also implements Node — we will get a runtime error in `<MyViewer />` because the field name is not present on Comment.
49
+
50
+ ```
51
+ TypeError: Cannot read properties of undefined (reading 'length')
52
+ ```
53
+
54
+ Not only do we not get a type letting us know that about this potential issue, but even at runtime, there is no way to check if node implements Viewer because Viewer is an abstract type!
55
+
56
+ ## Aliased Fragments
57
+
58
+ Aliased fragments can solve this problem. Here’s what `<MyNode />` would look like using them:
59
+
60
+ ```ts
61
+ function MyNode({nodeKey}) {
62
+ const node = useFragment(graphql`
63
+ fragment MyFragment on Node {
64
+ ...MyViewer @alias(as: "my_viewer")
65
+ }`, nodeKey);
66
+
67
+ // Relay returns the fragment key as its own nullable property
68
+ if(node.my_viewer == null) {
69
+ return null;
70
+ }
71
+
72
+ // Because `my_viewer` is typed as nullable, Flow/TypeScript will
73
+ // show an error if you try to use the `my_viewer` without first
74
+ // performing a null check.
75
+ // VVVVVVVVVVVVVV
76
+ return <MyViewer viewerKey={node.my_viewer} />
77
+ }
78
+ ```
79
+
80
+ With this approach, you can see that Relay exposes the fragment key as its own nullable property, which allows us to check that node actually implements Viewer and even allows Flow to enforce that the component handles the possibility!
81
+
82
+ ## @skip and @include
83
+
84
+ A similar problem can occur when using `@skip` and `@include` directives on fragments. In order to safely use the spread fragment, you need to check if it was fetched. Historically this has required gaining access to the query variable that was used to determine if the fragment was skipped or included.
85
+
86
+ With `@alias`, you can now check if the fragment was fetched by simply assigning the fragment an alias, and checking if the alias is null:
87
+
88
+ ```ts
89
+ function MyUser({userKey}) {
90
+ const user = useFragment(graphql`
91
+ fragment MyFragment on User {
92
+ ...ConditionalData @skip(if: $someVar) @alias
93
+ }`, userKey);
94
+
95
+ if(user.ConditionalData == null) {
96
+ return "No data fetched";
97
+ }
98
+ return <ConditionalData userKey={user.ConditionalData} />
99
+ }
100
+ ```
101
+
102
+ ## Enforced Safety
103
+
104
+ We've outlined two different ways that fragments can be unsafe in Relay today without `@alias`. To help prevent runtime issues resulting from these unsafe edge cases, Relay requires that all conditionally fetched fragments are aliased.
105
+
106
+ To disable this validation in your project, you can disable the `enforce_fragment_alias_where_ambiguous` compiler feature flag for your project. If you need to enable incremental adoption of this enforcement, Relay exposes a directive `@dangerously_unaliased_fixme` which will suppress enforcement errors. This will allow you to enable the enforcement for all new spreads without first needing to migrate all existing issues.
107
+
108
+ The [Relay VSCode extension](../editor-support.mdx) offers quick fixes to add either `@alias` or `@dangerously_unaliased_fixme` to unsafe fragments, and the
109
+ [mark-dangerous-conditional-fragment-spreads](../codemods/#mark-dangerous-conditional-fragment-spreads) codemod can be used to apply `@dangerously_unaliased_fixme` across your entire project.
110
+
111
+ ## Use with @required
112
+
113
+ `@alias` can be used with [`@required(action: NONE)`](./required-directive.mdx) to group together required fields. In the following example, we group `name` and `email` together as `requiredFields`. If either is null, that null will bubble up to, the `user.requiredFields` field, making it null. This allows us to perform a single check, without impacting the `id` field.
114
+
115
+ ```ts
116
+ function MyUser({userKey}) {
117
+ const user = useFragment(graphql`
118
+ fragment MyFragment on User {
119
+ id
120
+ ... @alias(as: "requiredFields") {
121
+ name @required(action: NONE)
122
+ email @required(action: NONE)
123
+ }
124
+ }`, userKey);
125
+
126
+ if(user.requiredFields == null) {
127
+ return `Missing required fields for user ${user.id}`;
128
+ }
129
+ return `Hello ${user.requiredFields.name} (${user.requiredFields.email}).!`;
130
+ }
131
+ ```
132
+
133
+ :::note
134
+ Using `@required` on a fragment spread that has an `@alias` is not currently supported, but we may add support in the future.
135
+ :::
136
+
137
+ ## Under the Hood
138
+
139
+ For people familiar with Relay, or curious to learn, here is a brief description of how this feature is implemented:
140
+
141
+ Under the hood, `@alias` is implemented entirely within Relay (compiler and runtime). It does not require any server support. The Relay compiler interprets the `@alias` directive, and generates types indicating that the fragment key, or inline fragment data, will be attached to the new field, rather than directly on the parent object. In the Relay runtime artifact, it wraps the fragment node with a new node indicating the name of the alias and additional information about the type of the fragment.
142
+
143
+ The Relay compiler also inserts an additional field into the spread which allows it to determine if the fragment has matched:
144
+
145
+ ```graphql
146
+ fragment Foo on Node {
147
+ ... on Viewer {
148
+ isViewer: __typename # <-- Relay inserts this
149
+ name
150
+ }
151
+ }
152
+ ```
153
+
154
+ Relay can now check for the existence of the `isViewer` field in the response to know if the fragment matched.
155
+
156
+ When Relay reads the content of your fragment out of the store using its runtime artifact, it uses this information to attach the fragment key to this new field, rather than attaching it directly to the parent object.
157
+
158
+ ### Related
159
+
160
+ While `@alias` is a Relay-specific feature, it draws inspiration from fragment modularity as outlined in the GraphQL [RFC Fragment Modularity](https://github.com/graphql/graphql-wg/blob/main/rfcs/FragmentModularity.mdx).
@@ -0,0 +1,167 @@
1
+ ---
2
+ id: catch-directive
3
+ title: '@catch Directive'
4
+ slug: /guides/catch-directive/
5
+ description: Relay guide to @catch
6
+ keywords:
7
+ - catch
8
+ - directive
9
+ - optional
10
+ ---
11
+
12
+ import DocsRating from '@site/src/core/DocsRating';
13
+
14
+ The `@catch` directive can be added to fields, fragment/operation definitions or
15
+ aliased inline fragment spreads declare how exceptions and unexpected values
16
+ should be handled at runtime. Using `@catch` allows Relay to surface these error
17
+ states as part of your fragment/query/mutation data instead of a null value
18
+ (which has been the default behavior), or a runtime exception if
19
+ [`@throwOnFieldError`](./throw-on-field-error-directive.mdx) is being used.
20
+
21
+ :::tip
22
+ Both `@catch` and `@throwOnFieldError` only handle field errors in the
23
+ query/fragment/mutation in which they are used. They **do not** handle errors
24
+ related to fields in any spread fragments.
25
+ :::
26
+
27
+ ## `to` Argument
28
+
29
+ The `@catch` directive accepts an optional `to` argument which has two options:
30
+
31
+ - `RESULT` (default): The value is returned as `{ ok: true, value: T } | { ok: false, errors: [error] }`. This allows you implement explicit field-granular error handling in your application.
32
+ - `NULL`: If an error is encountered within the `@catch`, the value will be replaced with `null`.
33
+
34
+ ## Examples
35
+
36
+ If a `@catch` error is caught directly on the field that the error originated
37
+ from - the error is provided on that field. Here's an example:
38
+
39
+ ```graphql
40
+ query MyQuery {
41
+ viewer {
42
+ name @catch
43
+ age
44
+ }
45
+ }
46
+ ```
47
+
48
+ If `name` contains an error - it would be provided in the response data on the
49
+ `name` field - like so:
50
+
51
+ ```js
52
+ {
53
+ viewer: {
54
+ name: {
55
+ ok: false,
56
+ errors: [{path: ['viewer', 'name']}]
57
+ }
58
+ age: 39
59
+ }
60
+ }
61
+ ```
62
+
63
+ However, if `@catch` exists on one of the ancestors of a field, that error will
64
+ bubble up to there, like so:
65
+
66
+ ```graphql
67
+ query MyQuery {
68
+ viewer @catch {
69
+ name
70
+ age
71
+ }
72
+ }
73
+ ```
74
+
75
+ ```js
76
+ {
77
+ viewer: {
78
+ ok: false,
79
+ errors: [{ path: ['viewer', 'name'] } ]
80
+ }
81
+ }
82
+ ```
83
+
84
+ ## Implications for nullability
85
+
86
+ Fields whose errors are explicitly handled by `@catch`, either by being
87
+ annotated with `@catch` or by being nested with a `@catch` ancestor will be
88
+ typed using their [Semantic Nullability](./semantic-nullability.mdx). In other
89
+ words, if a field has been marked as `@semanticNonNull` in the server schema to
90
+ indicate that it will only be null in the case of error, Relay will type that
91
+ field as non-nullable in its generated Flow/TypeScript types.
92
+
93
+ ## What can be caught with `@catch`?
94
+
95
+ ### Payload Field Errors
96
+
97
+ Payload [field
98
+ errors](https://spec.graphql.org/October2021/#sec-Errors.Field-errors) are
99
+ errors that occur as the result of a server-side exception while executing a
100
+ given field's resolver. In this case, the GraphQL specifies that the server must
101
+ provide a null value where a value should be, and a separate errors object.
102
+
103
+ When you `@catch` on a field, Relay takes those errors and provides them to you
104
+ in-line instead, making them easier to handle, and no longer invisible.
105
+
106
+ ### @required(action: THROW) within an @catch
107
+
108
+ If you have an `@required(action: THROW)` with an ancestor that contains a
109
+ `@catch` - instead of throwing an exception, the `@required` error would bubble
110
+ up and be provided in the same way normal errors would. Here's an example:
111
+
112
+ ```graphql
113
+ query MyQuery {
114
+ viewer @catch {
115
+ name @required(action: THROW)
116
+ age
117
+ }
118
+ }
119
+ ```
120
+
121
+ ```js
122
+ {
123
+ viewer: {
124
+ ok: false,
125
+ errors: [{ path: ["viewer", "name"] }]
126
+ }
127
+ }
128
+ ```
129
+
130
+ ### Missing Data in response
131
+
132
+ [Here is an example of where missing data may occur in Relay](https://relay.dev/docs/next/debugging/why-null/#graph-relationship-change)
133
+
134
+ If a field is expected to have a value, and that field is undefined - the field
135
+ is considered to be "missing data". This is also an unexpected state - and when
136
+ it happens with an `@catch` as an ancestor, it will also be caught like so:
137
+
138
+ ```js
139
+ {
140
+ viewer: {
141
+ ok: false,
142
+ errors: [{ path: ["viewer", "name"] }]
143
+ }
144
+ }
145
+ ```
146
+
147
+ ## How does `@catch` interact with `@throwOnFieldError`?
148
+
149
+ Using `@throwOnFieldError` enables fields to throw a JavaScript exception when a
150
+ field error occurs. By using `@catch` - you tell Relay that you don't want a
151
+ JavaScript exception in this case. Instead, you are requesting that the error be
152
+ provided in the data object, with the same behaviors and rules as are listed
153
+ above (including bubbling to a parent field).
154
+
155
+ It is important to note that you can still use @catch without
156
+ @throwOnFieldError. It will still provide you the error in the data object. But
157
+ other fields that are not under a `@catch` will still not throw - because
158
+ `@throwOnFieldError` would be missing.
159
+
160
+ Read more about `@throwOnFieldError`
161
+ [here](https://relay.dev/docs/next/api-reference/graphql-and-directives/#throwonfielderror-experimental).
162
+
163
+ ## GraphQL Conf Talk
164
+
165
+ The Relay team gave a talk at GraphQL Conf 2024 about `@catch` and explicit error handling in Relay. You can watch it here:
166
+
167
+ <iframe src="https://www.youtube-nocookie.com/embed/_TSYKAtaK5A" width={640} height={360} allowFullScreen={true} frameBorder="0" />
@@ -0,0 +1,208 @@
1
+ ---
2
+ id: client-schema-extensions
3
+ title: Client Schema Extensions
4
+ slug: /guides/client-schema-extensions/
5
+ description: Relay guide to client schema extensions
6
+ keywords:
7
+ - client
8
+ - schema
9
+ - extension
10
+ - commitLocalUpdate
11
+ ---
12
+
13
+ import DocsRating from '@site/src/core/DocsRating';
14
+
15
+ :::note
16
+ See also [the local data updates](../../guided-tour/updating-data/local-data-updates/) and [client-only data](../../guided-tour/updating-data/client-only-data/) sections of the guided tour.
17
+ :::
18
+
19
+ Relay can be used to read and write local data, and act as a single source of truth for _all_ data in your client application.
20
+
21
+ The Relay Compiler fully supports client-side extensions of the schema, which allows you to define local fields and types.
22
+
23
+ ## Table of Contents:
24
+
25
+ - [Extending the server schema](#extending-the-server-schema)
26
+ - [Querying local state](#querying-local-state)
27
+ - [Mutating local state](#mutating-local-state)
28
+ - [Initial local state](#initial-local-state)
29
+
30
+ ## Extending the server schema
31
+
32
+ To extend the server schema, create a new `.graphql` file inside your `--src`
33
+ directory. Let's call it `./src/clientSchema.graphql`. This file needs to be
34
+ referenced in the `"schemaExtensions"` of your Relay config, either directly or
35
+ via its folder.
36
+
37
+ This schema describes what local data can be queried on the client.
38
+ It can even be used to extend an existing server schema.
39
+
40
+ For example, we can create a new type called `Note`:
41
+
42
+ ```graphql
43
+ type Note {
44
+ id: ID!
45
+ title: String
46
+ body: String
47
+ }
48
+ ```
49
+
50
+ And then extend the server schema type `User`, with a list of `Note`, called `notes`.
51
+
52
+ ```graphql
53
+ extend type User {
54
+ notes: [Note]
55
+ }
56
+ ```
57
+
58
+ ## Querying local state
59
+
60
+ Accessing local data is no different from querying your GraphQL server, although you are required to include at least one server field in the query.
61
+ The field can be from the server schema, or it can be schema agnostic, like an introspection field (e.g. `__typename`).
62
+
63
+ Here, we use [useLazyLoadQuery](../../api-reference/use-lazy-load-query) to get the current `User` via the `viewer` field, along with their id, name and the local list of notes.
64
+
65
+ ```javascript
66
+ // Example.js
67
+ import * as React from 'react';
68
+ import { useLazyLoadQuery, graphql } from 'react-relay';
69
+
70
+ const Example = (props) => {
71
+ const data = useLazyLoadQuery(graphql`
72
+ query ExampleQuery {
73
+ viewer {
74
+ id
75
+ name
76
+ notes {
77
+ id
78
+ title
79
+ body
80
+ }
81
+ }
82
+ }
83
+ `, {});
84
+ // ...
85
+ }
86
+ ```
87
+
88
+ ## Mutating local state
89
+
90
+ All local data lives in the [Relay Store](../../api-reference/store/).
91
+
92
+ Updating local state can be done with any `updater` function.
93
+
94
+ The `commitLocalUpdate` function is especially ideal for this, because writes to local state are usually executed outside of a mutation.
95
+
96
+ To build upon the previous example, let's try creating, updating and deleting a `Note` from the list of `notes` on `User`.
97
+
98
+ ### Create
99
+
100
+ ```javascript
101
+ import {commitLocalUpdate} from 'react-relay';
102
+
103
+ let tempID = 0;
104
+
105
+ function createUserNote(environment) {
106
+ commitLocalUpdate(environment, store => {
107
+ const user = store.getRoot().getLinkedRecord('viewer');
108
+ const userNoteRecords = user.getLinkedRecords('notes') || [];
109
+
110
+ // Create a unique ID.
111
+ const dataID = `client:Note:${tempID++}`;
112
+
113
+ //Create a new note record.
114
+ const newNoteRecord = store.create(dataID, 'Note');
115
+
116
+ // Add the record to the user's list of notes.
117
+ user.setLinkedRecords([...userNoteRecords, newNoteRecord], 'notes');
118
+ });
119
+ }
120
+ ```
121
+
122
+ Note that since this record will be rendered by the `ExampleQuery` via `useLazyLoadQuery`, the query data will automatically be retained and won't be garbage collected.
123
+
124
+ If no component is rendering the local data and you want to manually retain it, you can do so by calling `environment.retain()`:
125
+
126
+ ```javascript
127
+ import {createOperationDescriptor, getRequest} from 'relay-runtime';
128
+
129
+ // Create a query that references that record
130
+ const localDataQuery = graphql`
131
+ query LocalDataQuery {
132
+ viewer {
133
+ notes {
134
+ __typename
135
+ }
136
+ }
137
+ }
138
+ `;
139
+
140
+ // Create an operation descriptor for the query
141
+ const request = getRequest(localDataQuery);
142
+ const operation = createOperationDescriptor(request, {} /* variables */);
143
+
144
+
145
+ // Tell Relay to retain this operation so any data referenced by it isn't garbage collected
146
+ // In this case, all the notes linked to the `viewer` will be retained
147
+ const disposable = environment.retain(operation);
148
+
149
+
150
+ // Whenever you don't need that data anymore and it's okay for Relay to garbage collect it,
151
+ // you can dispose of the retain
152
+ disposable.dispose();
153
+ ```
154
+
155
+ ### Update
156
+
157
+ ```javascript
158
+ import {commitLocalUpdate} from 'react-relay';
159
+
160
+ function updateUserNote(environment, dataID, body, title) {
161
+ commitLocalUpdate(environment, store => {
162
+ const note = store.get(dataID);
163
+
164
+ note.setValue(body, 'body');
165
+ note.setValue(title, 'title')
166
+ });
167
+ }
168
+ ```
169
+
170
+ ### Delete
171
+
172
+ ```javascript
173
+ import {commitLocalUpdate} from 'react-relay';
174
+
175
+ function deleteUserNote(environment, dataID) {
176
+ commitLocalUpdate(environment, store => {
177
+ const user = store.getRoot().getLinkedRecord('viewer');
178
+ const userNoteRecords = user.getLinkedRecords('notes');
179
+
180
+ // Remove the note from the list of user notes.
181
+ const newUserNoteRecords = userNoteRecords.filter(x => x.getDataID() !== dataID);
182
+
183
+ // Delete the note from the store.
184
+ store.delete(dataID);
185
+
186
+ // Set the new list of notes.
187
+ user.setLinkedRecords(newUserNoteRecords, 'notes');
188
+ });
189
+ }
190
+ ```
191
+
192
+ ## Initial local state
193
+
194
+ All new client-side schema fields default to `undefined` value. Often however, you will want to set the initial state before querying local data.
195
+ You can use an updater function via `commitLocalUpdate` to prime local state.
196
+
197
+ ```javascript
198
+ import {commitLocalUpdate} from 'react-relay';
199
+
200
+ commitLocalUpdate(environment, store => {
201
+ const user = store.getRoot().getLinkedRecord('viewer');
202
+
203
+ // initialize user notes to an empty array.
204
+ user.setLinkedRecords([], 'notes');
205
+ });
206
+ ```
207
+
208
+ <DocsRating />
@@ -0,0 +1,66 @@
1
+ ---
2
+ id: codemods
3
+ title: Codemods
4
+ slug: /guides/codemods/
5
+ description: Relay guide to codemods
6
+ keywords:
7
+ - codemod
8
+ - codemods
9
+ ---
10
+
11
+ import DocsRating from '@site/src/core/DocsRating'; import {FbInternalOnly,
12
+ OssOnly, fbContent} from 'docusaurus-plugin-internaldocs-fb/internal'; import
13
+ Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem';
14
+
15
+ The Relay compiler has the ability to make changes across the source files of
16
+ your projects with the use of codemods. You can see the list of codemods
17
+ available by running the Relay compiler's `codemod` command:
18
+
19
+ ```
20
+ > relay codemod --help
21
+ Apply codemod (verification with auto-applied fixes)
22
+
23
+ Usage: relay codemod [OPTIONS] [CONFIG] <COMMAND>
24
+
25
+ Commands:
26
+ mark-dangerous-conditional-fragment-spreads Marks unaliased conditional fragment spreads as @dangerously_unaliased_fixme
27
+ help Print this message or the help of the given subcommand(s)
28
+
29
+ Arguments:
30
+ [CONFIG] Compile using this config file. If not provided, searches for a config in package.json under the `relay` key or `relay.config.json` files among other up from the current working directory
31
+
32
+ Options:
33
+ -p, --project <project> Compile only this project. You can pass this argument multiple times. to compile multiple projects. If excluded, all projects will be compiled
34
+ -h, --help Print help
35
+ ```
36
+
37
+ ## Available codemods
38
+
39
+ The compiler currently has these available codemods:
40
+
41
+ ### mark-dangerous-conditional-fragment-spreads
42
+
43
+ This codemod finds fragment spreads that are _dangerously unaliased_; that is,
44
+ the fragment might not be fetched due to a directive such as `@skip` or its
45
+ inclusion on a mismatched type within a union. If such a conditional fragment is
46
+ not aliased with [`@alias`](../alias-directive/), there is no way for the
47
+ resulting generated Flow or TypeScript types to reflect its nullability. This
48
+ codemod will add the `@dangerously_unaliased_fixme` directive to such fragment
49
+ spreads, indicating to developers that there is a problem to be fixed. After
50
+ applying this codemod, the `enforce_fragment_alias_where_ambiguous` feature flag
51
+ can be enabled, which will ensure any future ambiguous fragment spreads must be
52
+ aliased.
53
+
54
+ Since this codemod can potentially modify many files, there is an optional
55
+ `--rollout` parameter which, if used alongside the
56
+ `enforce_fragment_alias_where_ambiguous` feature flag in rollout mode, allows
57
+ progressive codemod and enforcement of this validation.
58
+
59
+ ### remove-unnecessary-required-directives
60
+
61
+ Removes [@required](../api-reference/graphql/graphql-directives.mdx#required)
62
+ directives from non-null fields within
63
+ [@throwOnFieldError](../api-reference/graphql/graphql-directives.mdx#throwonfielderror)
64
+ fragments and operations, or linked fields with
65
+ [`@catch`](../guides/catch-directive.mdx), where the compiler is certain that the
66
+ directive does not change the generated types for the data being fetched.