不同的字段类型取决于args

时间:2017-09-04 19:24:50

标签: graphql graphql-js

我的数据库结构如下:

有一个Product表:

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

还有一个Product_translations

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

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

有没有办法在不创建两个不同类型和两个不同查询的情况下实现此目的?

1 个答案:

答案 0 :(得分:3)

是和否。

当您为查询指定返回类型时(让我们将其称为getProduct),您只能指定一种类型(或者一个联合或接口...稍后会详细介绍)。该类型(Product)将具有不可变的字段列表。向服务器发出请求时,必须确定这些字段的子集以使服务器返回。考虑到这一点,发送查询是不可能的(至少是本地的)服务器根据这些参数返回不同的字段子集。

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

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

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

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

恕我直言,还有一个更优雅的替代方案:联盟和接口

和以前一样,您需要根据语言参数是否存在来适当地构造返回的对象。但是,不是类型,而是返回Union或Interface,然后使用__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
    }
  }
}