npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@ldystudio/react-graphql-query

v2.0.0

Published

Definition-driven GraphQL query helpers for React and React Native built on TanStack Query and graphql-request

Readme

react-graphql-query

面向 React 和 React Native 的 definition 驱动 GraphQL 查询辅助库,基于 @tanstack/react-querygraphql-request

English

特性

  • 用一个 definition 绑定 documentparseKey、默认变量、默认查询配置和可选的 GraphQLClient
  • 同时支持手写根类型和 TypedDocumentNode 驱动的结果/变量类型推导
  • 返回解析后的目标数据,而不是整棵 GraphQL 根对象
  • 支持从 GraphQL document 保守推导 parseKey,也支持手动覆盖
  • 通过 GraphqlClientProvider 为 Hook 场景提供默认 client,也可通过 GraphqlQueryProvider 一次性组合注入 GraphQLClientQueryClient
  • 支持 definition 级可选 key,以及 getGraphDatasetGraphDatainvalidateGraphQuery 这类缓存辅助函数

安装

npm install @ldystudio/react-graphql-query @tanstack/react-query graphql graphql-request

要求:

  • react >= 18
  • @tanstack/react-query >= 5
  • graphql-request >= 6.1.0

快速开始

import { QueryClient } from "@tanstack/react-query";
import { GraphQLClient, gql } from "graphql-request";
import {
    GraphqlQueryProvider,
    defineGraphql,
    useGraphQuery,
} from "@ldystudio/react-graphql-query";

const client = new GraphQLClient("https://example.com/graphql");
const queryClient = new QueryClient();

interface ProductListRoot {
    storefront: {
        featuredProducts: {
            nodes: Array<{
                id: number;
                title: string;
            }>;
        };
    };
}

const FEATURED_PRODUCTS = defineGraphql<ProductListRoot>()({
    document: gql`
        query {
            storefront {
                featuredProducts {
                    nodes {
                        id
                        title
                    }
                }
            }
        }
    `,
});

function ProductList() {
    const query = useGraphQuery(FEATURED_PRODUCTS);

    if (query.isPending) {
        return null;
    }

    return query.data?.map((product) => product.title).join(", ");
}

export function App() {
    return (
        <GraphqlQueryProvider client={client} queryClient={queryClient}>
            <ProductList />
        </GraphqlQueryProvider>
    );
}

这个例子里,query.data 会被推导为 Array<{ id: number; title: string }>,因为根类型和 document 都沿着同一条安全路径 storefront.featuredProducts.nodes 收敛。

如果你的应用主要访问同一个 GraphQL endpoint,推荐在组件树外层包一层 GraphqlQueryProvider,这样组件里通常不需要重复传 client,Hook 也能自动读取共享的 QueryClient

核心 API

defineGraphql

定义一个可复用的 GraphQL definition。

const PRODUCT_DETAIL = defineGraphql<{
    catalog: {
        product: {
            id: number;
            title: string;
        };
    };
}>()({
    document: gql`
        query ($id: Int!) {
            catalog {
                product(id: $id) {
                    id
                    title
                }
            }
        }
    `,
});

它也支持直接接收 TypedDocumentNode,这样可以从生成的 GraphQL document 自动推导结果类型和变量类型。

import { ProductDocument } from "./__generated__/graphql";

const PRODUCT_DETAIL = defineGraphql({
    document: ProductDocument,
    parseKey: "catalog.product",
    key: ["catalog", "product-detail"],
});

这里的 ProductDocument 必须是一个真实存在的运行时值,通常来自 GraphQL codegen 生成的 document 常量。declare const ... 只能用于类型演示,不能直接放进业务代码执行。

definition 可以包含:

  • document: 必填,GraphQL 查询或 mutation 文档
  • parseKey: 可选,响应解析路径,例如 catalog.product
  • key: 可选,缓存身份,和 parseKey 分离
  • variables: 可选,默认变量
  • client: 可选,绑定在 definition 上的 GraphQLClient
  • React Query 选项,例如 staleTimegcTimeenabled

