Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for graphql-typed-document-node #2353

Open
wants to merge 1 commit into
base: v1.x-2022-07
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/wild-rivers-refuse.md
@@ -0,0 +1,5 @@
---
'@shopify/hydrogen': minor
---

Add support for @graphql-typed-document-node
17 changes: 12 additions & 5 deletions packages/hydrogen/src/hooks/useShopQuery/hooks.ts
Expand Up @@ -11,6 +11,8 @@ import {getStorefrontApiRequestHeaders} from '../../utilities/storefrontApi.js';
import {parseJSON} from '../../utilities/parse.js';
import {useQuery} from '../../foundation/useQuery/hooks.js';
import {HydrogenRequest} from '../../foundation/HydrogenRequest/HydrogenRequest.server.js';
import {TypedDocumentNode} from '@graphql-typed-document-node/core';
import {print} from 'graphql';

export interface UseShopQueryResponse<T> {
/** The data returned by the query. */
Expand All @@ -27,7 +29,7 @@ const shouldCacheResponse = (body: any) => {
/**
* The `useShopQuery` hook allows you to make server-only GraphQL queries to the Storefront API. It must be a descendent of a `ShopifyProvider` component.
*/
export function useShopQuery<T>({
export function useShopQuery<Data, Variables extends Record<string, any> = {}>({
query,
variables = {},
cache,
Expand All @@ -36,7 +38,7 @@ export function useShopQuery<T>({
/** A string of the GraphQL query.
* If no query is provided, useShopQuery will make no calls to the Storefront API.
*/
query?: string;
query?: string | TypedDocumentNode<Data, Variables>;
/** An object of the variables for the GraphQL query. */
variables?: Record<string, any>;
/** The [caching strategy](https://shopify.dev/custom-storefronts/hydrogen/querying/cache#caching-strategies) to
Expand All @@ -50,12 +52,12 @@ export function useShopQuery<T>({
* to preload the query for all requests.
*/
preload?: PreloadOptions;
}): UseShopQueryResponse<T> {
}): UseShopQueryResponse<Data> {
/**
* If no query is passed, we no-op here to allow developers to obey the Rules of Hooks.
*/
if (!query) {
return {data: undefined as unknown as T, errors: undefined};
return {data: undefined as unknown as Data, errors: undefined};
}

if (!META_ENV_SSR) {
Expand All @@ -75,7 +77,12 @@ export function useShopQuery<T>({
privateStorefrontToken,
} = useShop();

const body = query ? graphqlRequestBody(query, variables) : '';
const body = query
? graphqlRequestBody(
typeof query === 'string' ? query : print(query),
variables
)
: '';

const {data, error} = useQuery(
[storeDomain, storefrontApiVersion, body],
Expand Down
23 changes: 15 additions & 8 deletions packages/hydrogen/src/utilities/apiRoutes.ts
Expand Up @@ -19,6 +19,8 @@ import type {
import {emptySessionImplementation} from '../foundation/session/session.js';
import {UseShopQueryResponse} from '../hooks/useShopQuery/hooks.js';
import {FORM_REDIRECT_COOKIE, RSC_PATHNAME} from '../constants.js';
import {TypedDocumentNode} from '@graphql-typed-document-node/core';
import {print} from 'graphql';

let memoizedApiRoutes: Array<HydrogenApiRoute> = [];
let memoizedRawRoutes: ImportGlobEagerOutput = {};
Expand All @@ -27,7 +29,9 @@ type RouteParams = Record<string, string>;
export type RequestOptions = {
log: Logger;
params: RouteParams;
queryShop: <T>(args: QueryShopArgs) => Promise<UseShopQueryResponse<T>>;
queryShop: <Data, Variables extends Record<string, any> = {}>(
args: QueryShopArgs<Data, Variables>
) => Promise<UseShopQueryResponse<Data>>;
session: SessionApi | null;
hydrogenConfig: ResolvedHydrogenConfig;
};
Expand Down Expand Up @@ -144,23 +148,23 @@ export function getApiRouteFromURL(
* It's similar to the `useShopQuery` hook, which is available in server components.
* To use `queryShop`, pass `shopifyConfig` to `renderHydrogen` inside `App.server.jsx`.
*/
interface QueryShopArgs {
interface QueryShopArgs<Data, Variables extends Record<string, any>> {
/** A string of the GraphQL query.
* If no query is provided, then the `useShopQuery` makes no calls to the Storefront API.
*/
query: string;
query: string | TypedDocumentNode<Data, Variables>;
/** An object of the variables for the GraphQL query. */
variables?: Record<string, any>;
variables?: Variables;
}

function queryShopBuilder(
shopifyConfigGetter: ResolvedHydrogenConfig['shopify'],
request: HydrogenRequest
) {
return async function queryShop<T>({
return async function queryShop<Data, Variables extends Record<string, any>>({
query,
variables,
}: QueryShopArgs): Promise<T> {
}: QueryShopArgs<Data, Variables>): Promise<UseShopQueryResponse<Data>> {
const shopifyConfig =
typeof shopifyConfigGetter === 'function'
? await shopifyConfigGetter(request)
Expand Down Expand Up @@ -188,11 +192,14 @@ function queryShopBuilder(
storefrontId,
});

const fetcher = fetchBuilder<T>(
const fetcher = fetchBuilder<UseShopQueryResponse<Data>>(
`https://${storeDomain}/api/${storefrontApiVersion}/graphql.json`,
{
method: 'POST',
body: graphqlRequestBody(query, variables),
body: graphqlRequestBody(
typeof query === 'string' ? query : print(query),
variables
),
headers: {
'Content-Type': 'application/json',
...extraHeaders,
Expand Down