- 后端
- API网关
【免费下载链接】crystal
🔮 Graphile's Crystal Monorepo; home to Grafast, PostGraphile, pg-introspection, pg-sql2 and much more!
本指南聚焦 PostGraphile 的核心能力之一:基于数据库内被检视(introspected)的表与列,自动向生成的 GraphQL Schema 中注入一系列元素——包括表类型、列字段、关系字段、全局唯一标识、CRUD Mutations 与根查询字段。读完本文,你将掌握 PostGraphile 对一张 PostgreSQL 表的完整"翻译"规则、每个自动生成元素背后的插件与 inflector 机制,以及如何通过权限(RBAC)与 behaviors 精确控制这些元素是否出现在最终 Schema 中。
一、表驱动的 Schema 生成总览
PostGraphile 会在启动时对配置的 PostgreSQL schema 进行 introspection(结构检视),并把检视到的表、列、约束、外键等信息映射为 GraphQL 类型与字段。以下面这张典型的users表为例:
create table app_public.users ( id serial primary key, username citext not null unique, name text not null, about text, organization_id int not null references app_public.organizations on delete cascade, is_admin boolean not null default false, created_at timestamptz not null default now(), updated_at timestamptz not null default now() );对这样一张表,PostGraphile 会依次生成:
| 目标位置 | 生成内容 |
|---|---|
| 新增 GraphQL 类型 | 名为User的对象类型(UpperCamelCase 且单数化),并为每个列添加 camelCase 字段(id、username、about、organizationId、isAdmin、createdAt、updatedAt等);若表有主键则额外添加nodeId全局唯一标识字段 |
| 类型内关系字段 | 由外键推导出的前向关系字段,例如organizationByOrganizationId |
| 相关表类型 | 反向关系字段,例如在Organization类型上添加usersByOrganizationId |
根Mutation类型 | 针对该表的 CRUD Mutations(Create / Update / Delete) |
根Query类型 | allUsers连接字段(支持分页、过滤、排序);每个唯一约束对应一个userByKey(key: ...)字段(如userById、userByUsername);以及按nodeId取行的user(nodeId: ID!)字段 |
注意:上述
organizationByOrganizationId、usersByOrganizationId这类冗长命名,可以通过加载@graphile/simplify-inflection插件来简化(详见本文"简化关系命名"一节)。
二、表到 GraphQL 类型:命名与列字段
PostGraphile 将表名转换为 GraphQL 类型名时使用UpperCamelCase + 单数化规则(对应 inflector:tableType):users→User。列名则被转换为camelCase(organization_id→organizationId、created_at→createdAt),列字段的类型由其 PostgreSQL 数据类型映射而来。
在源码层面,这套机制由graphile-build-pg中的PgBasicsPlugin、PgTablesPlugin、PgAttributesPlugin等插件协同完成,它们被统一编排进 PostGraphile 的默认 preset 中。你可以在本仓库的 amber preset 定义 中看到这些插件的完整加载顺序:
export const orderedPlugins: GraphileConfig.Preset = { plugins: [ QueryQueryPlugin, PgBasicsPlugin, PgCodecsPlugin, PgTypesPlugin, PgIntrospectionPlugin, PgTablesPlugin, AddNodeInterfaceToSuitableTypesPlugin, NodePlugin, PgAllRowsPlugin, PgRowByUniquePlugin, PgAttributesPlugin, MutationPayloadQueryPlugin, PgRelationsPlugin, PgMutationCreatePlugin, PgMutationUpdateDeletePlugin, PgCustomTypeFieldPlugin, NodeAccessorPlugin, // ... ], };从代码结构可以看出:PgTablesPlugin负责把检视到的表注册为可用的数据资源,PgAttributesPlugin负责把列展开为类型字段,PgRelationsPlugin负责关系字段,而PgAllRowsPlugin、PgRowByUniquePlugin、NodeAccessorPlugin则分别对应根查询上的allUsers、userByKey与user(nodeId:)字段。
三、nodeId:全局唯一对象标识
只要表包含主键,PostGraphile 就会为其类型添加一个nodeId(在新版 amber preset 中表现为id)字段,遵循 GraphQL Global Object Identification Specification(即业界常说的 Relay 全局对象标识规范)。这为客户端提供了一种不依赖具体业务键的、跨类型稳定的对象寻址方式。
不同 preset 对全局唯一标识的处理有所差异:
postgraphile/presets/amber(默认):给每个带主键的表分配全局唯一标识,并将其暴露为名为id的属性;若表本身已有一个名为id的列,则把该列重命名为rowId以避免冲突;postgraphile/presets/relay:彻底隐藏裸主键,在整个 Schema(包括查询、变更、过滤、函数入参)中统一使用全局对象标识;- V4 preset:沿用 PostGraphile V4 的
nodeId命名习惯。
关于id/rowId/nodeId的取舍、specFromNodeId()解码辅助函数以及在函数入参中使用@argNvariant nodeId的完整讨论,请参阅 node-id 文档。
四、关系字段的自动发现
PostGraphile 通过检视表上的外键约束来自动发现关系,并在 Schema 中注入两类字段:
- 前向关系:在当前类型上,为每个外键添加指向目标表的字段(如
User.organizationByOrganizationId),命名规则为"目标类型 + 源字段"的 camelCase 组合(inflectors:singleRelationByKeys、singleRelationByKeysBackwards、manyRelationByKeys); - 反向关系:在目标表类型上添加指向当前表的字段(如
Organization.usersByOrganizationId)。
一对多、多对一、一对一关系都会被自动识别;多对多关系通常需要借助社区插件或通过返回setof的计算列处理,详见 relations 文档。非唯一约束暴露为支持分页、过滤 与排序的 connection;唯一约束则直接暴露表类型本身。
简化关系命名
默认的关系字段名(如organizationByOrganizationId)虽然无歧义,但较为冗长。文档中特别提醒:这些字段可以通过加载@graphile/simplify-inflection插件来简化,例如把organizationByOrganizationId简化为organization、把usersByOrganizationId简化为users。
五、CRUD Mutations 的自动生成
对于具备相应数据库权限的表,PostGraphile 会自动在根Mutation类型上生成 CRUD Mutations("Create / Read / Update / Delete" 中的 CUD 部分)。以上述users表为例,典型会得到:
createUser—— 创建单个User;updateUser/updateUserById/updateUserByUsername—— 通过全局唯一 id 或唯一键更新单个User;deleteUser/deleteUserById/deleteUserByUsername—— 通过全局唯一 id 或唯一键删除单个User。
update与delete类 Mutation 仅在表包含primary key列时才会生成;createMutation 则不受此限制。源码层面,这些能力来自PgMutationCreatePlugin与PgMutationUpdateDeletePlugin(见 amber preset)。如果你想禁用 CRUD Mutations(例如改为全部使用自定义 mutation),可以在 preset 中设置schema.defaultBehavior: "-insert -update -delete";更多设计建议与排查指南(如"mutation 没出现"的常见原因)请参考 crud-mutations 文档。
六、Query 类型:allUsers、userByKey 与 user(nodeId)
在根Query类型上,PostGraphile 为每张表生成三类查询字段:
type Query implements Node { allUsers( first: Int last: Int offset: Int before: Cursor after: Cursor orderBy: [UsersOrderBy!] = [PRIMARY_KEY_ASC] condition: UserCondition ): UsersConnection userById(id: Int!): User userByUsername(username: String!): User user(nodeId: ID!): User }allUsers:返回一个UsersConnection,内置游标分页(first/last/before/after)、偏移分页(offset)、排序(orderBy)与条件过滤(condition),默认按主键升序(PRIMARY_KEY_ASC)排列(inflector:allRows,由PgAllRowsPlugin提供);userById/userByUsername:表上每个唯一约束对应一个userByKey(key: ...)字段,用唯一键直接取单行(inflector:rowByUniqueKeys,由PgRowByUniquePlugin提供);user(nodeId: ID!):按全局唯一nodeId取单行(由NodeAccessorPlugin提供)。
连接(connection)遵循 Relay 游标连接规范,并附加了totalCount、nodes(跳过 edge 包装)、PageInfo.startCursor/endCursor等增强特性;如果你更偏好简单列表而非连接,可以通过 behaviors 配置defaultBehavior: "-connection +list"来切换(详见 connections 文档 与 behavior 文档)。
七、权限:PgRBACPlugin 与 Schema 最小化
如果你使用PgRBACPlugin(在非makeV4Preset()场景下默认启用),PostGraphile 只会暴露你实际拥有权限的表、列与字段。例如执行:
GRANT UPDATE (username, name) ON users TO graphql_visitor;那么生成的updateUserMutation 将只接受username和name字段——其余列不会出现在输入类型中。
PgRBACPlugin的工作机制是:检视数据库中的 RBAC(GRANT / REVOKE)权限,并将其映射进 GraphQL Schema。需要强调的是,按照 GraphQL 最佳实践,它仍然只生成一个统一的 Schema(而非按用户分别生成),具体做法是:以连接字符串中使用的 PostgreSQL 用户为起点,遍历该用户在数据库内可以变身为(become)的所有角色,取这些角色权限的并集作为 Schema 的暴露边界。
你可以通过pgService.pgSettingsForIntrospection对象影响检视阶段使用的设置。该配置项在源码中由 dataplan-pg 的makePgService解析并挂载到PgServiceConfiguration上(pgSettingsForIntrospection会随 introspection 连接一并发送),相关类型声明位于 dataplan-pg 的接口定义。
使用PgRBACPlugin是官方推荐做法,因为它能产出精简得多的 Schema——不包含你实际上无法使用的功能,从而降低 Schema 体积与误用风险。
关于列级 SELECT 授权的建议
文档中有一则重要提示:强烈不建议对列使用基于列的SELECTGRANT(详见 requirements 文档)。更推荐的做法是:把不同的权限关注点拆分到独立的表中,再通过一对一关系进行关联,从而让 RBAC 以表为粒度自然生效。
八、Unlogged 表:默认不暴露
如果数据库中存在通过CREATE UNLOGGED TABLE创建的unlogged 表(其数据不写入 WAL 日志,崩溃后不保证持久),PostGraphile 默认不会将其加入 GraphQL Schema。你可以在本仓库的测试 Schema 中看到相关示例——kitchen-sink-schema.sql 定义了一张c.unlogged表用于验证这一行为。
如需覆盖该默认行为,可以显式地通过 smart tags 或类似方式,为这张 unlogged 表赋予所需的 behaviors,从而使其重新进入 Schema。
九、延伸阅读
围绕"表自动映射"这一主题,以下仓库文档与本指南直接相关,可作为下一步深入的方向:
- relations:外键如何驱动关系字段,含一对多、多对多示例;
- connections:连接的分页语义、
totalCount/nodes增强与列表切换; - filtering:
condition参数、索引约束与高级过滤插件; - crud-mutations:CRUD Mutation 的字段清单、示例与排查清单;
- node-id:全局唯一标识的 preset 差异与解码方式;
- behavior 与 smart-tags:通过 behaviors 与智能标签精确控制每个元素的暴露与形态。
- 后端
- API网关
【免费下载链接】crystal
🔮 Graphile's Crystal Monorepo; home to Grafast, PostGraphile, pg-introspection, pg-sql2 and much more!
相关推荐
PostGraphile 表驱动的 GraphQL Schema 生成指南:从 PostgreSQL 表到自动化的查询、连接与 CRUD
PostGraphile 表驱动的 GraphQL Schema 生成指南:从 PostgreSQL 表到自动化的查询、连接与 CRUD PostGraphil
后端API网关PostGraphile v4 枚举(Enums)完全指南:从 PostgreSQL 类型映射到枚举表、Domain 与 Schema 扩展
PostGraphile v4 枚举(Enums)完全指南:从 PostgreSQL 类型映射到枚举表、Domain 与 Schema 扩展 导读 本篇指南聚焦
后端API网关PostGraphile 5 完全指南:从 PostgreSQL 自动生成高性能、可深度定制的 GraphQL API
PostGraphile 5 完全指南:从 PostgreSQL 自动生成高性能、可深度定制的 GraphQL API PostGraphile 是 Graph
后端API网关
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考