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

v3.3.0

Published

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

Downloads

286

Readme

react-graphql-query

面向 React 和 React Native 的 definition 驱动 GraphQL 查询辅助库。它把 graphql-request@tanstack/react-query、类型化 GraphQL Document、响应解析和缓存辅助能力组合在一个可复用的 definition 里。

English

你会得到什么

  • defineGraphql 只定义一次 GraphQL operation。
  • 同一个 definition 可用于 hooks、非 hook 请求、mutation、infinite query 和缓存辅助函数。
  • 直接读取解析后的数据,例如 catalog.product,不用每次处理完整 GraphQL root object。
  • 可从 TypedDocumentNode 推导返回类型和变量类型,也支持手写 root 类型。
  • 通过 GraphqlQueryProvider 提供共享 GraphQLClient
  • 内置 codegen 流程,可从 .graphql 自动生成 defineGraphql wrapper 文件。

安装

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

Peer 要求:

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

如果需要使用内置 codegen CLI,在应用项目里再安装:

npm install -D @graphql-codegen/cli @graphql-codegen/client-preset graphql

5 分钟接入

1. 创建 GraphQL client 和 Provider

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

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

export function Providers({ children }: { children: React.ReactNode }) {
    return (
        <GraphqlQueryProvider client={graphClient} queryClient={queryClient}>
            {children}
        </GraphqlQueryProvider>
    );
}

建议在应用根部使用 GraphqlQueryProvider。它会同时提供 GraphQLClient 和 TanStack Query 的 QueryClient

2. 定义一个 operation

手写 document 方式:

import { gql } from "graphql-request";
import { defineGraphql } from "@ldystudio/react-graphql-query";

type ProductDetailRoot = {
    catalog: {
        product: {
            id: string;
            title: string;
        };
    };
};

export const PRODUCT_DETAIL = defineGraphql<ProductDetailRoot, { id: string }>()({
    document: gql`
        query ProductDetail($id: ID!) {
            catalog {
                product(id: $id) {
                    id
                    title
                }
            }
        }
    `,
    parseKey: "catalog.product",
});

生成 document 方式:

import { defineGraphql } from "@ldystudio/react-graphql-query";
import * as Gen from "../__generated__/main";

export const PRODUCT_DETAIL = defineGraphql<Gen.ProductDetailQuery, Gen.ProductDetailQueryVariables>()({
    document: Gen.ProductDetailDocument,
    parseKey: "catalog.product",
});

3. 在组件里查询数据

import { useGraphQuery } from "@ldystudio/react-graphql-query";
import { PRODUCT_DETAIL } from "./gql";

export function ProductTitle({ id }: { id: string }) {
    const query = useGraphQuery(PRODUCT_DETAIL, {
        variables: { id },
        enabled: Boolean(id),
    });

    if (query.isPending) return null;
    if (query.isError) return <span>加载失败</span>;

    return <h1>{query.data.title}</h1>;
}

query.dataparseKey 指向的数据,所以这个组件拿到的是 catalog.product,不是完整 GraphQL 响应。

推荐的 Codegen 工作流

长期最省心的方式是:

  1. .graphql operation 文件。
  2. 执行 react-graphql-query-codegen
  3. *.gql.ts 导入生成好的 defineGraphql wrapper。
  4. 在业务里使用 useGraphQueryuseGraphMutation 和缓存辅助函数。

安装 codegen 依赖

npm install -D @graphql-codegen/cli @graphql-codegen/client-preset graphql

添加脚本

{
    "scripts": {
        "codegen": "react-graphql-query-codegen --config graphql.codegen.ts"
    }
}

创建 graphql.codegen.ts

import { defineGraphqlCodegenProject } from "@ldystudio/react-graphql-query/codegen";

const API_URL = "https://example.com/graphql/v1";

export default defineGraphqlCodegenProject({
    targets: {
        main: {
            schema: API_URL,
            documents: ["src/service/gql/main.graphql"],
            output: "src/service/__generated__/main.ts",
            definitions: {
                output: "src/service/gql/main.gql.ts",
            },
            config: {
                defaultScalarType: "unknown",
            },
        },
    },
    format: {
        command: ["bunx", "biome", "check", "--write"],
    },
});

