Skip to content

Instantly share code, notes, and snippets.

@fdecampredon
Created December 28, 2022 13:26
Show Gist options
  • Save fdecampredon/e8e977ea935d8b74ab82afbd3a5b6cf3 to your computer and use it in GitHub Desktop.
Save fdecampredon/e8e977ea935d8b74ab82afbd3a5b6cf3 to your computer and use it in GitHub Desktop.
Relay and next 13
import { graphql } from 'graphql';
import schema from './your_schema_path';
import type { VariablesOf, GraphQLTaggedNode } from 'react-relay';
import type {
ConcreteRequest,
PayloadData,
OperationType,
} from 'relay-runtime';
export type ServerQuery<TQuery extends OperationType> = {
id: string;
variables: VariablesOf<TQuery>;
payload: PayloadData;
};
const preloadServerQuery = async <TQuery extends OperationType>(
gqlQuery: GraphQLTaggedNode,
variables: VariablesOf<TQuery>,
): Promise<ServerQuery<TQuery>> => {
if (typeof gqlQuery === 'function') {
gqlQuery = gqlQuery();
}
if (gqlQuery.kind !== 'Request') {
throw new Error(
'preloadServerQuery: Expected a graphql`...` tagged query.',
);
}
const request = gqlQuery as ConcreteRequest;
const { params } = request;
const queryVariables = { ...variables };
const { providedVariables } = params as any;
if (providedVariables) {
Object.keys(providedVariables).forEach(key => {
//@ts-expect-error no types
queryVariables[key] = params.providedVariables[key].get();
});
}
// fetch instead of graphql if necessary
const response = await graphql({
schema,
// handle persisted queries is necessary
source: params.text as string,
variableValues: queryVariables,
});
return {
id: params.id ?? params.cacheID!,
variables,
// see https://github.com/apollographql/apollo-server/issues/3149#issuecomment-1117566982
payload: normalizeObject(response.data),
};
};
export default preloadServerQuery;
const normalizeObject = (obj: any): any => {
if (typeof obj !== 'object' || obj == null) {
return obj;
}
if (Array.isArray(obj)) {
return obj.map(normalizeObject);
}
if (!(obj instanceof Object)) {
const res = {} as any;
const keys = Object.keys(obj);
for (const key of keys) {
res[key] = normalizeObject(obj[key]);
}
obj = res;
}
return obj;
};
import { useMemo } from 'react';
import { useRelayEnvironment } from 'react-relay';
// @ts-expect-error no types
import useLazyLoadQueryNode from 'react-relay/lib/relay-hooks/useLazyLoadQueryNode';
// @ts-expect-error no types
import useMemoOperationDescriptor from 'react-relay/lib/relay-hooks/useMemoOperationDescriptor';
import { __internal } from 'relay-runtime';
import type { ServerQuery } from './preloadServerQuery';
import type { OperationType, GraphQLTaggedNode } from 'relay-runtime';
const { fetchQuery } = __internal;
function useServerQuery<TQuery extends OperationType>(
gqlQuery: GraphQLTaggedNode,
serverQuery: ServerQuery<TQuery>,
): TQuery['response'] {
const environment = useRelayEnvironment();
const operation = useMemoOperationDescriptor(gqlQuery, serverQuery.variables);
if (operation.request.node.params.id !== serverQuery.id) {
throw Error(
`useServerQuery(): Mismatched version for query '${operation.request.node.params.name}'`,
);
}
// ugly hack to avoid commiting the payload multiple times
useMemo(() => {
environment.commitPayload(operation, serverQuery.payload);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return useLazyLoadQueryNode({
componentDisplayName: 'useServerQuery()',
fetchKey: null,
fetchPolicy: 'store-only',
fetchObservable: fetchQuery(environment, operation),
query: operation,
renderPolicy: null,
});
}
export default useServerQuery;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment