如何抽象executeQuery函数以适配GraphQL解析器的所有返回类型?
解决GraphQL解析器中executeQuery函数返回值类型不匹配问题
我们在GraphQL解析器中封装了executeQuery()函数来减少重复的数据库查询代码,但遇到了返回值类型不匹配的问题:
- 函数原本通过
rowCount判断返回result.rows(多条/空)还是result.rows[0](单条),但当查询结果为空时,会返回null,而部分GraphQL字段(比如Company类型的Plugins: [CompanyPlugin])期望返回空数组[],导致测试失败。
当前executeQuery实现和调用示例如下:
const executeQuery = async <T>( pool: Pool, query: string, values: QueryValues, ): Promise<ExecuteQueryResult<T>> => { const result = await pool.query(query, values) if (result.rowCount != 1 && result.rowCount != 0) { return result.rows } return result.rows[0] }
// 原写法(直接返回rows) async Plugins(obj, _args, ctx: UserContext) { const { text, values } = sql`select * from company_plugins where company_id = ${obj.id}` const result = await ctx.db.query({ text, values }) return result.rows }, // 使用executeQuery后的写法(返回值可能为null,不符合数组类型要求) async Plugins(obj, _args, ctx: UserContext) { const { text, values } = sql`select * from company_plugins where company_id = ${obj.id}` const result = await ctx.executeQuery(text, values) return result },
Company类型定义:
Plugins: [CompanyPlugin]
方案:让调用方显式指定返回类型(单条/列表)
因为GraphQL解析器本身明确知道当前字段需要返回单条数据还是列表,最可靠的方式是给executeQuery增加一个参数,让调用方显式声明返回类型,避免自动判断的歧义。
1. 修改executeQuery函数(带类型重载)
type ReturnTypeOption = 'single' | 'list'; // 函数重载:明确不同返回类型对应的返回值 async function executeQuery<T>(pool: Pool, query: string, values: QueryValues, returnType: 'single'): Promise<T | null>; async function executeQuery<T>(pool: Pool, query: string, values: QueryValues, returnType?: 'list'): Promise<T[]>; // 函数实现 async function executeQuery<T>( pool: Pool, query: string, values: QueryValues, returnType: ReturnTypeOption = 'list' ): Promise<T | T[] | null> { const result = await pool.query(query, values); if (returnType === 'single') { // 单条场景:返回第一条数据或null return result.rows[0] as T | null; } // 列表场景:始终返回数组,空结果时返回[] return result.rows as T[]; }
2. 在解析器中调用时指定返回类型
对于需要返回列表的字段(比如Plugins),指定returnType: 'list':
async Plugins(obj, _args, ctx: UserContext) { const { text, values } = sql`select * from company_plugins where company_id = ${obj.id}`; // 明确指定返回列表类型,空结果会返回[] const result = await ctx.executeQuery<CompanyPlugin>(text, values, 'list'); return result; }
对于需要返回单条数据的字段(比如单个用户查询),指定returnType: 'single':
async User(obj, args, ctx: UserContext) { const { text, values } = sql`select * from users where id = ${args.id}`; // 指定返回单条数据,无结果时返回null const result = await ctx.executeQuery<User>(text, values, 'single'); return result; }
为什么这个方案更优
- 明确性:调用方直接声明返回类型,消除自动判断带来的歧义,代码可读性更强。
- 类型安全:通过TypeScript函数重载,能在编译阶段就保证返回值类型与GraphQL字段定义匹配,减少运行时错误。
- 灵活性:同时支持单条和列表场景,适配不同的解析器需求。
内容的提问来源于stack exchange,提问作者Exodus Reed
相关产品推荐
相关产品推荐