示例 .graphql 文件

query ProductDetail($id: ID!) {
    catalog {
        product(id: $id) {
            id
            title
        }
    }
}

执行 codegen

npm run codegen

配置 definitions.output 后,CLI 会自动追加缺失 wrapper:

import { defineGraphql } from "@ldystudio/react-graphql-query";
import * as Gen from "../__generated__/main";

export const PRODUCT_DETAIL = defineGraphql<Gen.ProductDetailQuery, Gen.ProductDetailQueryVariables>()({
    document: Gen.ProductDetailDocument,
});

definitions 生成器只追加,不覆盖。已有 definition 不会被重写,所以可以安全手动添加 parseKeykey、业务 root 类型、默认选项或 client

如果某个 target 需要独立 GraphQL client:

definitions: {
    output: "src/service/gql/secondary.gql.ts",
    client: {
        name: "SecondaryGraphqlClient",
        importPath: "~/service/client",
    },
}

更多完整示例:

所有示例都使用占位 endpoint、operation 名称和字段路径。

核心概念

Definition

definition 是本库的复用单元。

const PRODUCT_DETAIL = defineGraphql<Root, Variables>()({
    document,
    parseKey: "catalog.product",
    key: ["catalog", "product-detail"],
    variables: { locale: "zh-CN" },
    staleTime: 60_000,
});

常见字段:

  • document:GraphQL query 或 mutation document。必填。
  • parseKey:响应解析路径,例如 catalog.product;传 "" 时返回完整 root 响应。
  • key:可选缓存身份,和 parseKey 解耦。
  • variables:默认变量,调用方不传时使用。
  • client:可选 definition 级 GraphQLClient
  • TanStack Query 选项,例如 enabledstaleTimegcTime

parseKey

parseKey 告诉库返回 GraphQL 响应的哪一段。

parseKey: "catalog.product"

这样的响应:

{
    catalog: {
        product: { id: "p1", title: "Cube" }
    }
}

会变成:

{ id: "p1", title: "Cube" }

当一个 operation 有意读取多个顶层字段,且调用方需要完整 root object 时,可以使用空 parseKey

const DASHBOARD = defineGraphql()({
    document: Gen.DashboardDocument,
    parseKey: "",
    key: ["dashboard"],
});

这时 query.data 是完整响应,例如 { notifications, accountSummary }

如果省略 parseKey,库会尝试从“只有一条明确选择路径”的 document 里自动推导。显式传入时,以手写值为准。

key

key 控制缓存身份。当缓存 key 需要稳定,或不想和响应路径绑定时使用。

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

查询

useGraphQuery

React 组件里使用。

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

返回 TanStack Query 的 UseQueryResult

graphQuery

React hook 外使用:路由加载、SSR、预取、事件处理、脚本。

const product = await graphQuery(PRODUCT_DETAIL, {
    client: graphClient,
    variables: { id: "p1" },
});

graphQueryOptions

需要直接接入 TanStack Query 时使用。

const options = graphQueryOptions(PRODUCT_DETAIL, {
    client: graphClient,
    variables: { id: "p1" },
});

Mutation

定义 mutation

export const UPDATE_PRODUCT = defineGraphql<Gen.UpdateProductMutation, Gen.UpdateProductMutationVariables>()({
    document: Gen.UpdateProductDocument,
    parseKey: "catalog.updateProduct",
});

在 React 里使用

const mutation = useGraphMutation(UPDATE_PRODUCT);

mutation.mutate({
    id: "p1",
    title: "Updated title",
});

Mutation 回调会收到 GraphMutationContext,里面有 clientdefinitionqueryClient

在 React 外使用

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

乐观更新

useGraphMutationoptimisticUpdate,可以立即把变更应用到目标 query 的缓存,mutation 失败时自动恢复原值:

