如何在不编写长查询的情况下查询所有GraphQL类型字段?


130

假设您具有GraphQL类型,并且其中包含许多字段。如何在不写下包含所有字段名称的长查询的情况下查询所有字段?

例如,如果我有这些字段:

 public function fields()
    {
        return [
            'id' => [
                'type' => Type::nonNull(Type::string()),
                'description' => 'The id of the user'
            ],
            'username' => [
                'type' => Type::string(),
                'description' => 'The email of user'
            ], 
             'count' => [
                'type' => Type::int(),
                'description' => 'login count for the user'
            ]

        ];
    }

要查询所有字段,通常查询是这样的:

FetchUsers{users(id:"2"){id,username,count}}

但是我想要一种无需编写所有字段即可获得相同结果的方法,如下所示:

FetchUsers{users(id:"2"){*}}
//or
FetchUsers{users(id:"2")}

有没有办法在GraphQL中做到这一点?

我正在使用Folkloreatelier / laravel-graphql库。


4
您在问如何设计使GraphQL不支持的操作。
特拉维斯·韦伯

12
只需输入这40个字段,并希望您不要打错了:)
Ska

32
哇,我才刚开始使用GraphQL,这是一个严肃的WTF。
user949300

1
不支持它是有道理的,假设您有“学生”和“班级”对象,学生有“班级”字段,列出了他参加的所有班级,班级有“学生”字段,列出了参加该班级的所有学生。多数民众赞成在一个周期性的结构。现在,如果您要求所有领域的所有学生,是否还包括返回课程的所有领域?这些班有学生,他们的领域也会包括在内吗?和学生上课,...
Buksy

Answers:


120

不幸的是,您无法做的事情。GraphQL要求您明确指定要从查询中返回哪些字段。


5
好的,如果我要从后端请求某种未知形式的对象以进行代理或发送回去?
Meandre '16

19
@ meandre,graphql的整个思想是不存在“未知形式”之类的东西。
s.meijer

2
@meandre,我的以下回答对您有用吗?
泰隆·威尔逊

这不是大多数API查询语言和协议的全部概念吗?,@ meandre
Clijsters

有趣的是,使用graphql时,的确是另外一种思维方式
andy mccullough

91

是的,您可以使用自省功能。进行GraphQL查询,例如(对于UserType类型

{
   __type(name:"UserType") {
      fields {
         name
         description
      }  
   }
}

然后您会收到类似的响应(实际的字段名称将取决于您的实际架构/类型定义)

{
  "data": {
    "__type": {
      "fields": [
        {
          "name": "id",
          "description": ""
        },
        {
          "name": "username",
          "description": "Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only."
        },
        {
          "name": "firstName",
          "description": ""
        },
        {
          "name": "lastName",
          "description": ""
        },
        {
         "name": "email",
          "description": ""
        },
        ( etc. etc. ...)
      ]
    }
  }
}

然后,您可以在客户端中读取此字段列表,并动态构建第二个GraphQL查询以获取所有这些字段。

这取决于您知道要获取其字段的类型的名称-如果您不知道该类型,则可以使用内省法将所有类型和字段加在一起

{
  __schema {
    types {
      name
      fields {
        name
        description
      }
    }
  }
}

注意:这是在线GraphQL数据-您需要自己弄清楚如何与实际的客户端进行读写。您的graphQL javascript库可能已经以某种方式使用了自省功能,例如apollo codegen命令使用了自省功能来生成类型。


似乎有人应该表达对递归类型的关心。如果您从树上摔下来,碰到包含某种形式(列表,单个或其他..)的自身的类型,则可能会无限次递归。
米洛斯·格鲁吉茨

在我使用此特定查询的经验中,这实际上并没有发生-查询本身定义了解析深度。
Mark Chackerian '19年

上面的答案仅允许您查询查询中可用的字段类型。它不会返回所有对象字段“值”,这就是原始问题所在。
–Quantdaddy

4
按照答案,您必须根据第一个查询的结果动态构建第二个查询-我将其留给读者练习。
Mark Chackerian '19

39

我想做到这一点的唯一方法是利用可重复使用的片段:

fragment UserFragment on Users {
    id
    username
    count
} 

FetchUsers {
    users(id: "2") {
        ...UserFragment
    }
}

19
如果这样做了,那么仍然要“至少在片段中”写每个字段名称,而我一直想避免这种情况,似乎GraphQL迫使我们明确了。
BlackSigma

如何在POSTMan查询中添加此内容?或jquery / UI框架来生成字符串化的JSON。对于实际的开发目的,此graphiQL似乎没有用。
mfaisalhyder

这仅用于重用目的。
Henok Tesfaye,

@BlackSigma考虑到GraphQL文档,这应该是公认的最佳答案
JP Ventura,

4
@JPVentura:没有我的朋友,可重用性和通配符在概念和应用方面都有区别。片段的目的在文档“ GraphQL包含称为片段的可重用单元”中很清楚。使用片段很有用,但不能解决问题。
BlackSigma '19

11

当我需要从Google Places API加载序列化到数据库的位置数据时,我遇到了同样的问题。通常,我希望整个事情都可以在地图上使用,但是我不想每次都指定所有字段。

我当时在Ruby中工作,所以无法为您提供PHP实现,但原理应相同。

我定义了一个名为JSON的自定义标量类型,它仅返回一个文字JSON对象。

ruby的实现就像这样(使用graphql-ruby)

module Graph
  module Types
    JsonType = GraphQL::ScalarType.define do
      name "JSON"
      coerce_input -> (x) { x }
      coerce_result -> (x) { x }
    end
  end
end

然后我将它用于这样的对象

field :location, Types::JsonType

不过,我会非常谨慎地使用它,仅在知道始终需要整个JSON对象的地方使用它(就像我在我的情况下一样)。否则,从更广泛的意义上讲,它将击败GraphQL的对象。


1
这正是我所需要的,谢谢。我的用例是我在整个系统中都有用户可转换的字符串,并且它们以json的形式存储在db中{"en": "Hello", "es": "Hola"}。而且,由于每个用户都可以为其用例实现自己的语言子集,因此UI查询每个可能的子集都没有意义。您的示例效果很好。
路加·埃里斯曼

2

GraphQL查询格式旨在允许:

  1. 查询和结果形状都完全相同
  2. 服务器知道确切的必填字段,因此客户端下载唯一重要的数据。

但是,根据GraphQL文档,您可以创建片段以使选择集更可重用:

# Only most used selection properties

fragment UserDetails on User {
    id,
    username
} 

然后,您可以通过以下方式查询所有用户详细信息:

FetchUsers {
    users() {
        ...UserDetails
    }
}

您还可以在片段旁边添加其他字段

FetchUserById($id: ID!) {
    users(id: $id) {
        ...UserDetails
        count
    }
}

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.