使用GraphQL代码生成器从解析器返回不完整的形状

问题描述

我使用以下约定让解析器返回部分数据,并允许其他解析器填写缺少的字段:

type UserExtra {
  name: String!
}

type User {
  id: ID!
  email: String!
  extra: UserExtra!
}

type Query {
  user(id: ID!): User!
  users: [User!]!
}
const getUser = (id: string): { id: string,email: string,extra: { name: string } => fetchUser(id);

// `fetchUsers` only returns `id` and `email`,but not `extra`
const getUsers = (): { id: string,email: string }[] => fetchUsers();

// we can use this function to fetch the extra field for a given user
const getUserExtra = (id: string): { name: string }  => fetchUserExtra();

export default {
  Query: {
    user: (parent,args) => getUser(args.id),users: () => getUsers(),},User: {
    // here we fetch the `extra` field anytime an `User` is requested
    // in real-life I would check if the query is requesting the `extra`
    // field or not,and only fetch the data if requested by the query
    extra: (parent) => {
      return getUserExtra(parent.id)
    },}
}

我遇到的问题是GraphQL代码生成生成一个Resolver类型,该类型期望Query#users返回完整的User形状,当然,它不知道即使我从Query#users返回了部分形状,但由于有了User#extra,客户仍然可以收到期望的形状。

在使TS满意的情况下处理这种情况的最佳方法是什么?

解决方法

当我遇到这种情况时,我将extra字段设为可空(将extra: UserExtra!替换为extra: UserExtra)。关于如何在Graphql模式中处理可空性的文章很多(Thisthis对我来说是有影响力的两篇文章。)

大概,extra字段在不同的解析器中分隔开,因为您必须做一些额外的工作来获取它们,例如从另一个服务或数据存储中请求数据。如果该请求最终失败,则架构最好将类型声明为可为空,以便其余用户数据仍在extra设置为null的情况下返回,而不是将其他user数据丢弃extra为null并违反架构类型。 this article中的Non-null fields mean small failures have an outsized impact很好地解释了这个问题。折衷方案是,然后您的客户端代码需要检查extra是否为空,但是有人可能认为这迫使您的客户端代码考虑更优雅地处理可能的故障案例,这是一件好事。

此更改还可以解决您要解决的原始问题,因为extra将是生成的graphql-code-generator类型中的可选类型,不需要您的主用户解析器返回。