You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何抽象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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 04:32:43