Como consultar todos os campos do tipo GraphQL sem escrever uma consulta longa?

130

Suponha que você tenha um tipo GraphQL e ele inclui muitos campos. Como consultar todos os campos sem anotar uma consulta longa que inclua os nomes de todos os campos?

Por exemplo, se eu tiver estes campos:

 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'
            ]

        ];
    }

Para consultar todos os campos, geralmente a consulta é algo como isto:

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

Mas eu quero uma maneira de obter os mesmos resultados sem escrever todos os campos, algo como isto:

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

Existe uma maneira de fazer isso no GraphQL?

Estou usando a biblioteca Folkloreatelier / laravel-graphql .

BlackSigma
fonte
4
Você está perguntando como fazer algo que o GraphQL, por design, não suporta.
Travis Webb
12
Basta digitar esses 40 campos de algo e espero que você não faça um erro de digitação :)
Ska
32
Uau, estou apenas começando no GraphQL, e este é um WTF sério.
user949300
1
Faz sentido que não seja suportado, imagine que você tenha objetos Student e Class, o aluno tenha o campo "classes" que lista todas as aulas que ele assiste, a classe tem o campo "alunos" que lista todos os alunos que assistem a essa aula. Isso é uma estrutura cíclica. Agora, se você solicitar a todos os alunos com todos os campos, isso incluiria também todos os campos das aulas retornados? E essas aulas têm alunos, seus campos também seriam incluídos? E os alunos têm aulas, ...
Buksy
Eu tinha essa pergunta e era para que eu pudesse ver o que estava disponível para puxar. Muitos clientes GraphQL (por exemplo, GraphiQL, consulte gatsbyjs.org/docs/running-queries-with-graphiql ) têm um explorador de esquema que usa a introspecção para apresentar o que você pode obter, se esse é o motivo por trás de querer "tudo" "
James

Respostas:

120

Infelizmente, o que você gostaria de fazer não é possível. O GraphQL exige que você seja explícito sobre a especificação de quais campos você gostaria que retornassem da sua consulta.

Peter Horne
fonte
5
Ok, e se eu solicitar algum objeto de uma forma desconhecida do back-end que devo proxy ou enviar de volta?
30516 meandre
19
@meandre, toda a idéia do graphql é que não existe uma "forma desconhecida".
s.meijer
2
@meandre, Minha resposta abaixo pode ser útil para você?
Tyrone Wilson
Não é toda a idéia da maioria das linguagens de consulta API e protocolos ?, @meandre
Clijsters
interessante, é realmente uma mentalidade diferente ao usar o graphql
andy mccullough 21/03
91

Sim, você pode fazer isso usando a introspecção . Faça uma consulta GraphQL como (para o tipo UserType )

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

e você receberá uma resposta como (os nomes reais dos campos dependerão da sua definição real de esquema / tipo)

{
  "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. ...)
      ]
    }
  }
}

Você pode então ler esta lista de campos em seu cliente e criar dinamicamente uma segunda consulta GraphQL para obter todos esses campos.

Isso depende de você saber o nome do tipo para o qual deseja obter os campos - se você não souber o tipo, poderá reunir todos os tipos e campos usando a introspecção, como

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

OBSERVAÇÃO: esses são os dados do GraphQL sem fio - você está sozinho para descobrir como ler e escrever com o seu cliente real. Sua biblioteca javascript graphQL já pode empregar introspecção em alguma capacidade, por exemplo, o comando apollo codegen usa introspecção para gerar tipos.

Mark Chackerian
fonte
Parece que se deve expressar preocupação com os tipos recursivos. Se você desceu na árvore e colidiu com um tipo que se continha, de alguma forma (lista, única ou outra ..), você poderia ter uma recursão infinita.
Milos Grujic
Na verdade, isso não acontece na minha experiência com essa consulta específica - a própria consulta define a profundidade da resolução.
Mark Chackerian
A resposta acima permite apenas consultar os tipos de campos disponíveis em uma consulta. Ele não retorna todos os campos do objeto "values", que é o objetivo da pergunta original.
precisa saber é o seguinte
4
De acordo com a resposta, você deve criar dinamicamente uma segunda consulta com base nos resultados da primeira consulta - deixei isso como um exercício para o leitor.
Mark Chackerian
39

Eu acho que a única maneira de fazer isso é utilizando fragmentos reutilizáveis:

fragment UserFragment on Users {
    id
    username
    count
} 

FetchUsers {
    users(id: "2") {
        ...UserFragment
    }
}
tommy
fonte
19
Se eu fiz isso, ainda tenho que escrever o nome de cada campo "pelo menos no fragmento", o que eu estava tentando evitar, parece que o GraphQL nos força a ser explícitos.
BlackSigma
como adicionar isso em uma consulta POSTMan? ou jquery / UI framwork para criar um JSON com string. Este graphiQL parece inútil para propósitos reais de desenvolvimento.
Mfaisalhyder 15/05/19
Isso é apenas para fins de reutilização.
Henok Tesfaye
@BlackSigma Considerando documentação GraphQL , este deve ser o aceita como melhor resposta
JP Ventura
4
@JPVentura: Não, meu amigo, existe uma diferença entre reutilização e curinga, tanto no conceito quanto na aplicação. A finalidade do fragmento é clara na documentação "O GraphQL inclui unidades reutilizáveis ​​chamadas fragmentos". Usar fragmento é útil, mas não é a resposta para a pergunta.
BlackSigma 31/10/19
11

Eu enfrentei esse mesmo problema quando precisei carregar os dados de localização serializados no banco de dados a partir da API do Google Places. Geralmente, eu gostaria da coisa toda, para que funcionasse com mapas, mas não queria especificar todos os campos todas as vezes.

Eu estava trabalhando em Ruby, então não posso fornecer a implementação do PHP, mas o princípio deve ser o mesmo.

Eu defini um tipo escalar personalizado chamado JSON que apenas retorna um objeto JSON literal.

A implementação do ruby ​​foi assim (usando graphql-ruby)

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

Então eu usei para nossos objetos assim

field :location, Types::JsonType

Eu usaria isso com moderação, usando-o apenas onde você sabe que sempre precisa de todo o objeto JSON (como eu fiz no meu caso). Caso contrário, ele está derrotando o objeto do GraphQL de maneira mais geral.

Tyrone Wilson
fonte
1
Isso é exatamente o que eu precisava, obrigado. Meu caso de uso é que tenho seqüências traduzíveis pelo usuário em todo o sistema e elas são armazenadas como json no db like {"en": "Hello", "es": "Hola"}. E como cada usuário pode implementar seu próprio subconjunto de idiomas para seu caso de uso, não faz sentido para a interface do usuário consultar todos os subconjuntos possíveis. Seu exemplo funciona perfeitamente.
Luke Ehresman
2

O formato de consulta GraphQL foi projetado para permitir:

  1. As formas de consulta e resultado são exatamente iguais .
  2. O servidor conhece exatamente os campos solicitados, portanto, o cliente baixa apenas os dados essenciais.

No entanto, de acordo com a documentação do GraphQL , você pode criar fragmentos para tornar os conjuntos de seleção mais reutilizáveis:

# Only most used selection properties

fragment UserDetails on User {
    id,
    username
} 

Em seguida, você pode consultar todos os detalhes do usuário:

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

Você também pode adicionar campos adicionais ao lado do seu fragmento :

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