不同的字段类型取决于 args

Different field types depending on args

我的数据库结构如下:

有个Product table:

id (integer)
manufacture_id (integer)
state (boolean)

还有一个Product_translationstable

product_id (integer)
language_id (integer)
name (string)
description (string)

在查询产品时,如果我提供语言 ID 作为参数,我希望能够直接收到名称和描述,或者收到包含所有语言 ID 和 name/description 的翻译列表相反,如果我不提供语言 ID。

有没有一种方法可以在不创建两个不同类型和两个不同查询的情况下实现这一点?

是也不是。

当您为查询指定 return 类型时(我们称它为 getProduct),您只能指定一种类型(或联合或接口......稍后详细介绍)。该类型 (Product) 将有一个 immutable 字段列表。当您向服务器发出请求时,您将必须识别这些字段的子集才能让服务器 return。考虑到这一点,不可能(至少在本机上)发送查询让服务器 return 根据这些参数有不同的字段子集。

也就是说,您可以做的是定义一个包含所有可能字段的类型,如下所示:

type Product {
  id: ID!
  name: String
  description: String
  translations: [Translation!]!
}

然后在 getProduct 的解析器中,您可以从 table 获取产品,然后检查 language 是否作为参数提供。如果不是,请获取翻译列表并将您的产品的翻译 属性 设置为它。如果提供了语言,则只获取该翻译,用它来填充产品的名称和描述属性,并将翻译设置为空数组。

这样,根据是否将语言作为参数传入,您的 returned 产品将包含 A) 名称和描述为 null 以及填充的翻译列表;或 B) 名称和描述以及用于翻译的空数组。

恕我直言,还有一个更优雅的选择:联合和接口

和以前一样,您需要根据是否存在语言参数来适当地构建 returned 对象。但是,您 return 一个联合体或接口,而不是一个类型,然后利用 __resolveType 字段来 return 一个特定类型(每个都有不同的字段)。

这种方法有两个优点:第一,您可以避免 return 不必要的空字段。第二,如果您使用 Apollo 作为客户端,它会自动添加一个 __typename 字段,您可以在客户端使用该字段轻松确定查询实际 return 的类型。

这里有一个示例,您可以直接插入 Launchpad 进行操作:

import { makeExecutableSchema } from 'graphql-tools';

const typeDefs = `
  type Query {
    getProduct (id: ID, language: ID):  ProductInterface
  },
  type Product  implements ProductInterface {
    id: ID
    translations: [Translation!]!
  },
  type TranslatedProduct implements ProductInterface {
    id: ID
    name: String
    description: String
  },
  type Translation {
    language: ID
    name: String
    description: String
  },
  interface ProductInterface {
    id: ID
  }
`;

const products = [
  {
    id: '1',
    translations: [ 
      {
        language: '100',
        name: 'Foo',
        description: 'Foo!'
      },
      {
        language: '200',
        name: 'Qux',
        description: 'Qux!'
      }
    ]
  }
]

const resolvers = {
  Query: {
    getProduct: (root, {id, language}, context) => {
      const product = products.find(p => p.id === id)
      if (language) {
        product.translation = product.translations.find(t => t.language === language)
      }
      return product
    },
  },
  ProductInterface: {
    __resolveType: (root) => {
      if (root.translation) return 'TranslatedProduct'
      return 'Product'
    }
  },
  TranslatedProduct: {
    name: (root) => root.translation.name,
    description: (root) => root.translation.description
  }
};

export const schema = makeExecutableSchema({
  typeDefs,
  resolvers,
});

然后您可以像这样请求查询:

{
  getProduct (id: "1", language: "200") {
    __typename
    ... on Product {
      translations {
        language
        name
        description
      }
    }
    ... on TranslatedProduct {
      name
      description
    }
  }
}