如何用Prisma/Postgres实现Relay风格的node查询字段?
实现Relay兼容的
node字段方案(Prisma + Yoga + TypeGraphQL) 关于UUID唯一性的问题
- 不需要在Prisma中额外配置全局唯一约束。UUID(v4版本)本身的算法设计就保证了极低的全局冲突概率,只要给每个模型的
id字段定义为:
就足够满足Relay对全局唯一ID的要求。如果实在担心极端情况,可以给每个id String @id @default(uuid())id加@unique约束,但这属于冗余操作,实际项目中很少这么做。
跨模型查询的实现思路
Prisma没有原生的跨表查询API,需要手动遍历所有模型进行查询,找到匹配的记录。具体步骤如下:
1. 编写跨模型查询函数
在Prisma客户端封装一个函数,接收UUID后逐个查询所有模型:
import { PrismaClient } from '@prisma/client'; const prisma = new PrismaClient(); export async function findByIdAcrossModels(id: string) { // 按查询优先级顺序遍历模型,找到第一个匹配的记录 const user = await prisma.user.findUnique({ where: { id } }); if (user) return { __typename: 'User', ...user }; const post = await prisma.post.findUnique({ where: { id } }); if (post) return { __typename: 'Post', ...post }; const comment = await prisma.comment.findUnique({ where: { id } }); if (comment) return { __typename: 'Comment', ...comment }; // 没有找到匹配记录时返回null return null; }
2. 定义Relay的Node接口(TypeGraphQL)
首先定义Node接口,所有需要支持Relay节点查询的模型都要实现这个接口:
import { InterfaceType, Field, ID } from 'type-graphql'; @InterfaceType() export abstract class Node { @Field(() => ID) id: string; }
然后让你的模型类实现这个接口,比如User:
import { ObjectType, Field, ID } from 'type-graphql'; import { Node } from './Node'; @ObjectType({ implements: Node }) export class User implements Node { @Field(() => ID) id: string; @Field() name: string; // 其他字段... }
3. 实现node查询字段
在你的Query resolver中添加node字段,调用上面的跨模型查询函数:
import { Query, Arg, ID } from 'type-graphql'; import { findByIdAcrossModels } from './prisma-utils'; import { Node } from './Node'; import { User } from './User'; import { Post } from './Post'; import { Comment } from './Comment'; @Resolver() export class NodeResolver { @Query(() => Node, { nullable: true }) async node(@Arg('id', () => ID) id: string) { // 若使用Relay推荐的base64编码全局ID,需先解码拆分类型与真实ID // const decoded = Buffer.from(id, 'base64').toString('utf8'); // const [type, rawId] = decoded.split(':'); // 再用rawId执行查询 const result = await findByIdAcrossModels(id); return result; } }
4. 处理Relay全局ID编码(可选但推荐)
Relay推荐使用base64编码的"类型:ID"作为全局唯一ID,前端可直接解析类型信息。你可以在模型的id字段resolver中做编码:
// 在User类中 @Field(() => ID) id: string; @FieldResolver(() => ID) globalId(@Root() user: User) { return Buffer.from(`User:${user.id}`).toString('base64'); }
对应的,在node查询中要先解码得到原始UUID,再执行查询。
性能优化建议
模型较多时,逐个串行查询效率较低,可改用Promise.all并行查询:
export async function findByIdAcrossModels(id: string) { const [user, post, comment] = await Promise.all([ prisma.user.findUnique({ where: { id } }), prisma.post.findUnique({ where: { id } }), prisma.comment.findUnique({ where: { id } }), ]); if (user) return { __typename: 'User', ...user }; if (post) return { __typename: 'Post', ...post }; if (comment) return { __typename: 'Comment', ...comment }; return null; }
内容的提问来源于stack exchange,提问作者Fez Vrasta
相关产品推荐
相关产品推荐

