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

在Apollo TypeScript中为非空字段启用默认解析器的问题

问题描述

使用@graphql-codegen/cli结合Apollo构建GraphQL API,解析器基于TypeScript编写。遇到的问题是:非空字段的子解析器无法触发,因为TypeScript要求主解析器返回所有非空字段的值,导致子解析器完全没机会执行。

当前GraphQL定义:

type Post {
  id: ID!
  body: String!
}
input PostsRequest {
  start: Int
  end: Int
  cursor: Int
}

type PostsResponse {
  posts: [Post!]!
  totalCount: Int!
}

extend type Query {
  posts(request: PostsRequest!): PostsResponse!
}

解析器代码:

export const PostsResolver: Resolvers<{ query: { value: number } }> = {
  Query: {
    posts: (_, { request: { start, end } }, context = { query: { value: 4 } }) => {
      const query = constructQuery(start ?? 0, end ?? 5);

      context.query = query;
      return {
        totalCount: -1,
        posts: [{ id: "-1", body: "you should never see this!" }],
      };
    },
  },
  PostsResponse: {
    posts: (_1, _2, context) => {
      const results = executeQuery(context.query);
      return results.map((result) => ({ id: result.id, body: "you should never see this!" }));
    },
    totalCount: (_1, _2, context) => executeCount(context.query),
  },
  Post: {
    body: (post) => getPostBody(post.id),
  },
};

现在所有Post的body都返回占位文本"you should never see this!",而非getPostBody()的结果。


解决方案

1. 核心问题:提前返回子字段导致解析器跳过

Apollo的解析逻辑是:如果父解析器已经返回了某个字段的具体值,就不会再调用该字段对应的子解析器。你在PostsResponse.posts和Query.posts的返回值中都手动设置了body字段,导致Post.body解析器完全没机会执行。

解决方法:只返回Post的标识字段(id),去掉手动设置的body
修改后的解析器代码:

export const PostsResolver: Resolvers<{ query: { value: number } }> = {
  Query: {
    posts: (_, { request: { start, end } }, context = { query: { value: 4 } }) => {
      const query = constructQuery(start ?? 0, end ?? 5);

      context.query = query;
      return {
        totalCount: -1,
        posts: [{ id: "-1" }], // 仅返回id
      };
    },
  },
  PostsResponse: {
    posts: (_1, _2, context) => {
      const results = executeQuery(context.query);
      return results.map((result) => ({ id: result.id })); // 仅返回id
    },
    totalCount: (_1, _2, context) => executeCount(context.query),
  },
  Post: {
    body: (post) => getPostBody(post.id),
  },
};

2. 处理TypeScript非空字段报错

修改后TypeScript会报错,因为Post类型要求body为非空字符串,但你返回的对象只有id。可以通过以下两种方式解决:

方式一:使用类型断言

在返回对象时添加类型断言,告诉TypeScript这是合法的Post类型:

return results.map((result) => ({ id: result.id }) as Post);

方式二:调整GraphQL Codegen配置(更优雅)

在codegen.ts中配置自定义映射类型,允许解析器返回仅包含必要字段的对象:

import type { CodegenConfig } from '@graphql-codegen/cli';

const config: CodegenConfig = {
  // 其他基础配置(schema、documents等)
  generates: {
    './src/generated/resolvers-types.ts': {
      plugins: ['typescript', 'typescript-resolvers'],
      config: {
        // 配置自定义映射,让Post解析器的父对象可以是仅含id的类型
        mappers: {
          Post: '../types#PostResolverParent',
        },
      },
    },
  },
};

export default config;

然后在src/types.ts中定义映射类型:

export type PostResolverParent = { id: string };

重新生成类型后,TypeScript就会允许解析器返回仅包含id的对象,同时不会破坏类型安全。


内容的提问来源于stack exchange,提问作者AMWJ

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 20:50:17