useGraphQuery

在 React 组件里执行 definition。

const query = useGraphQuery(PRODUCT_DETAIL, {
    variables: { id: 7 },
});

它内部调用 useQuery(graphQueryOptions(...)),返回标准 UseQueryResult

如果当前组件树被 GraphqlClientProvider 包裹,且 definition 和 hook options 都没有显式传 clientuseGraphQuery 会自动使用 provider 中的 client。

GraphqlQueryProvider

一次性提供 GraphQLClientQueryClient

<GraphqlQueryProvider client={client} queryClient={queryClient}>
    <App />
</GraphqlQueryProvider>

这是更推荐的应用级 provider。它内部组合了 QueryClientProviderGraphqlClientProvider

GraphqlClientProvider

为 Hook 场景提供默认 GraphQLClient

<GraphqlClientProvider client={client}>
    <App />
</GraphqlClientProvider>

它会给本库的所有 Hook API 提供默认 GraphQLClient,包括 useGraphQueryuseInfiniteGraphQueryuseGraphMutation

你也可以开启 debugParseKeyHeader,这样 Hook 请求会带上 x-graph-parse-key,方便在 graphql-requestrequestMiddleware 里打印请求日志。

<GraphqlClientProvider client={client} debugParseKeyHeader>
    <App />
</GraphqlClientProvider>

graphQuerygraphQueryOptionsgraphInfiniteQueryOptionsgraphMutation 都不会继承 provider client,也不会继承这个调试 header 开关。

useGraphMutation

在 React 组件里执行解析后的 mutation。

const updateProduct = useGraphMutation(UPDATE_PRODUCT);

updateProduct.mutate({
    id: 7,
    title: "Updated title",
});

它内部基于 TanStack Query 的 useMutation,并会自动从 GraphqlQueryProviderQueryClientProvider 读取共享的 QueryClient

onMutateonSuccessonErroronSettled 这些 mutation 回调还会额外收到库层的 GraphMutationContext,其中包含 clientdefinitionqueryClient

graphMutation

在非 Hook 场景直接执行解析后的 mutation。

const product = await graphMutation(UPDATE_PRODUCT, {
    client,
    variables: {
        id: 7,
        title: "Updated title",
    },
});

useInfiniteGraphQuery

在 React 组件里执行解析后的 infinite query。

const query = useInfiniteGraphQuery(PRODUCT_CONNECTION, {
    variables: { first: 20 },
    initialPageParam: null as string | null,
    pageParamToVariables: (pageParam, variables) => ({
        ...variables,
        after: pageParam,
        first: variables?.first ?? 20,
    }),
    getNextPageParam: (lastPage) =>
        lastPage.pageInfo.hasNextPage ? lastPage.pageInfo.endCursor : undefined,
});

对于 cursor 分页,通常更适合把 parseKey 指向 connection 本身,例如 catalog.products,而不是直接指向 nodes,这样 pageInfo 还在。

graphInfiniteQueryOptions

生成标准 TanStack Query infinite query 配置对象。

const options = graphInfiniteQueryOptions(PRODUCT_CONNECTION, {
    variables: { first: 20 },
    initialPageParam: null as string | null,
    pageParamToVariables: (pageParam, variables) => ({
        ...variables,
        after: pageParam,
        first: variables?.first ?? 20,
    }),
    getNextPageParam: (lastPage) =>
        lastPage.pageInfo.hasNextPage ? lastPage.pageInfo.endCursor : undefined,
});

graphQuery

在非 Hook 场景直接获取解析后的数据。

import { QueryClient } from "@tanstack/react-query";
import { graphQuery } from "@ldystudio/react-graphql-query";

const queryClient = new QueryClient();

const product = await graphQuery(PRODUCT_DETAIL, {
    client,
    queryClient,
    variables: { id: 7 },
});

