¿Cómo consultar todos los campos de tipo GraphQL sin escribir una consulta larga?

130

Suponga que tiene un tipo GraphQL e incluye muchos campos. ¿Cómo consultar todos los campos sin escribir una consulta larga que incluya los nombres de todos los campos?

Por ejemplo, si tengo estos 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 los campos, generalmente la consulta es algo como esto:

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

Pero quiero una forma de tener los mismos resultados sin escribir todos los campos, algo como esto:

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

¿Hay alguna manera de hacer esto en GraphQL?

Estoy usando la biblioteca Folkloreatelier / laravel-graphql .

BlackSigma
fuente
44
Estás preguntando cómo hacer algo que GraphQL, por diseño, no admite.
Travis Webb
12
Simplemente escriba esos 40 campos de algo y espero que no haga un error tipográfico :)
Ska
32
Wow, recién estoy comenzando en GraphQL, y este es un WTF serio.
user949300
1
Tiene sentido que no sea compatible, imagina que tienes objetos de Alumno y Clase, el alumno tiene "clases" de campo que enumeran todas las clases a las que asiste, la clase tiene "alumnos" de campo que enumera todos los alumnos que asisten a esa clase. Esa es una estructura cíclica. Ahora, si solicita todos los estudiantes con todos los campos, ¿eso también incluiría todos los campos de clases devueltos? Y esas clases tienen estudiantes, ¿se incluirían también sus campos? Y los estudiantes tienen clases, ...
Buksy
Tenía esta pregunta y fue para poder ver lo que estaba disponible para tirar. Muchos clientes GraphQL (por ejemplo, GraphiQL, ver gatsbyjs.org/docs/running-queries-with-graphiql ) tienen un explorador de esquemas que utiliza la introspección para presentarle lo que puede extraer, si esa es la razón detrás de querer obtener "todo" ".
James hace

Respuestas:

120

Desafortunadamente, lo que te gustaría hacer no es posible. GraphQL requiere que sea explícito al especificar qué campos desea que devuelva su consulta.

Peter Horne
fuente
55
Ok, y si solicito algún objeto de forma desconocida del backend que se supone que debo enviar o devolver.
Meandre
19
@meandre, toda la idea de graphql es que no existe una "forma desconocida".
s.meijer
2
@meandre, ¿Mi respuesta a continuación podría serle útil?
Tyrone Wilson
¿No es la idea completa de la mayoría de los lenguajes y protocolos de consulta API ?, @meandre
Clijsters
interesante, realmente es una mentalidad diferente cuando se usa graphql
andy mccullough
91

Sí, usted puede hacer esto mediante la introspección . Realice una consulta GraphQL como (para el tipo UserType )

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

y obtendrá una respuesta como (los nombres de campo reales dependerán de su esquema real / definición de 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. ...)
      ]
    }
  }
}

Luego puede leer esta lista de campos en su cliente y generar dinámicamente una segunda consulta GraphQL para obtener todos estos campos.

Esto depende de que usted sepa el nombre del tipo para el que desea obtener los campos; si no conoce el tipo, puede obtener todos los tipos y campos juntos mediante la introspección como

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

NOTA: estos son los datos GraphQL over-the-wire: usted está solo para descubrir cómo leer y escribir con su cliente real. Es posible que su biblioteca de JavaScript GraphQL ya emplee la introspección de alguna manera, por ejemplo, el comando codec de Apollo usa la introspección para generar tipos.

Mark Chackerian
fuente
Parece que uno debería expresar preocupación por los tipos recursivos. Si bajó del árbol y tropezó con un tipo que se contiene a sí mismo, de alguna forma (lista, único u otro ...), podría tener una recursión infinita.
Milos Grujic
En realidad, eso no sucede en mi experiencia con esta consulta en particular: la consulta en sí misma define la profundidad de resolución.
Mark Chackerian
La respuesta anterior solo le permite consultar los tipos de campos disponibles en una consulta. No devuelve todos los "valores" de los campos de objeto, que es de lo que trata la pregunta original.
quantdaddy
44
Según la respuesta, debe generar dinámicamente una segunda consulta basada en los resultados de la primera consulta; lo dejé como un ejercicio para el lector.
Mark Chackerian
39

Supongo que la única forma de hacerlo es mediante la utilización de fragmentos reutilizables:

fragment UserFragment on Users {
    id
    username
    count
} 

FetchUsers {
    users(id: "2") {
        ...UserFragment
    }
}
tommy
fuente
19
Si hice eso, entonces todavía tengo que escribir cada nombre de campo "al menos en el fragmento", pero lo que estaba tratando de evitar, parece que GraphQL nos obliga a ser explícitos.
BlackSigma
¿Cómo agregar esto en una consulta POSTMan? o jquery / UI framwork para hacer un JSON en cadena. Este GraphiQL parece inútil para fines de desarrollo real.
mfaisalhyder
Esto es únicamente para fines de reutilización.
Henok Tesfaye
@BlackSigma Considerando la documentación de GraphQL , esta debería ser la mejor respuesta aceptada
JP Ventura
44
@JPVentura: No, amigo mío, hay una diferencia entre la reutilización y el comodín tanto en concepto como en aplicación. El propósito del fragmento está claro en la documentación "GraphQL incluye unidades reutilizables llamadas fragmentos". Usar fragmentos es útil, pero no es la respuesta a la pregunta.
BlackSigma
11

Enfrenté este mismo problema cuando necesitaba cargar los datos de ubicación que había serializado en la base de datos desde la API de Google Places. En general, me gustaría todo para que funcione con mapas, pero no quería tener que especificar todos los campos cada vez.

Estaba trabajando en Ruby, así que no puedo darle la implementación de PHP, pero el principio debería ser el mismo.

Definí un tipo escalar personalizado llamado JSON que simplemente devuelve un objeto JSON literal.

La implementación de ruby ​​fue así (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

Luego lo usé para nuestros objetos así

field :location, Types::JsonType

Sin embargo, usaría esto con moderación, usándolo solo donde sabe que siempre necesita todo el objeto JSON (como lo hice en mi caso). De lo contrario, está derrotando el objeto de GraphQL en términos más generales.

Tyrone Wilson
fuente
1
Esto es exactamente lo que necesitaba, gracias. Mi caso de uso es que tengo cadenas traducibles por el usuario en todo el sistema, y ​​se almacenan como json en db like {"en": "Hello", "es": "Hola"}. Y dado que cada usuario puede implementar su propio subconjunto de idiomas para su caso de uso, no tiene sentido que la IU consulte cada subconjunto posible. Tu ejemplo funciona perfectamente.
Luke Ehresman
2

El formato de consulta GraphQL fue diseñado para permitir:

  1. Tanto la consulta como la forma del resultado son exactamente iguales .
  2. El servidor conoce exactamente los campos solicitados, por lo que el cliente descarga solo datos esenciales.

Sin embargo, según la documentación de GraphQL , puede crear fragmentos para que los conjuntos de selección sean más reutilizables:

# Only most used selection properties

fragment UserDetails on User {
    id,
    username
} 

Luego, puede consultar todos los detalles del usuario:

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

También puede agregar campos adicionales junto a su fragmento :

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