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,164 @@
1
+ ---
2
+ id: defining-types
3
+ title: "Defining Types"
4
+ slug: /guides/relay-resolvers/defining-types/
5
+ description: How to define types for your client state schema
6
+ ---
7
+
8
+ import {FbInternalOnly, fbContent} from 'docusaurus-plugin-internaldocs-fb/internal';
9
+ import Tabs from '@theme/Tabs';
10
+ import TabItem from '@theme/TabItem';
11
+
12
+
13
+ You can think of client state resolvers as defining a GraphQL server that runs in the client. Just like with a server-defined GraphQL server you will need to define the _types_ that exist in your schema as well as the _fields_ on those types. Just like a GraphQL server, fields are defined as functions that compute the GraphQL value from the parent object. In Relay Resolvers we call this parent JavaScript object the "model" of the type.
14
+
15
+ :::info
16
+ Each client state GraphQL type is backed by a JavaScript object type which these docs will refer to as its "model type". Resolvers "on" this type will be passed an instance of this type as their first argument.
17
+ :::
18
+
19
+ Resolver types are defined using the `@relayType` tag followed by the name of the type you are defining. By default Relay assumes your client types are “strong”, meaning each instance has an ID which is unique within the type. This property allows Relay to apply a number of optimizations, such as memoizing resolver computation.
20
+
21
+ ### Defining a “strong” type
22
+ Strong types are defined by a docblock followed by an exported function whose name matches the type's name, and which accepts an ID as its only argument and returns an instance of the type’s model. Resolvers that define edges to this type will simply need to return the ID of the object, rather than deriving the model themselves.
23
+
24
+ <Tabs
25
+ groupId="resolver"
26
+ defaultValue="Docblock"
27
+ values={fbContent({
28
+ internal: [
29
+ {label: 'Docblock', value: 'Docblock'},
30
+ {label: 'Flow', value: 'Flow'},
31
+ ],
32
+ external: [
33
+ {label: 'Docblock', value: 'Docblock'},
34
+ ]
35
+ })}>
36
+ <TabItem value="Docblock">
37
+
38
+
39
+ ```tsx
40
+ /**
41
+ * @relayType User
42
+ */
43
+ export function User(id: DataID): UserModel {
44
+ return UserService.getById(id);
45
+ }
46
+ ```
47
+
48
+ </TabItem>
49
+
50
+ <TabItem value="Flow">
51
+
52
+ ```tsx
53
+ /**
54
+ * @relayType
55
+ */
56
+ export function User(id: DataID): UserModel {
57
+ return UserService.getById(id);
58
+ }
59
+ ```
60
+ </TabItem>
61
+ </Tabs>
62
+
63
+ :::tip
64
+ Elsewhere in the docs this function is referred to as the “model resolver” for the type.
65
+ :::
66
+
67
+ Generally objects in your client data store will be able to change over time. To support this Relay Resolvers support resolvers that subscribe to the underlying data source. To learn about this, see the page on [Live Fields](./live-fields.mdx).
68
+
69
+ ### Defining a “weak” type
70
+
71
+ If your type does not have a unique identifier, you can define it as “weak” by adding the `@weak` docblock tag. Weak types are defined by a docblock followed by an exported type definition matching the types name. Resolvers that define edges to weak types will need to return a fully populated model object matching this type.
72
+
73
+ <Tabs
74
+ groupId="resolver"
75
+ defaultValue="Docblock"
76
+ values={fbContent({
77
+ internal: [
78
+ {label: 'Docblock', value: 'Docblock'},
79
+ {label: 'Flow', value: 'Flow'},
80
+ ],
81
+ external: [
82
+ {label: 'Docblock', value: 'Docblock'},
83
+ ]
84
+ })}>
85
+ <TabItem value="Docblock">
86
+
87
+ ```tsx
88
+ /**
89
+ * @relayType ProfilePicture
90
+ * @weak
91
+ */
92
+ export type ProfilePicture = { url: string, height: number, width: number };
93
+ ```
94
+ </TabItem>
95
+
96
+ <TabItem value="Flow">
97
+
98
+ ```tsx
99
+ /**
100
+ * @relayType
101
+ */
102
+ export type ProfilePicture = { url: string, height: number, width: number };
103
+ ```
104
+ </TabItem>
105
+
106
+ </Tabs>
107
+
108
+ :::tip
109
+ Generally weak types are used for creating a namespace for a set of fields that ultimately "belong" to a parent object.
110
+ :::
111
+
112
+ ### Implementing Abstract Types
113
+
114
+ Relay Resolver types can implement [abstract types](https://relay.dev/docs/glossary/#abstract-type) defined in the graphql schema. Note, these abstract types can
115
+ be defined on your GraphQL server schema OR a [client side schema extension](https://relay.dev/docs/next/guides/client-schema-extensions/).
116
+
117
+ For example, given the following interface:
118
+
119
+ ```graphql
120
+ # IUser.graphql
121
+ interface IUser {
122
+ name: String
123
+ }
124
+ ```
125
+
126
+ You could define two (or more) concrete resolver types that implement the IUser interface by adding annotations in the docblock (the same applies for unions).
127
+ <FbInternalOnly>
128
+
129
+ Note, support for abstract types is not available for relay resolvers in Flow syntax (yet).
130
+
131
+ </FbInternalOnly>
132
+
133
+ <Tabs
134
+ groupId="resolver"
135
+ defaultValue="Docblock"
136
+ values={fbContent({
137
+ internal: [
138
+ {label: 'Docblock', value: 'Docblock'},
139
+ ],
140
+ external: [
141
+ {label: 'Docblock', value: 'Docblock'},
142
+ ]
143
+ })}>
144
+ <TabItem value="Docblock">
145
+
146
+
147
+ ```tsx
148
+ /**
149
+ * @relayType BasicUser implements IUser
150
+ */
151
+ export function BasicUser(id: DataID): BasicUserModel {
152
+ return { ...BasicUserService.getById(id), name: 'BasicUser1'};
153
+ }
154
+
155
+ /**
156
+ * @relayType SpecialUser implements IUser
157
+ */
158
+ export function SpecialUser(id: DataID): SpecialUserModel {
159
+ return { ...SpecialUserService.getById(id), name: 'SpecialUser1'};
160
+ }
161
+ ```
162
+
163
+ </TabItem>
164
+ </Tabs>
@@ -0,0 +1,27 @@
1
+ ---
2
+ id: deprecated
3
+ title: "Deprecated"
4
+ slug: /guides/relay-resolvers/deprecated/
5
+ description: Marking fields in your client state schema as @deprecated
6
+ ---
7
+
8
+ GraphQL allows you to mark fields as `@deprecated` and provide an optional human-readable reason. Relay Resolvers bring this same convention to your client data. By marking fields in your client state schema as deprecated they will receive the same treatment as deprecated fields in your server GraphQL schema.
9
+
10
+ Deprecated fields are surfaced as such in Relay's [VSCode extension](https://relay.dev/docs/editor-support/) in autocomplete and on hover. Additionally, they will be rendered as greyed out and ~~struck through~~ in the editor.
11
+
12
+ :::info
13
+ GraphQL deprecation reasons are expected to be written in markdown. Relay Resolvers will render these descriptions as markdown in the VSCode extension.
14
+ :::
15
+
16
+ You can mark a field as deprecated by adding the `@deprecated` docblock tag followed by optional text to specify the reason.
17
+
18
+ ```tsx
19
+ /**
20
+ * @relayField Author.fullName: String
21
+ *
22
+ * @deprecated Google "Falsehoods Programmers Believe About Names"
23
+ */
24
+ export function fullName(author: AuthorModel): string {
25
+ return `${author.firstName} ${author.lastName}`;
26
+ }
27
+ ```
@@ -0,0 +1,127 @@
1
+ ---
2
+ id: derived-fields
3
+ title: "Derived Fields"
4
+ slug: /guides/relay-resolvers/derived-fields/
5
+ description: Defining field which are a pure function of other fields
6
+ ---
7
+ import {FbInternalOnly, fbContent} from 'docusaurus-plugin-internaldocs-fb/internal';
8
+ import Tabs from '@theme/Tabs';
9
+ import TabItem from '@theme/TabItem';
10
+
11
+ In addition to modeling client state, Relay Resolvers also allow you to define fields which are a pure function of other fields. These fields are called derived fields and can be defined on any type no matter if it's defined on the server or client.
12
+
13
+ For globally relevant data, resolvers have a few advantages of alternative solutions like [React Hooks](https://react.dev/learn/reusing-logic-with-custom-hooks):
14
+
15
+ * **Global memoization** - Relay Resolvers automatically memoize derived fields. Unlike hooks, this cache is shared by all components in your application, so if two sibling components both read the same field, the computation will only be performed once.
16
+ * **Efficient updates** - If your derived resolver recomputes but derives the same value, Relay can avoid rerendering components that read the field.
17
+ * **Composable** - Derived fields can be composed with other derived fields, allowing you to build up complex, but explicit computation graphs.
18
+ * **Discoverable** - Values in the graph are discoverable via the GraphQL schema and thus are more likely to be discovered and reused instead of reinvented.
19
+ * **Documented** - GraphQL's field documentation and structured deprecation model make it easy to understand the purpose of a field and its intended use.
20
+
21
+ ## Defining a Derived Resolver
22
+
23
+ Derived resolvers look like any other resolver except that they read GraphQL data instead of being computed from a parent model type. Derived resolvers read GraphQL data by defining a "root fragment" which is a GraphQL fragment defined on the parent type of the field.
24
+
25
+ The root fragment is defined using the `@rootFragment` docblock tag followed by the name of the fragment. This tells Relay to pass the resolver function a fragment key for that fragment. The fragment data may then be read using `readFragment` imported from `relay-runtime`.
26
+
27
+ <Tabs
28
+ groupId="resolver"
29
+ defaultValue="Docblock"
30
+ values={fbContent({
31
+ internal: [
32
+ {label: 'Docblock', value: 'Docblock'},
33
+ {label: 'Flow', value: 'Flow'},
34
+ ],
35
+ external: [
36
+ {label: 'Docblock', value: 'Docblock'},
37
+ ]
38
+ })}>
39
+ <TabItem value="Docblock">
40
+
41
+ ```tsx
42
+ import {readFragment} from 'relay-runtime';
43
+
44
+ /**
45
+ * @relayField User.fullName: String
46
+ * @rootFragment UserFullNameFragment
47
+ */
48
+ export function fullName(key: UserFullNameFragment$key): string {
49
+ const user = readFragment(graphql`
50
+ fragment UserFullNameFragment on User {
51
+ firstName
52
+ lastName
53
+ }
54
+ `, key);
55
+ return `${user.firstName} ${user.lastName}`;
56
+ }
57
+ ```
58
+ </TabItem>
59
+
60
+ <TabItem value="Flow">
61
+ <FbInternalOnly>
62
+
63
+ ```tsx
64
+ import {readFragment} from 'relay-runtime';
65
+
66
+ /**
67
+ * @relayField
68
+ */
69
+ export function fullName(key: UserFullNameFragment$key): string {
70
+ const user = readFragment(graphql`
71
+ fragment UserFullNameFragment on User {
72
+ firstName
73
+ lastName
74
+ }
75
+ `, key);
76
+ return `${user.firstName} ${user.lastName}`;
77
+ }
78
+ ```
79
+
80
+ </FbInternalOnly>
81
+ </TabItem>
82
+ </Tabs>
83
+
84
+ :::info
85
+ Relay will track all the values read from the fragment and automatically recompute the resolver when any of those values change.
86
+ :::
87
+
88
+ ## Composition
89
+
90
+ One powerful feature of derived resolvers is that they can read other Relay Resolver fields. This means you can define a derived resolver that combines server data, client data and even other derived resolvers. This allows you to build up complex, but explicit, computation graphs.
91
+
92
+ ```tsx
93
+ /**
94
+ * @relayField CheckoutItem.isValid: Boolean
95
+ * @rootFragment CheckoutItemFragment
96
+ */
97
+ export function isValid(key): boolean {
98
+ const item = readFragment(graphql`
99
+ fragment CheckoutItemFragment on CheckoutItem {
100
+ product {
101
+ price
102
+ }
103
+ quantity
104
+ }
105
+ `, key);
106
+ return item.product.price * item.quantity > 0;
107
+ }
108
+
109
+ /**
110
+ * @relayField ShoppingCart.canCheckout: Boolean
111
+ * @rootFragment ShoppingCartFragment
112
+ */
113
+ export function canCheckout(key): boolean {
114
+ const cart = readFragment(graphql`
115
+ fragment ShoppingCartFragment on ShoppingCart {
116
+ items {
117
+ isValid
118
+ }
119
+ }
120
+ `, key);
121
+ return cart.items.every(item => item.isValid);
122
+ }
123
+ ```
124
+
125
+ ## Passing Arguments to your @rootFragment
126
+
127
+ If a field in a derived resolver's root fragment requires arguments, you can pass them by adding an `@arguments` tag to the docblock tag. The `@argument` tag takes the name of the argument and the type of the argument. The argument type must be a valid GraphQL input type. For more information about arguments and Resolvers see [Field Arguments](./field-arguments.mdx).
@@ -0,0 +1,44 @@
1
+ ---
2
+ id: descriptions
3
+ title: "Descriptions"
4
+ slug: /guides/relay-resolvers/descriptions/
5
+ description: Adding human readable descriptions to your resolver schema
6
+ ---
7
+
8
+ One killer feature of GraphQL is that makes the data in your schema discoverable. Relay Resolvers bring this structure to your client data. By adding descriptions to your resolvers you can make your client state schema self-documenting as well.
9
+
10
+ Descriptions are surfaced by Relay's [VSCode extension](https://relay.dev/docs/editor-support/) in autocomplete and on hover.
11
+
12
+ You can add a description to a type by adding free text to the docblock tag:
13
+
14
+ :::info
15
+ GraphQL descriptions are expected to be written in markdown. Relay Resolvers will render these descriptions as markdown in the VSCode extension.
16
+ :::
17
+
18
+ ## Types
19
+
20
+ ```tsx
21
+ /**
22
+ * @relayType Author
23
+ *
24
+ * An author in our **amazing** CMS. Authors can
25
+ * write posts but not necessarily change their permissions.
26
+ */
27
+ export function Author(id: DataID): AuthorModel {
28
+ return AuthorService.getById(id);
29
+ }
30
+ ```
31
+
32
+ ## Fields
33
+
34
+ ```tsx
35
+ /**
36
+ * @relayField Author.fullName: String
37
+ *
38
+ * The author's first and last name. Does not include
39
+ * any [honorifics](https://en.wikipedia.org/wiki/Honorific).
40
+ */
41
+ export function fullName(author: AuthorModel): string {
42
+ return `${author.firstName} ${author.lastName}`;
43
+ }
44
+ ```
@@ -0,0 +1,41 @@
1
+ ---
2
+ id: enabling
3
+ title: "Enabling Relay Resolvers"
4
+ slug: /guides/relay-resolvers/enabling-resolvers
5
+ description: Enabling experimental Relay Resolvers
6
+ ---
7
+
8
+ ## Runtime
9
+
10
+ When using Relay Resolvers, we recommend configuring a `fieldLogger` in your Relay Environment in order to track errors which have been thrown within Relay resolver functions.
11
+
12
+ ```ts
13
+ import { Environment, RecordSource, RelayFeatureFlags } from "relay-runtime";
14
+ import RelayModernStore from "relay-runtime/lib/store/RelayModernStore";
15
+
16
+ // It is recommended to log errors thrown by Resolvers
17
+ function fieldLogger(event) {
18
+ if(event.kind === "relay_resolver.error") {
19
+ // Log this somewhere!
20
+ console.warn(`Resolver error encountered in ${event.owner}.${event.fieldPath}`)
21
+ console.warn(event.error)
22
+ }
23
+ }
24
+
25
+ const environment = new Environment({
26
+ network: Network.create(/* your fetch function here */),
27
+ store: new RelayModernStore(new RecordSource()),
28
+ relayFieldLogger: fieldLogger
29
+ });
30
+
31
+ // ... create your Relay context with your environment
32
+ ```
33
+
34
+ <FbInternalOnly>
35
+
36
+ ## Enable new Flow based RelayResolver syntax
37
+ To opt-in the new syntax in a file, add `//relay:enable-new-relay-resolver` to the file
38
+
39
+ To convert files to the new syntax, run codemode: `flow-runner codemod relay/migrateResolver <path>`. The codemod doesn't support all cases, so you might need to modify some files manually after it runs.
40
+
41
+ </FbInternalOnly>
@@ -0,0 +1,64 @@
1
+ ---
2
+ id: errors
3
+ title: "Error Handling"
4
+ slug: /guides/relay-resolvers/errors/
5
+ description: How Relay handles errors thrown by resolvers
6
+ ---
7
+
8
+ Just like GraphQL servers, Relay Resolvers support field-level error handling. If an individual resolver throws an error, when that field is read, Relay will log that error to the environment's user-provided `relayFieldLogger` logger, and the field will become null.
9
+
10
+ This provides important symmetry with GraphQL servers. Resolvers are designed to enable a smooth migration path to allow teams to start with fields defined client-side using Resolvers and then eventually migrate them to a server.
11
+
12
+ If a resolver throws an error, Relay will log the error to the user-provided error logger, and will return null for the field which the resolver defines. To enable this behavior at runtime, the Relay compiler will not allow resolver fields to be typed as non-nullable.
13
+
14
+ The object passed to the `relayFieldLogger` will have the following shape:
15
+
16
+ ```ts
17
+ type ResolverErrorEvent = {
18
+ kind: 'relay_resolver.error',
19
+ // The name of the fragment/query in which the field was read
20
+ owner: string,
21
+ // The path from the owner root to the field which threw the error
22
+ fieldPath: string,
23
+ // The error thrown by the resolver
24
+ error: Error,
25
+ }
26
+ ```
27
+
28
+ An example logger might look like:
29
+
30
+ ```ts
31
+ function fieldLogger(event) {
32
+ if(event.kind === "relay_resolver.error") {
33
+ // Log this somewhere!
34
+ console.warn(`Resolver error encountered in ${event.owner}.${event.fieldPath}`)
35
+ console.warn(event.error)
36
+ }
37
+ }
38
+
39
+ const environment = new Environment({
40
+ network: Network.create(/* your fetch function here */),
41
+ store: new RelayModernStore(new RecordSource()),
42
+ relayFieldLogger: fieldLogger
43
+ });
44
+ ```
45
+
46
+ :::note
47
+ [Live Resolvers](./live-fields.mdx) can potentially throw errors when they are first evaluated or when their `.read()` method is called. Both types of errors will be handled identically by Relay.
48
+ :::
49
+
50
+ ## Support for Semantic Nullability
51
+
52
+ Relay resolver fields can be specified as [semantically non-null](../../semantic-nullability/) just like server schema fields. Developers can add the directive `@semanticNonNull` in the docblock of a relay resolver in order to indicate that the field is non-nullable in the semantic sense, but that the client should still be prepared to handle errors.
53
+
54
+ For example:
55
+ ```ts
56
+ /**
57
+ * @relayField RelayExample.semantic_non_null_field: String @semanticNonNull
58
+ */
59
+ export function semantic_non_null_field(
60
+ model: RelayExampleModel,
61
+ ): string {
62
+ return model.someField ?? 'field was null, this is the default';
63
+ }
64
+ ```
@@ -0,0 +1,63 @@
1
+ ---
2
+ id: field-arguments
3
+ title: "Field Arguments"
4
+ slug: /guides/relay-resolvers/field-arguments/
5
+ description: Defining field arguments for resolver fields
6
+ ---
7
+
8
+ ## Runtime Arguments
9
+
10
+ If your resolver needs access to argument data at runtime, you can simply define arguments in the field definition of your resolver's docblock, and then read the argument as a property on the second argument to your resolver function.
11
+
12
+ ```tsx
13
+ /**
14
+ * @relayField User.greet(salutation: String!): String
15
+ */
16
+ export function greet(user: UserModel, args: { salutation: string }): string {
17
+ return `${args.salutation}, ${user.name}!`;
18
+ }
19
+ ```
20
+
21
+ Consuming this field will require passing the argument to the field in your GraphQL query:
22
+
23
+ ```graphql
24
+ query MyQuery($salutation: String!) {
25
+ me {
26
+ greet(salutation: $salutation)
27
+ }
28
+ }
29
+ ```
30
+
31
+ This, in turn will require passing the argument when you fetch the query.
32
+
33
+ ## Passing Arguments to your @rootFragment
34
+
35
+ If you are defining a [derived resolver](./derived-fields.mdx) and one of the fields in its root fragment requires arguments, you must define an explicit fragment argument using [@argumentDefinitions](../../api-reference/graphql/graphql-directives.mdx#argumentdefinitions) in your fragment definition. Your resolver field will then expect this argument to be passed as a field argument.
36
+
37
+ ```tsx
38
+ /**
39
+ * @relayField User.fancyGreeting: String
40
+ * @rootFragment UserFancyGreetingFragment
41
+ */
42
+ export function fancyGreeting(key: UserFancyGreetingFragment$key): string {
43
+ const user = readFragment(graphql`
44
+ fragment UserFancyGreetingFragment on User @argumentDefinitions(
45
+ salutation: {type: "String"},
46
+ ) {
47
+ name
48
+ greet(salutation: $salutation)
49
+ }
50
+ `, key);
51
+ return `${user.name} says ${user.greet}`;
52
+ }
53
+ ```
54
+
55
+ Consuming this field will require passing the argument to the field in your GraphQL query:
56
+
57
+ ```graphql
58
+ query MyQuery($salutation: String!) {
59
+ me {
60
+ fancyGreeting(salutation: $salutation)
61
+ }
62
+ }
63
+ ```
@@ -0,0 +1,62 @@
1
+ ---
2
+ id: introduction
3
+ title: "Introduction to Relay Resolvers"
4
+ slug: /guides/relay-resolvers/introduction
5
+ description: An introduction to Relay Resolvers
6
+ ---
7
+
8
+ Relay Resolvers are a Relay feature which allow you to augment Relay’s GraphQL graph with values that are known only on the client. This allows you to schematize client state in the same way that you model server state, and to use Relay’s familiar data-fetching APIs to access that state. Client state can include both data from client-side data stores as well as derived data that is computed from other values in the graph.
9
+
10
+ By modeling derived and client state in the graph, Relay can present a unified data access API for product developers. All globally relevant data that a product engineer wants to access can be discovered and efficiently obtained from the same structured GraphQL schema. Additionally resolvers provide a number of runtime benefits:
11
+
12
+ - Global memoization with garbage collection
13
+ - Efficient reactive recomputation of resolvers
14
+ - Efficient UI updates when data changes
15
+
16
+ You can think of resolvers as additional schema types and fields which are defined in your client code and are stitched into your server's schema. Just like you define resolver methods/functions which model your fields on the server, Relay Resolvers are defined using resolver functions.
17
+
18
+ ## Use Cases for Relay Resolvers
19
+
20
+ Relay Resolvers are useful for modeling a number of different kinds of data. Here are some examples of types of data that can be schematized using Relay Resolvers and made available to product code:
21
+
22
+ * **User-Created Data** - You can model complex form state, or other data that should outlive a specific component tree
23
+ * **Client-Side Database** - Persistent data stores like IndexDB, localStorage, or SQLite
24
+ * **Third-Party APIs** - Data that is fetched from a third-party API directly by the client, for example search results from a third-party search provider
25
+ * **Encrypted Data** - End-to-end encrypted data that is opaque on the server and thus cannot be modeled in the server schema
26
+ * **Legacy Data Stores** - During the adoption of Relay and GraphQL, data from pre-existing data layers, like Redux, can be exposed in the graph to ensure migrated and un-migrated portions of your app always remain in sync
27
+
28
+ ## Defining a Resolver
29
+
30
+ Resolvers are defined using exported functions that are annotated with a special [`@relayType` or `@relayField` docblock](../../api-reference/relay-resolvers/docblock-format.mdx). These docblocks are visible to the Relay compiler, and allow the compiler to build up your client schema and automatically import your function in Relay's generated artifacts. Resolver functions may be defined in any file in your Relay project, though you may wish to define some convention for where they live within your codebase.
31
+
32
+ The simplest resolver augments an existing type and does not have any inputs:
33
+
34
+ ```tsx
35
+ /**
36
+ * @relayField Query.greeting: String
37
+ */
38
+ export function greeting(): string {
39
+ return "Hello World";
40
+ }
41
+ ```
42
+
43
+ Consuming resolvers is identical to consuming a server field. Product code doesn't need to know which kind of field it is reading.
44
+
45
+ ```tsx
46
+ import {useLazyLoadQuery, graphql} from 'react-relay';
47
+ import {useClientQuery, graphql} from 'react-relay';
48
+
49
+ function Greeting() {
50
+ const data = useClientQuery(graphql`
51
+ query GreetingQuery {
52
+ greeting
53
+ }`, {});
54
+ return <p>{data.greeting}</p>;
55
+ }
56
+ ```
57
+
58
+ :::note
59
+ If your query contains only client-defined fields, you will need to use a a different query API to fetch data. Note how this example uses `useClientQuery` instead of `useLazyLoadQuery` or `usePreloadedQuery`. If your query also contains server data, you can use the standard `useLazyLoadQuery` or `usePreloadedQuery` APIs.
60
+
61
+ We intend to remove this requirement in future versions of Relay.
62
+ :::
@@ -0,0 +1,30 @@
1
+ ---
2
+ id: limitations
3
+ title: "Limitations"
4
+ slug: /guides/relay-resolvers/limitations/
5
+ description: Limitations of Relay Resolvers
6
+ ---
7
+
8
+ Relay Resolvers do have some limitations. Here we will collect a list of known limitations and provide alternatives where possible.
9
+
10
+ ## No info argument
11
+
12
+ In a full GraphQL implementation, resolvers would have access to an `info` argument. This argument is not available in Relay Resolvers today.
13
+
14
+ ## Not all GraphQL constructs are supported
15
+
16
+ Today Relay Resolvers only support a subset of GraphQL constructs. For example, it's not currently possible to define input types, enums or interfaces using Relay Resolvers.
17
+
18
+ ## No support for mutations
19
+
20
+ Today Relay Resolvers only support the read path. Defining mutation fields is not yet supported. We are working to understand what it means to perform a mutation against a reactive schema, and hope to support them in the future.
21
+
22
+ ## Resolvers are always evaluated lazily
23
+
24
+ Today Relay Resolvers are always evaluated lazily on a per-fragment basis. This has the advantage that if a resolver is not read, it will never be evaluated. However, it can lead to issues with waterfalls if your client schema ends up making async requests to fetch data as its read. We are actively exploring other execution strategies for Relay Resolvers, such as evaluating all fields in a query at request time, but expect the way resolvers are defined to remain stable.
25
+
26
+ ## Verbose/awkward docblock syntax
27
+
28
+ Today defining a resolver requires defining a function with a docblock which uses special syntax and duplicates information already specified in the function's name and types. Further, in order to enforce that these values match up, Relay emits type assertions in its generated types. While these assertions do ensure safety, they are an awkward developer experience.
29
+
30
+ To address these issues we are exploring a more streamlined approach where names and types can be inferred from your Flow or TypeScript code similar to the approach taken by [Grats](https://grats.capt.dev/). This syntax may become available in future versions of Relay.