适合预取、路由加载、SSR 或事件流里取数。

graphQueryOptions

生成标准 TanStack Query 配置对象。

const options = graphQueryOptions(PRODUCT_DETAIL, {
    client,
    variables: { id: 7 },
});

可以配合 useQueryprefetchQuery 或其他 TanStack Query API 使用。

缓存辅助函数

definition 级缓存 helper 让你直接操作 parse 后的数据,而不用手动回填 GraphQL root shape。

setGraphData 会沿着 parseKey 回写,只更新解析出来的那一段数据,同时保留原始 GraphQL root object 上的 sibling fields。

如果 updater 返回 undefined,这个 helper 不会删除已有缓存;已有缓存会保持不变,而当当前 query key 不存在时,它也不会创建新的缓存条目。

  • queryKeyOf
  • getGraphData
  • setGraphData
  • invalidateGraphQuery
  • cancelGraphQuery
  • removeGraphQuery
  • resetGraphQuery

乐观更新示例:

const mutation = useGraphMutation(UPDATE_PRODUCT, {
    onMutate: async (variables, context) => {
        await cancelGraphQuery(context.queryClient, PRODUCT_LIST);

        const previous = getGraphData(context.queryClient, PRODUCT_LIST);

        setGraphData(context.queryClient, PRODUCT_LIST, undefined, (current) =>
            current?.map((item) =>
                item.id === variables.id ? { ...item, title: variables.title } : item
            )
        );
        return { previous };
    },
    onError: (_error, _variables, rollback, context) => {
        if (rollback?.previous) {
            setGraphData(context.queryClient, PRODUCT_LIST, undefined, rollback.previous);
        }
    },
    onSettled: (_data, _error, _variables, _rollback, context) => {
        void invalidateGraphQuery(context.queryClient, PRODUCT_LIST);
    },
});

parseKey 推导

默认情况下,库会从 document 中自动推导 parseKey

query {
    storefront {
        featuredProducts {
            nodes {
                id
            }
        }
    }
}

上面会推导出:

"storefront.featuredProducts.nodes";

推导规则是保守的:

  • 只有当每一层都只有一个选中字段时才会继续向下
  • 运行时解析和类型推导都会停在最后一个无歧义节点
  • 遇到不透明包装值、分叉结构或歧义选择时,不会继续收窄

如果你需要显式路径,可以手动传 parseKey

const PRODUCT_DETAIL = defineGraphql<Root>()({
    parseKey: "catalog.product",
    document: gql`
        query ($id: Int!) {
            catalog {
                product(id: $id) {
                    id
                    title
                }
            }
        }
    `,
});

显式传入时,以手写值为准。

client 优先级

GraphQLClient 有三个来源:

  1. definition.client
  2. options.client
  3. GraphqlClientProvider

优先级如下:

  1. definition.client
  2. options.client
  3. provider client

如果三者都没有,会抛错。

queryKey 生成规则

默认情况下,queryKey 由完整的 parseKey 路径和完整的 variables 对象组成。

getGraphQueryKey("catalog.product", { id: 7 });

生成结果类似:

["catalog", "product", { id: 7 }];

保留完整变量对象可以避免“值序列相同但语义不同”的请求共享同一个缓存键。

如果你希望缓存身份更显式,可以在 definition 上提供 key

const PRODUCT_LIST = defineGraphql({
    document: ProductListDocument,
    parseKey: "catalog.products.nodes",
    key: ["catalog", "product-list"],
});

这会生成类似下面的 query key:

["catalog", "product-list", { first: 20 }];

限制

  • 当前只支持 graphql-request
  • 自动 parseKey 推导要求 document 只有一个顶层字段
  • 结构分叉或语义不明确时,推导会停在最后一个安全节点
  • debugParseKeyHeader 只影响 GraphqlClientProviderGraphqlQueryProvider 下的 Hook 请求

License

MIT