const mutation = useGraphMutation<
    typeof UPDATE_PRODUCT,
    unknown,
    unknown,
    typeof PRODUCT_LIST
>(UPDATE_PRODUCT, {
    optimisticUpdate: {
        query: PRODUCT_LIST,
        getOptimisticState: ({ currentData, variables }) =>
            currentData?.map((item) =>
                item.id === variables.id ? { ...item, title: variables.title } : item
            ),
    },
});
  • getOptimisticState 接收当前解析数据(currentData)和 mutation 的 variables,返回下一个缓存值;返回 undefined 表示不改动缓存。
  • 失败时自动恢复原值;成功时(invalidateQueryOnSuccess 默认为 false)乐观值保留在缓存里,设为 true 则触发重新拉取。
  • 目标是 infinite query 时,用 kind: "infinite" 并传 queryVariables
  • 数据类型从 query 定义自动推导,调用方无需手写 GraphQueryData<...>

乐观更新的设计灵感来自 tanstack-query-optimistic-updates

Infinite Query

建议把 parseKey 指向 connection 本身,不要直接指向 nodes,这样 pageInfo 还在。

const PRODUCT_CONNECTION = defineGraphql<Gen.ProductConnectionQuery, Gen.ProductConnectionQueryVariables>()({
    document: Gen.ProductConnectionDocument,
    parseKey: "catalog.products",
});
const query = useInfiniteGraphQuery(PRODUCT_CONNECTION, {
    variables: { first: 20 },
    initialPageParam: null as string | null,
    pageParamToVariables: (pageParam, variables) => ({
        ...variables,
        first: variables?.first ?? 20,
        after: pageParam,
    }),
    getNextPageParam: (lastPage) =>
        lastPage.pageInfo.hasNextPage ? lastPage.pageInfo.endCursor : undefined,
});

需要原始 TanStack Query 配置时,用 graphInfiniteQueryOptions

缓存辅助函数

缓存辅助函数操作的是解析后的数据,不需要你手动重建 GraphQL root object。

可用函数:

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

做乐观更新时,优先使用 useGraphMutationoptimisticUpdate 选项(见 乐观更新),它自动处理取消、快照、回滚和失效。需要更底层的控制时,再直接使用缓存辅助函数,例如:

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);
    },
});

如果 setGraphData 的 updater 返回 undefined,已有缓存保持不变;当前 query key 没有缓存时,也不会创建新缓存。

Client 解析规则

Hook API 的 client 优先级:

  1. definition.client
  2. options.client
  3. GraphqlClientProviderGraphqlQueryProvider 里的 provider client

非 hook 辅助函数没有 React provider context,例如 graphQuerygraphQueryOptionsgraphInfiniteQueryOptionsgraphMutation。这些场景要在 definition 或调用 options 里传 client

调试 Header

GraphqlClientProviderGraphqlQueryProvider 可以给 hook 请求加 x-graph-parse-key

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

适合在 graphql-request middleware 里记录请求。它只影响 provider 下的 hook 请求。

API 速查

| 任务 | API | | --- | --- | | 定义 operation | defineGraphql | | 组件里查询 | useGraphQuery | | React 外查询 | graphQuery | | 生成 TanStack Query 配置 | graphQueryOptions | | 组件里 mutation | useGraphMutation | | React 外 mutation | graphMutation | | 组件里 infinite query | useInfiniteGraphQuery | | 生成 infinite query 配置 | graphInfiniteQueryOptions | | 应用级注入 client | GraphqlQueryProvider | | 读写解析后的缓存 | getGraphData, setGraphData |

限制

  • 当前只支持 graphql-request
  • 自动 parseKey 推导只适用于 document 只有一个顶层字段的情况。需要多个顶层字段的完整 root 响应时,请使用 parseKey: ""
  • 嵌套 selection 分叉或语义不明确时,推导会停在最后一个安全 object 节点。
  • debugParseKeyHeader 只影响 GraphqlClientProviderGraphqlQueryProvider 下的 hook 请求。

License

MIT