再学 Supabase 的 GraphQL API
在上一篇文章中我们学习了 Supabase 的 GraphQL API 功能,并通过 Supabase Studio 内置的 GraphiQL IDE 和 curl 简单验证了查询接口。但是,关于 GraphQL API,还有一些点可以展开聊聊,比如如何实现复杂查询,如何实现增删改,如何通过 SDK 调用,等等,我们今天来进一步学习它。
实现复杂查询
下面是一个稍微复杂点的带过滤条件的查询,查询 id=1 的班级:
{
classesCollection(filter: {id: {eq: 1}}) {
edges {
node {
id
name
}
}
}
}其中 classesCollection 表示查询 classes 表,后面的括号里除了加上过滤条件,还可以加上分页、排序等条件。可以在 GraphiQL IDE 右侧的文档中查看 classesCollection 的定义如下:

分页
Supabase 的 GraphQL API 支持两种分页方式:键集分页(Keyset Pagination) 和 偏移分页(Offset Pagination)。键集分页是通过游标实现的,使用 first、last、before 和 after 参数来处理在集合中向前和向后分页,遵循 Relay 的分页规范。
我们首先通过 first:10, after:null 查询表中前 10 条记录:
{
classesCollection(first:10, after:null) {
pageInfo {
startCursor,
endCursor,
hasNextPage
}
edges {
node {
id
name
}
}
}
}其中 pageInfo 表示查询结果中要带上分页信息,分页信息中的 endCursor 是最后一条记录的游标:
{
"data": {
"classesCollection": {
"edges": [
{
"node": {
"id": 1,
"name": "一年级一班"
}
},
...
],
"pageInfo": {
"endCursor": "WzJd",
"hasNextPage": true,
"startCursor": "WzFd"
}
}
}
}我们将 pageInfo.endCursor 赋值到 after 就可以继续查询下一页:
{
classesCollection(first:10, after:"WzJd") {
pageInfo {
startCursor,
endCursor,
hasNextPage
}
edges {
node {
id
name
}
}
}
}偏移分页和传统 SQL 的 limit 和 offset 类似,使用 first 和 offset 参数进行分页,可以跳过结果中的 offset 条记录。下面的查询表示一页 10 条记录,查询第二页:
{
classesCollection(first:10, offset:10) {
...
}
}过滤
filter 参数用于设置过滤条件,它的类型为 classesFilter,定义如下:

我们可以通过 classes 表的每一个字段进行过滤,也可以通过 and/or/not 逻辑操作符组合过滤条件。当我们根据字段进行过滤时,不同的字段类型对应的过滤类是不一样的,支持的过滤操作也不一样,比如 int 类型对应的 IntFilter,string 类型对应 StringFilter,下面的表格列出了常见的过滤操作符:

比如过滤 id 小于 2 的班级:
{
classesCollection(filter:{id:{lt:2}}) {
...
}
}过滤名称以 一年级 开头的班级:
{
classesCollection(filter:{name:{like:"一年级%"}}) {
...
}
}使用 and 将多个条件组合:
{
classesCollection(filter:{
and: [
{name:{like:"一年级%"}}
{id: {lt:10}}
]
}) {
...
}
}也可以简写成:
{
classesCollection(filter:{
name:{like:"一年级%"},
id: {lt:10}
}) {
...
}
}排序
orderBy 参数用于控制排序,它的类型为 classesOrderBy!:

支持四种排序方式:
- AscNullsFirst - 正序,空值靠前
- AscNullsLast - 正序,空值靠后
- DescNullsFirst - 倒序,空值靠前
- DescNullsLast - 倒序,空值靠后
比如下面是按 id 倒序的例子:
{
classesCollection(orderBy: {id: DescNullsLast}) {
...
}
}表间关联
和 RESTful API 一样,Supabase 会自动检测表之间的外键关系,下面是一个关联查询的例子,查询 id=1 的班级下的前 10 个学生:
{
classesCollection(filter: {id: {eq: 1}}) {
edges {
node {
id
name
studentsCollection(first: 10) {
edges {
node {
id
name
}
}
}
}
}
}
}实现增删改
在上一篇文章中我们了解到,GraphQL 支持三种基本操作:
- Query(查询):用于从服务器获取数据,客户端通过发送一个查询语句来请求特定的数据,查询语句是一个树状结构,描述了需要获取的数据字段及其子字段;
- Mutation(变更):用于修改数据,如创建、更新或删除操作,遵循严格的类型定义,负责执行对服务器数据的写操作;
- Subscription(订阅):用于监听数据变化并实现实时更新,允许客户端实时接收数据更新,通常用于实现实时通信功能;
Supabase 的 GraphQL API 支持 Mutation 操作实现数据的创建、更新或删除。打开右侧 Docs 面板,可以看到支持 Mutation 操作:

通过下面的语句往 classes 表新增两条记录:
mutation {
insertIntoclassesCollection (
objects: [
{name: "二年级一班"},
{name: "二年级二班"},
]
) {
affectedCount
records {
id
name
}
}
}通过下面的语句编辑 classes 表中 id=4 的记录:
mutation {
updateclassesCollection (
set: {name: "二年级X班"},
filter: {id: {eq: 4}}
) {
affectedCount
records {
id
name
}
}
}通过下面的语句删除 classes 表中 id=4 的记录:
mutation {
deleteFromclassesCollection (
filter: {id: {eq: 4}}
) {
affectedCount
records {
id
name
}
}
}通过 SDK 调用
Supabase 提供的 SDK,比如 Python SDK 或 JS SDK 等是通过 RESTful API 来实现查询的,要实现 GraphQL API 的调用,可以采用一些 GraphQL JS 框架,比如 Relay 和 Apollo 等。
可参考官方的集成文档:
- Relay - https://supabase.com/docs/guides/graphql/with-relay
- Apollo - https://supabase.com/docs/guides/graphql/with-apollo
深入 Supabase GraphQL API 原理
我们前面曾提到过,Supabase 的 GraphQL API 是基于 Postgres 扩展 pg_graphql 实现的,它会根据数据库架构自动生成 GraphQL 接口。如果更深入一步,我们会发现,这个扩展是通过内置函数 graphql.resolve(...) 来实现 GraphQL 接口的:当我们请求 GraphQL 接口时,实际上调用的是 graphql.resolve(...) 函数。我们可以打开 SQL Editor 输入下面的查询语句验证下:
select graphql.resolve($$
{
classesCollection {
edges {
node {
id
name
}
}
}
}
$$)运行结果如下:

对 pg_graphql 的原理感兴趣的同学,可以看官方这篇博客:https://supabase.com/blog/how-pg-graphql-works

