是和否。
当您为查询指定返回类型时(我们称之为getProduct),您只能指定一种类型(或者联合或接口……稍后会详细介绍)。该类型 (Product) 将具有不可变的字段列表。当您向服务器发出请求时,您必须识别这些字段的子集才能让服务器返回。考虑到这一点,不可能(至少在本机上)发送查询让服务器根据这些参数返回不同的字段子集。
也就是说,您可以做的是定义一个包含所有可能字段的类型,如下所示:
type Product {
id: ID!
name: String
description: String
translations: [Translation!]!
}
然后在 getProduct 的解析器中,您可以从表中获取产品,然后检查是否提供了 language 作为参数。如果不是,请获取翻译列表并将产品的翻译属性设置为它。如果提供了语言,则仅获取该翻译,使用它来填充产品的名称和描述属性,并将翻译设置为空数组。
这样,根据语言是否作为参数传入,您返回的产品将包含 A) 名称和描述为 null 以及填充的翻译列表;或 B) 名称和描述以及用于翻译的空数组。
恕我直言,还有一个更优雅的替代方案:联合和接口。
和以前一样,您需要根据语言参数是否存在来适当地构造返回的对象。但是,您返回的不是类型,而是联合或接口,然后利用 __resolveType 字段返回特定类型(每个类型都有不同的字段)。
这种方法有两个优点:第一,您可以避免返回不必要的空字段。第二,如果您使用 Apollo 作为客户端,它会自动添加一个 __typename 字段,您可以在客户端使用该字段来轻松确定查询实际返回的类型。
这是一个您可以直接插入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
}
}
}