relay-runtime 20.1.1 → 21.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/experimental.d.ts +34 -0
- package/experimental.js +1 -1
- package/experimental.js.flow +11 -11
- package/handlers/RelayDefaultHandlerProvider.d.ts +12 -0
- package/handlers/connection/ConnectionHandler.d.ts +51 -0
- package/handlers/connection/ConnectionHandler.js.flow +5 -5
- package/handlers/connection/ConnectionInterface.d.ts +40 -0
- package/handlers/connection/ConnectionInterface.js.flow +1 -1
- package/handlers/connection/MutationHandlers.d.ts +17 -0
- package/index.d.ts +274 -0
- package/index.js +1 -1
- package/index.js.flow +125 -62
- package/lib/experimental.js +3 -3
- package/lib/index.js +105 -57
- package/lib/multi-actor-environment/ActorIdentifier.js +2 -2
- package/lib/multi-actor-environment/MultiActorEnvironment.js +3 -1
- package/lib/mutations/commitMutation.js +8 -8
- package/lib/mutations/validateMutation.js +4 -4
- package/lib/query/GraphQLTag.js +3 -3
- package/lib/query/fetchQuery.js +15 -3
- package/lib/store/DataChecker.js +38 -4
- package/lib/store/NormalizationEngine.js +373 -0
- package/lib/store/OperationExecutor.js +172 -113
- package/lib/store/RelayConcreteVariables.js +1 -1
- package/lib/store/RelayErrorTrie.js +2 -2
- package/lib/store/RelayExperimentalGraphResponseTransform.js +8 -8
- package/lib/store/RelayModernEnvironment.js +26 -19
- package/lib/store/RelayModernRecord.js +18 -8
- package/lib/store/RelayModernSelector.js +9 -9
- package/lib/store/RelayModernStore.js +152 -43
- package/lib/store/RelayPublishQueue.js +1 -1
- package/lib/store/RelayReader.js +76 -38
- package/lib/store/RelayRecordSource.js +6 -0
- package/lib/store/RelayReferenceMarker.js +2 -1
- package/lib/store/RelayResponseNormalizer.js +88 -55
- package/lib/store/RelayStoreSubscriptions.js +34 -10
- package/lib/store/RelayStoreUtils.js +8 -1
- package/lib/store/ResolverFragments.js +2 -2
- package/lib/store/live-resolvers/LiveResolverCache.js +25 -9
- package/lib/store/observeFragmentExperimental.js +17 -1
- package/lib/store/observeQueryExperimental.js +2 -2
- package/lib/subscription/requestSubscription.js +3 -3
- package/lib/util/RelayError.js +3 -0
- package/lib/util/RelayFeatureFlags.js +6 -2
- package/lib/util/RelayReplaySubject.js +4 -4
- package/lib/util/handlePotentialSnapshotErrors.js +2 -2
- package/lib/util/stableCopy.js +2 -2
- package/llm-docs/api-reference/entrypoint-apis/entrypoint-container.mdx +38 -0
- package/llm-docs/api-reference/entrypoint-apis/load-entrypoint.mdx +77 -0
- package/llm-docs/api-reference/entrypoint-apis/use-entrypoint-loader.mdx +99 -0
- package/llm-docs/api-reference/graphql/graphql-directives.mdx +378 -0
- package/llm-docs/api-reference/hooks/_use-lazy-load-query-extra.mdx +16 -0
- package/llm-docs/api-reference/hooks/load-query.mdx +84 -0
- package/llm-docs/api-reference/hooks/relay-environment-provider.mdx +78 -0
- package/llm-docs/api-reference/hooks/use-client-query.mdx +65 -0
- package/llm-docs/api-reference/hooks/use-fragment.mdx +69 -0
- package/llm-docs/api-reference/hooks/use-lazy-load-query.mdx +62 -0
- package/llm-docs/api-reference/hooks/use-mutation.mdx +94 -0
- package/llm-docs/api-reference/hooks/use-pagination-fragment.mdx +166 -0
- package/llm-docs/api-reference/hooks/use-prefetchable-forward-pagination-fragment.mdx +134 -0
- package/llm-docs/api-reference/hooks/use-preloaded-query.mdx +84 -0
- package/llm-docs/api-reference/hooks/use-query-loader.mdx +95 -0
- package/llm-docs/api-reference/hooks/use-refetchable-fragment.mdx +122 -0
- package/llm-docs/api-reference/hooks/use-relay-environment.mdx +37 -0
- package/llm-docs/api-reference/hooks/use-subscription.mdx +66 -0
- package/llm-docs/api-reference/relay-resolvers/docblock-format.mdx +321 -0
- package/llm-docs/api-reference/relay-resolvers/runtime-functions.mdx +94 -0
- package/llm-docs/api-reference/relay-runtime/commit-mutation.mdx +65 -0
- package/llm-docs/api-reference/relay-runtime/fetch-query.mdx +118 -0
- package/llm-docs/api-reference/relay-runtime/field-logger.mdx +170 -0
- package/llm-docs/api-reference/relay-runtime/observe-fragment.mdx +92 -0
- package/llm-docs/api-reference/relay-runtime/relay-environment.mdx +53 -0
- package/llm-docs/api-reference/relay-runtime/request-subscription.mdx +54 -0
- package/llm-docs/api-reference/relay-runtime/runtime-configuration.mdx +52 -0
- package/llm-docs/api-reference/relay-runtime/store.mdx +734 -0
- package/llm-docs/api-reference/relay-runtime/wait-for-fragment-data.mdx +89 -0
- package/llm-docs/api-reference/types/CacheConfig.mdx +8 -0
- package/llm-docs/api-reference/types/Disposable.mdx +4 -0
- package/llm-docs/api-reference/types/GraphQLSubscriptionConfig.mdx +17 -0
- package/llm-docs/api-reference/types/MutationConfig.mdx +31 -0
- package/llm-docs/api-reference/types/SelectorStoreUpdater.mdx +6 -0
- package/llm-docs/api-reference/types/UploadableMap.mdx +3 -0
- package/llm-docs/community/learning-resources.mdx +64 -0
- package/llm-docs/debugging/declarative-mutation-directives.mdx +34 -0
- package/llm-docs/debugging/disallowed-id-types-error.mdx +43 -0
- package/llm-docs/debugging/inconsistent-typename-error.mdx +47 -0
- package/llm-docs/debugging/relay-devtools.mdx +73 -0
- package/llm-docs/debugging/why-null.mdx +116 -0
- package/llm-docs/editor-support.mdx +55 -0
- package/llm-docs/error-reference/unknown-field.mdx +36 -0
- package/llm-docs/getting-started/babel-plugin.mdx +31 -0
- package/llm-docs/getting-started/compiler-config.mdx +25 -0
- package/llm-docs/getting-started/compiler.mdx +98 -0
- package/llm-docs/getting-started/lint-rules.mdx +87 -0
- package/llm-docs/getting-started/production.mdx +30 -0
- package/llm-docs/getting-started/quick-start.mdx +216 -0
- package/llm-docs/glossary/glossary.mdx +1040 -0
- package/llm-docs/guided-tour/list-data/advanced-pagination.mdx +157 -0
- package/llm-docs/guided-tour/list-data/connections.mdx +81 -0
- package/llm-docs/guided-tour/list-data/pagination.mdx +193 -0
- package/llm-docs/guided-tour/list-data/rendering-connections.mdx +112 -0
- package/llm-docs/guided-tour/list-data/streaming-pagination.mdx +87 -0
- package/llm-docs/guided-tour/managing-data-outside-react/retaining-queries.mdx +51 -0
- package/llm-docs/guided-tour/refetching/refetching-queries-with-different-data.mdx +337 -0
- package/llm-docs/guided-tour/refetching/refreshing-queries.mdx +350 -0
- package/llm-docs/guided-tour/rendering/environment.mdx +59 -0
- package/llm-docs/guided-tour/rendering/error-states.mdx +295 -0
- package/llm-docs/guided-tour/rendering/fragments.mdx +354 -0
- package/llm-docs/guided-tour/rendering/loading-states.mdx +245 -0
- package/llm-docs/guided-tour/rendering/queries.mdx +261 -0
- package/llm-docs/guided-tour/rendering/variables.mdx +233 -0
- package/llm-docs/guided-tour/reusing-cached-data/fetch-policies.mdx +56 -0
- package/llm-docs/guided-tour/reusing-cached-data/filling-in-missing-data.mdx +102 -0
- package/llm-docs/guided-tour/reusing-cached-data/introduction.mdx +22 -0
- package/llm-docs/guided-tour/reusing-cached-data/presence-of-data.mdx +93 -0
- package/llm-docs/guided-tour/reusing-cached-data/rendering-partially-cached-data.mdx +175 -0
- package/llm-docs/guided-tour/reusing-cached-data/staleness-of-data.mdx +116 -0
- package/llm-docs/guided-tour/updating-data/client-only-data.mdx +115 -0
- package/llm-docs/guided-tour/updating-data/graphql-mutations.mdx +334 -0
- package/llm-docs/guided-tour/updating-data/graphql-subscriptions.mdx +279 -0
- package/llm-docs/guided-tour/updating-data/imperatively-modifying-linked-fields.mdx +511 -0
- package/llm-docs/guided-tour/updating-data/imperatively-modifying-store-data-legacy.mdx +142 -0
- package/llm-docs/guided-tour/updating-data/imperatively-modifying-store-data.mdx +275 -0
- package/llm-docs/guided-tour/updating-data/introduction.mdx +25 -0
- package/llm-docs/guided-tour/updating-data/local-data-updates.mdx +71 -0
- package/llm-docs/guided-tour/updating-data/typesafe-updaters-faq.mdx +83 -0
- package/llm-docs/guided-tour/updating-data/updating-connections.mdx +592 -0
- package/llm-docs/guides/alias-directive.mdx +160 -0
- package/llm-docs/guides/catch-directive.mdx +167 -0
- package/llm-docs/guides/client-schema-extensions.mdx +208 -0
- package/llm-docs/guides/codemods.mdx +79 -0
- package/llm-docs/guides/data-driven-dependencies/client-3d.mdx +255 -0
- package/llm-docs/guides/data-driven-dependencies/configuration.mdx +127 -0
- package/llm-docs/guides/data-driven-dependencies/introduction.mdx +39 -0
- package/llm-docs/guides/data-driven-dependencies/server-3d.mdx +664 -0
- package/llm-docs/guides/document-comparison.mdx +106 -0
- package/llm-docs/guides/graphql-server-specification.mdx +453 -0
- package/llm-docs/guides/network-layer.mdx +69 -0
- package/llm-docs/guides/persisted-queries.mdx +328 -0
- package/llm-docs/guides/relay-resolvers/context.mdx +99 -0
- package/llm-docs/guides/relay-resolvers/defining-fields.mdx +151 -0
- package/llm-docs/guides/relay-resolvers/defining-types.mdx +164 -0
- package/llm-docs/guides/relay-resolvers/deprecated.mdx +27 -0
- package/llm-docs/guides/relay-resolvers/derived-fields.mdx +127 -0
- package/llm-docs/guides/relay-resolvers/descriptions.mdx +44 -0
- package/llm-docs/guides/relay-resolvers/enabling.mdx +41 -0
- package/llm-docs/guides/relay-resolvers/errors.mdx +64 -0
- package/llm-docs/guides/relay-resolvers/field-arguments.mdx +63 -0
- package/llm-docs/guides/relay-resolvers/introduction.mdx +62 -0
- package/llm-docs/guides/relay-resolvers/limitations.mdx +30 -0
- package/llm-docs/guides/relay-resolvers/live-fields.mdx +164 -0
- package/llm-docs/guides/relay-resolvers/return-types.mdx +161 -0
- package/llm-docs/guides/relay-resolvers/suspense.mdx +41 -0
- package/llm-docs/guides/required-directive.mdx +240 -0
- package/llm-docs/guides/semantic-nullability.mdx +93 -0
- package/llm-docs/guides/testing-relay-components.mdx +642 -0
- package/llm-docs/guides/testing-relay-with-preloaded-queries.mdx +160 -0
- package/llm-docs/guides/throw-on-field-error-directive.mdx +58 -0
- package/llm-docs/guides/type-emission.mdx +414 -0
- package/llm-docs/home.mdx +32 -0
- package/llm-docs/principles-and-architecture/architecture-overview.mdx +24 -0
- package/llm-docs/principles-and-architecture/compiler-architecture.mdx +106 -0
- package/llm-docs/principles-and-architecture/runtime-architecture.mdx +249 -0
- package/llm-docs/principles-and-architecture/thinking-in-graphql.mdx +309 -0
- package/llm-docs/principles-and-architecture/thinking-in-relay.mdx +104 -0
- package/llm-docs/principles-and-architecture/videos.mdx +50 -0
- package/llm-docs/tutorial/arrays-lists.mdx +126 -0
- package/llm-docs/tutorial/fragments-1.mdx +487 -0
- package/llm-docs/tutorial/graphql.mdx +172 -0
- package/llm-docs/tutorial/interfaces-polymorphism.mdx +161 -0
- package/llm-docs/tutorial/intro.mdx +58 -0
- package/llm-docs/tutorial/mutations-updates.mdx +624 -0
- package/llm-docs/tutorial/organizing-mutations-queries-and-subscriptions.mdx +13 -0
- package/llm-docs/tutorial/queries-1.mdx +267 -0
- package/llm-docs/tutorial/queries-2.mdx +389 -0
- package/llm-docs/tutorial/refetchable-fragments.mdx +352 -0
- package/multi-actor-environment/ActorIdentifier.d.ts +17 -0
- package/multi-actor-environment/ActorIdentifier.js.flow +2 -2
- package/multi-actor-environment/ActorSpecificEnvironment.js.flow +15 -15
- package/multi-actor-environment/ActorUtils.js.flow +1 -1
- package/multi-actor-environment/MultiActorEnvironment.d.ts +123 -0
- package/multi-actor-environment/MultiActorEnvironment.js.flow +32 -24
- package/multi-actor-environment/MultiActorEnvironmentTypes.d.ts +225 -0
- package/multi-actor-environment/MultiActorEnvironmentTypes.js.flow +6 -6
- package/multi-actor-environment/index.d.ts +14 -0
- package/multi-actor-environment.d.ts +8 -0
- package/mutations/RelayDeclarativeMutationConfig.d.ts +70 -0
- package/mutations/RelayDeclarativeMutationConfig.js.flow +9 -9
- package/mutations/RelayRecordProxy.js.flow +8 -11
- package/mutations/RelayRecordSourceMutator.js.flow +4 -4
- package/mutations/RelayRecordSourceProxy.js.flow +4 -4
- package/mutations/RelayRecordSourceSelectorProxy.js.flow +6 -6
- package/mutations/applyOptimisticMutation.d.ts +25 -0
- package/mutations/applyOptimisticMutation.js.flow +2 -2
- package/mutations/commitLocalUpdate.d.ts +10 -0
- package/mutations/commitMutation.d.ts +48 -0
- package/mutations/commitMutation.js.flow +21 -17
- package/mutations/createUpdatableProxy.js.flow +19 -19
- package/mutations/readUpdatableFragment.js.flow +3 -3
- package/mutations/readUpdatableQuery.js.flow +3 -3
- package/mutations/validateMutation.js.flow +7 -7
- package/network/RelayNetwork.d.ts +12 -0
- package/network/RelayNetworkTypes.d.ts +145 -0
- package/network/RelayNetworkTypes.js.flow +18 -18
- package/network/RelayObservable.d.ts +197 -0
- package/network/RelayObservable.js.flow +32 -30
- package/network/RelayQueryResponseCache.d.ts +16 -0
- package/network/RelayQueryResponseCache.js.flow +3 -3
- package/network/wrapNetworkWithLogObserver.js.flow +1 -1
- package/package.json +2 -1
- package/query/GraphQLTag.d.ts +45 -0
- package/query/GraphQLTag.js.flow +22 -10
- package/query/fetchQuery.d.ts +21 -0
- package/query/fetchQuery.js.flow +23 -10
- package/query/fetchQueryInternal.d.ts +26 -0
- package/query/fetchQueryInternal.js.flow +4 -4
- package/query/fetchQuery_DEPRECATED.d.ts +17 -0
- package/query/fetchQuery_DEPRECATED.js.flow +1 -1
- package/store/ClientID.d.ts +14 -0
- package/store/DataChecker.js.flow +51 -15
- package/store/NormalizationEngine.js.flow +782 -0
- package/store/OperationExecutor.d.ts +51 -0
- package/store/OperationExecutor.js.flow +204 -98
- package/store/RelayConcreteVariables.js.flow +5 -5
- package/store/RelayErrorTrie.js.flow +12 -12
- package/store/RelayExperimentalGraphResponseHandler.js.flow +3 -3
- package/store/RelayExperimentalGraphResponseTransform.js.flow +10 -10
- package/store/RelayModernEnvironment.d.ts +97 -0
- package/store/RelayModernEnvironment.js.flow +58 -43
- package/store/RelayModernFragmentSpecResolver.js.flow +1 -1
- package/store/RelayModernOperationDescriptor.d.ts +28 -0
- package/store/RelayModernOperationDescriptor.js.flow +1 -1
- package/store/RelayModernRecord.d.ts +92 -0
- package/store/RelayModernRecord.js.flow +44 -20
- package/store/RelayModernSelector.d.ts +123 -0
- package/store/RelayModernSelector.js.flow +21 -21
- package/store/RelayModernStore.d.ts +57 -0
- package/store/RelayModernStore.js.flow +219 -58
- package/store/RelayOperationTracker.d.ts +29 -0
- package/store/RelayOperationTracker.js.flow +2 -2
- package/store/RelayOptimisticRecordSource.js.flow +2 -2
- package/store/RelayPublishQueue.js.flow +29 -20
- package/store/RelayReader.js.flow +129 -57
- package/store/RelayRecordSource.d.ts +26 -0
- package/store/RelayRecordSource.js.flow +10 -0
- package/store/RelayRecordState.d.ts +28 -0
- package/store/RelayRecordState.js.flow +1 -1
- package/store/RelayReferenceMarker.js.flow +5 -4
- package/store/RelayResponseNormalizer.d.ts +28 -0
- package/store/RelayResponseNormalizer.js.flow +130 -62
- package/store/RelayStoreSubscriptions.js.flow +52 -8
- package/store/RelayStoreTypes.d.ts +1327 -0
- package/store/RelayStoreTypes.js.flow +371 -278
- package/store/RelayStoreUtils.d.ts +86 -0
- package/store/RelayStoreUtils.js.flow +16 -8
- package/store/ResolverCache.js.flow +2 -2
- package/store/ResolverFragments.d.ts +43 -0
- package/store/ResolverFragments.js.flow +22 -14
- package/store/StoreInspector.js.flow +7 -8
- package/store/ViewerPattern.d.ts +11 -0
- package/store/cloneRelayHandleSourceField.js.flow +1 -1
- package/store/cloneRelayScalarHandleSourceField.js.flow +1 -1
- package/store/createFragmentSpecResolver.d.ts +16 -0
- package/store/createRelayContext.js.flow +1 -1
- package/store/createRelayLoggingContext.js.flow +4 -4
- package/store/defaultGetDataID.js.flow +2 -2
- package/store/isRelayModernEnvironment.d.ts +8 -0
- package/store/isRelayModernEnvironment.js.flow +4 -2
- package/store/live-resolvers/LiveResolverCache.js.flow +55 -20
- package/store/live-resolvers/LiveResolverSuspenseSentinel.js.flow +3 -3
- package/store/live-resolvers/getOutputTypeRecordIDs.js.flow +1 -1
- package/store/live-resolvers/isLiveStateValue.js.flow +2 -2
- package/store/live-resolvers/resolverDataInjector.d.ts +27 -0
- package/store/live-resolvers/resolverDataInjector.js.flow +8 -5
- package/store/observeFragmentExperimental.d.ts +46 -0
- package/store/observeFragmentExperimental.js.flow +50 -21
- package/store/observeQueryExperimental.d.ts +30 -0
- package/store/observeQueryExperimental.js.flow +5 -5
- package/store/readInlineData.d.ts +19 -0
- package/store/readInlineData.js.flow +5 -5
- package/store/waitForFragmentExperimental.d.ts +49 -0
- package/store/waitForFragmentExperimental.js.flow +3 -3
- package/subscription/requestSubscription.d.ts +27 -0
- package/subscription/requestSubscription.js.flow +10 -10
- package/util/JSResourceTypes.flow.js.flow +4 -4
- package/util/NormalizationNode.d.ts +235 -0
- package/util/NormalizationNode.js.flow +127 -123
- package/util/ReaderNode.d.ts +264 -0
- package/util/ReaderNode.js.flow +156 -151
- package/util/RelayConcreteNode.d.ts +120 -0
- package/util/RelayConcreteNode.js.flow +32 -32
- package/util/RelayError.d.ts +13 -0
- package/util/RelayError.js.flow +4 -1
- package/util/RelayFeatureFlags.d.ts +40 -0
- package/util/RelayFeatureFlags.js.flow +21 -1
- package/util/RelayProfiler.d.ts +121 -0
- package/util/RelayProfiler.js.flow +1 -1
- package/util/RelayReplaySubject.d.ts +25 -0
- package/util/RelayReplaySubject.js.flow +3 -3
- package/util/RelayRuntimeTypes.d.ts +59 -0
- package/util/RelayRuntimeTypes.js.flow +36 -33
- package/util/createPayloadFor3DField.d.ts +17 -0
- package/util/createPayloadFor3DField.js.flow +9 -5
- package/util/deepFreeze.d.ts +8 -0
- package/util/deepFreeze.js.flow +2 -2
- package/util/getFragmentIdentifier.d.ts +10 -0
- package/util/getFragmentIdentifier.js.flow +1 -1
- package/util/getPaginationMetadata.d.ts +20 -0
- package/util/getPaginationMetadata.js.flow +1 -1
- package/util/getPaginationVariables.d.ts +20 -0
- package/util/getPaginationVariables.js.flow +1 -1
- package/util/getPendingOperationsForFragment.d.ts +18 -0
- package/util/getPendingOperationsForFragment.js.flow +2 -2
- package/util/getRefetchMetadata.d.ts +19 -0
- package/util/getRefetchMetadata.js.flow +6 -5
- package/util/getRelayHandleKey.d.ts +8 -0
- package/util/getRequestIdentifier.d.ts +17 -0
- package/util/getValueAtPath.d.ts +8 -0
- package/util/getValueAtPath.js.flow +3 -3
- package/util/handlePotentialSnapshotErrors.d.ts +14 -0
- package/util/handlePotentialSnapshotErrors.js.flow +5 -5
- package/util/isEmptyObject.js.flow +1 -1
- package/util/isPromise.d.ts +8 -0
- package/util/isPromise.js.flow +2 -2
- package/util/isScalarAndEqual.d.ts +8 -0
- package/util/isScalarAndEqual.js.flow +1 -1
- package/util/recycleNodesInto.d.ts +8 -0
- package/util/recycleNodesInto.js.flow +2 -2
- package/util/registerEnvironmentWithDevTools.js.flow +1 -1
- package/util/shallowFreeze.js.flow +1 -1
- package/util/stableCopy.d.ts +8 -0
- package/util/stableCopy.js.flow +5 -5
- package/util/withProvidedVariables.d.ts +19 -0
- 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.
|