
Refine v5 集成 HasuraGraphQL Data Provider 的idType配置与完整实战指南【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refineRefine 为 Hasura 提供了开箱即用的 GraphQL Data Providerrefinedev/hasura让你可以直连 Hasura 生成的 GraphQL API用 Refine 的useList、useForm、useTable等 Hook 完成 CRUD、筛选、排序、分页甚至实时订阅。本文以官方文档documentation/docs/examples/data-provider/hasura.md为骨架结合仓库内packages/hasura的源码实现与examples/data-provider-hasura完整示例重点讲解idType的配置策略并顺带解析命名约定、筛选算子映射、自定义 GraphQL 查询与 live provider 订阅等关键机制。读完本文你将能在 Refine 项目中稳定接入任意 Hasura 后端并正确处理uuid与Int两种主键类型混用的场景。一、Hasura Data Provider 是什么Hasura 是一个基于数据库自动生成 GraphQL/REST API 的后端引擎其生成的 API 天然支持过滤where、排序order_by、分页limit/offset和聚合查询_aggregate。Refine 的refinedev/hasura包把这些能力封装成了符合 Refine 规范的 Data Provider业务代码只需要描述要什么数据由 provider 负责翻译成 Hasura 的 GraphQL 语法。从仓库源码可以看到Data Provider 实现 的签名是const dataProvider ( client: GraphQLClient, options?: HasuraDataProviderOptions, ): RequiredDataProvider { ... };它接收一个graphql-request的GraphQLClient实例和可选的options返回一个实现了getList、getOne、create、update、deleteOne、custom等全部方法的 Data Provider。options的类型定义在 packages/hasura/src/types/index.tsexport type HasuraProviderOptions { idType?: IDType | ((resource: string) IDType); namingConvention?: NamingConvention; };其中IDType的合法取值是uuid | Int | String | NumericNamingConvention是hasura-default | graphql-default。安装与最小接入在 packages/hasura/README.md 中给出了标准的安装与接入方式npm install refinedev/hasuraimport dataProvider, { GraphQLClient } from refinedev/hasura; const client new GraphQLClient(HASURA_API_URL, { headers: { x-hasura-role: public, }, }); const App () { return ( Refine dataProvider{dataProvider(client)} /* ... */ {/* ... */} /Refine ); };仓库中的完整示例 examples/data-provider-hasura/src/App.tsx 展示了更真实的初始化代码它指向一个 Hasura Cloud 实例通过x-hasura-role: public请求头指定访问角色然后创建GraphQLClient并传给dataProvider(client)。同一个文件里还注释展示了如何用graphql-ws创建 WebSocket 客户端、传入liveProvider以启用实时订阅。二、核心配置ID Data TypeidType这是官方文档的核心章节。Hasura 表的主键列类型可以是uuid默认也是 Hasura Cloud 新表的常见选择也可以是自增Int甚至是String、Numeric。Data Provider 在生成_by_pk、delete_*_by_pk、update_*_by_pk等按主键操作的 GraphQL 变量类型时必须知道主键的 GraphQL 标量类型否则类型不匹配会直接报错。默认情况下provider 假设你的ID类型是uuid见 getIdType 实现return idType ?? uuid。你可以通过idType选项改变这一行为它支持两种传法。传固定值Int 或 uuid如果所有资源的主键类型一致直接传字符串即可这会为所有资源统一决定idTypeconst myDataProvider dataProvider(client, { idType: Int, });传函数按资源名动态决定如果项目里不同表的主键类型不同例如users表用自增Intposts表用uuid可以传一个接收资源名的函数返回对应的 ID 类型const idTypeMap: Recordstring, Int | uuid { users: Int, posts: uuid, }; const myDataProvider dataProvider(client, { idType: (resource) idTypeMap[resource] ?? uuid, });函数式的写法带兜底逻辑?? uuid未登记的资源名回退到默认的uuid非常适合多资源、多主键类型混用的中大型项目。源码层面idType 如何被使用idType的作用点在getIdType工具函数中集中收敛// packages/hasura/src/types/index.ts export const getIdType ( resource: string, idType?: IDType | ((resource: string) IDType), ) { if (typeof idType function) { return idType(resource); } return idType ?? uuid; };在 getOne 中id变量的 GraphQL 类型就是用getIdType(resource, idType)生成的deleteOne 也以同样的方式声明id: $id的类型。因此只要在初始化 provider 时正确配置idType所有按主键操作的查询/变更都会自动使用正确的类型无需在业务代码中重复指定。另外值得注意的是idType选项同样作用于 live provider 的订阅生成逻辑见 liveProvider 源码确保useOne订阅的where: { id: { _eq: $id } }变量类型与表主键一致。三、第二个选项namingConvention 命名约定除idType外options还支持namingConvention取值有两种取值说明hasura-default默认Hasura 原生命名字段为蛇形created_at操作名为posts_by_pk、posts_aggregate排序变量名为order_by布尔表达式类型名为posts_bool_expgraphql-defaultGraphQL 社区的驼峰命名约定字段转为createdAt操作名转为postsByPk排序变量名为orderBy从 dataProvider 实现 可以看到provider 会读取namingConvention并计算defaultNamingConvention布尔值随后在每一处操作名拼接、排序变量命名、过滤类型命名时走不同的分支getList中排序参数在graphql-default下使用orderBy在hasura-default下使用order_bydataProvider/index.ts#L178-L185过滤类型在hasura-default下是${operation}_bool_exp在graphql-default下转成 PascalCase 的${operation}BoolExp。配套的 generateFilters.ts 里有一个convertHasuraOperatorToGraphqlDefaultNaming函数会把_eq、_ilike之类的算子转换成_Eq、_Ilike形式以匹配 Hasura 在graphql-default命名约定下生成的算子名。也就是说选择哪种约定必须与你在 Hasura 控制台为 GraphQL 引擎配置的命名约定保持一致否则生成的查询会因为字段/操作名不匹配而失败。四、getList 的能力矩阵分页、排序与筛选getList是使用频率最高的方法它把 Refine 的pagination、sorters、filters三个参数翻译成 Hasura 的 GraphQL 语法并同时请求posts_aggregate拿到总数用于分页见 dataProvider/index.ts#L144-L252。分页仅在pagination.mode server时启用翻译为{ limit, offset: (currentPage - 1) * limit }默认每页 10 条、当前页从 1 开始dataProvider/index.ts#L153-L160排序交给generateSorting生成[{ column: asc }]结构的order_by数组筛选交给generateFilters生成where布尔表达式总数额外生成一个${operation}_aggregate聚合操作取aggregate.count作为total返回。筛选算子映射表Refine 的通用筛选算子eq、contains等与 Hasura 的where算子并不是一一对应的映射逻辑完整定义在 generateFilters.ts 的hasuraFilters表中Refine 算子Hasura 算子说明eq/ne_eq/_neq等于 / 不等于lt/lte/gt/gte_lt/_lte/_gt/_gte大小比较in/nin_in/_nin在集合内 / 不在集合内contains/ncontains_ilike/_nilike不区分大小写的模糊匹配containss/ncontainss_like/_nlike区分大小写的模糊匹配null/nnull_is_null值为true/falsestartswith/nstartswith_iregex正则前缀匹配值会被处理为^valueendswith/nendswith_iregex正则后缀匹配值会被处理为value$startswiths/endswiths_similar/_similar使用 SQLLIKE的value%/%value形式or/and/not_or/_and/_not逻辑组合值的预处理逻辑在handleFilterValuegenerateFilters.ts#L82-L111中contains会把值包成%value%startswith会拼上^endswith会拼上$。此外generateFilters会用_and包裹整组过滤条件支持field.split(.)形式的嵌套字段如category.title与_and/_or/_not递归组合。五、自定义 GraphQL通过 meta 传入 gqlQuery / gqlMutation当默认生成的操作名或字段集无法满足需求时例如需要嵌套查询关联表、聚合、自定义返回字段Data Provider 允许通过meta.gqlQuery查询和meta.gqlMutation变更直接传入手写的 GraphQL 文档并配合meta.gqlVariables补充额外变量。以仓库示例中的 posts/queries.tsx 为例列表查询手写了GetPosts其中把分页、排序、过滤参数显式声明为变量export const POSTS_QUERY gql query GetPosts( $offset: Int! $limit: Int! $order_by: [posts_order_by!] $where: posts_bool_exp ) { posts( offset: $offset limit: $limit order_by: $order_by where: $where ) { id title content category_id created_at category { id title } } posts_aggregate(where: $where) { aggregate { count } } } ;在 列表页 中通过useTable的meta传入const { tableProps, filters, sorters } useTableGetFieldsFromListGetPostsQuery({ filters: { initial: [{ field: title, operator: contains, value: }] }, meta: { gqlQuery: POSTS_QUERY }, sorters: { initial: [{ field: id, order: asc }] }, });注意这里order_by、where、offset、limit的变量名必须与 provider 自动注入的变量名一致这样useTable的分页、排序、筛选状态才能正确合并进查询源码中通过mergeHasuraFilters合并自定义where与筛选条件见 generateFilters.ts#L183-L234。变更操作同理例如删除按钮传入自定义 mutationlist.tsx#L120-L127DeleteButton hideText sizesmall recordItemId{record?.id} meta{{ gqlMutation: POST_DELETE_MUTATION, }} /对应的POST_DELETE_MUTATION使用$id: uuid!变量这与文档讲解的idType概念直接相关——如果你的posts表主键是Int这里的变量类型也应改为$id: Int!或者更稳妥地交给idType选项统一处理。六、完整 CRUD操作名与 Hasura 语法的对应关系从 dataProvider/index.ts 可以整理出 provider 内部使用的 Hasura 操作命名规则默认hasura-default约定下Refine 方法Hasura 操作关键变量getOne{resource}_by_pkid类型由idType决定getMany{resource}where: { id: { _in: ids } }getList{resource}{resource}_aggregatelimit、offset、order_by、wherecreateinsert_{resource}_oneobject: {resource}_insert_inputcreateManyinsert_{resource}objects: [{resource}_insert_input!]返回returningupdateupdate_{resource}_by_pkpk_columns: { id }、_set: {resource}_set_inputupdateManyupdate_{resource}where_set返回returningdeleteOnedelete_{resource}_by_pkiddeleteManydelete_{resource}where: { id: { _in: ids } }返回returningcustom由meta.operation决定按method生成 query 或 mutation在graphql-default命名约定下这些操作名会统一转为 camelCase如insert_posts_one→insertPostsOne。此外getApiUrl在 Hasura provider 中刻意抛错dataProvider/index.ts#L571-L575因为 GraphQL 场景下通常不需要暴露 REST 风格的 API 地址。七、实时能力liveProvider 与 GraphQL 订阅refinedev/hasura还导出一个liveProvider用于把 Hasura 的 GraphQL 订阅接入 Refine 的useList、useOne、useMany实现数据变更的实时刷新。其实现见 packages/hasura/src/liveProvider/index.ts接收一个graphql-ws的ClientWebSocket 连接subscribe方法根据subscriptionTypeuseList/useOne/useMany选择对应的订阅模板生成器订阅模板生成器位于 utils/generateUseListSubscription.ts 等文件中会复用idType、namingConvention、分页、排序与过滤参数生成 Hasura 风格的订阅查询订阅要求params中必须提供meta、subscriptionType和resource缺失时抛出带提示的错误信息liveProvider/index.ts#L36-L52。在示例 App.tsx 中开启实时能力的方式是创建 WebSocket 客户端后传入liveProvider并在Refine组件上配置liveProvider与options{{ liveMode: auto }}示例中以注释形式保留便于对照启用。八、跑通官方示例仓库中的 examples/data-provider-hasura 是一个基于 Ant Design 的完整 Refine 应用包含posts与categories两个资源覆盖列表含筛选、排序、删除、创建、编辑、详情页以及手写的 GraphQL 查询/变更定义列表页演示useTable与自定义gqlQuery、useSelect关联分类下拉、标题模糊筛选、多选分类筛选创建页演示useForm与自定义gqlMutation配合 Markdown 编辑器录入正文GraphQL 定义集中存放查询与变更文档类型化由 Hasura 生成的 GraphQL 类型定义配合refinedev/hasura导出的GetFields、GetFieldsFromList、GetVariables等类型工具实现端到端类型安全。本地运行该示例的命令来自 示例 READMEnpm create refine-applatest -- --example contenteditable="false">【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考