Key Takeaways
Según un informe de Gartner, más de El 50% de las empresas utilizarán GraphQL en producción para 2025, frente a menos del 10% en 2021.
GraphQL, inicialmente desarrollado por Facebook y ahora respaldado por una comunidad global de desarrolladores y organizaciones, es un estándar de API moderno diseñado para mejorar la eficiencia de la obtención y manipulación de datos. Como tecnología de código abierto del lado del servidor, sirve como lenguaje de consulta y como motor de ejecución para interactuar con las API.
Sistemas operativos de GraphQL
GraphQL es un lenguaje versátil de consulta y manipulación de código abierto para API que funciona a la perfección en todas las plataformas. Es compatible con servidores escritos en una amplia gama de lenguajes de programación, incluidos Java, Python, C#, PHP, R, Haskell, JavaScript, Perl, Ruby, Scala, Go, Erlang, Clojure, Elixir y muchos otros. Esto lo hace compatible con prácticamente cualquier lenguaje o marco de programación, lo que garantiza una amplia aplicabilidad y flexibilidad.
Consultas de GraphQL
Estas son algunas consultas de GraphQL y sus respuestas para una mejor comprensión
- Entrada para campos específicos del objeto
En esencia, GraphQL le permite solicitar campos específicos a los objetos. Por ejemplo, consideremos el campo de héroe definido en el tipo de consulta del esquema Referencia de diseño para: https://graphql.org/learn/queries/
type Query { hero: Character}
- Consulta
Cuando se consulta, puede tener este aspecto
{ hero { name }}
- Respuesta
{"data": {"hero": {"name": "A2-H2"}}}
Los documentos de GraphQL siempre comienzan con un tipo de operación raíz, como el tipo de consulta en este caso, que actúa como punto de entrada a la API. A partir de ahí, defines un conjunto de selección especificando los campos que deseas, hasta sus valores escalares o de hoja de enumeración. En este ejemplo, el campo de nombre, que es una cadena, obtiene el nombre del héroe, «A2-H2». La especificación de GraphQL garantiza que las respuestas se devuelvan en una clave de datos de nivel superior, mientras que los errores, si los hay, se detallan en una clave de errores de nivel superior. Es importante destacar que la estructura de respuestas refleja la estructura de consultas, que es una característica fundamental de GraphQL, ya que garantiza que el cliente reciba siempre resultados predecibles y que el servidor sepa con precisión qué datos se solicitan. En el ejemplo anterior, solo se consultó el nombre del héroe. Sin embargo, GraphQL también admite campos anidados, lo que te permite obtener datos relacionados dentro de la misma consulta
- Consulta
Copiar código
{hero {name friends {name}}}
- Respuesta
{ "data": {
"hero": {
"name": "A2-H2",
"friends": [
{ "name": "Adam Oliver" },
{ "name": "James Theodore" },
{ "name": "Elijah Mathew" }
]}}}
En este caso, el campo de amigos devuelve una lista de objetos y la consulta especifica el campo de nombre de cada amigo. GraphQL permite a los clientes recorrer objetos relacionados y obtener una gran cantidad de datos en una sola solicitud, lo que evita las múltiples llamadas que suelen necesitar las API REST tradicionales. Además, ya sea que el campo devuelva un solo objeto o una lista de objetos, la sintaxis de la consulta sigue siendo la misma y el esquema define qué tipo de datos esperar. Esta flexibilidad hace que GraphQL sea altamente eficiente para obtener datos relacionados y anidados.
- Argumentos
Si bien recorrer objetos y sus campos ya es una característica poderosa de GraphQL, su verdadero potencial reside en su capacidad de aceptar argumentos para los campos, lo que permite una obtención de datos aún más dinámica y flexible. Por ejemplo, considere el siguiente esquema
type Query { droid(id: ID!): Droid}
En este caso, el campo droide requiere un argumento id para especificar qué datos del droide se van a buscar. Un cliente puede consultarlo de la siguiente manera
- Consulta
{
human(id: "1000") {
name
height
}
}
- Respuesta
{
"data": {
"human": {
"name": "Adam Oliver",
"height": 1.72
}
}
}
A diferencia de las API REST, en las que los argumentos suelen limitarse a los parámetros de consulta y a los segmentos de URL, GraphQL te permite pasar argumentos a cualquier campo u objeto anidado. Esto elimina la necesidad de realizar varias llamadas a la API, ya que cada campo de una sola consulta de GraphQL puede aceptar su propio conjunto de argumentos. GraphQL también admite argumentos para los campos que devuelven tipos escalares, lo que permite operaciones como las transformaciones de datos del lado del servidor. Por ejemplo,
- Consulta
{
human(id: "1000") {
name
height(unit: FOOT)
}
}
- Respuesta
{
"data": {
"human": {
"name": "Adam Oliver",
"height": 5.5467791
}
}
}
En este ejemplo, el argumento unit, de tipo Enum, especifica la unidad de medida deseada para el campo de altura. GraphQL admite varios tipos de argumentos, incluidos los personalizados definidos por el servidor, siempre que se puedan serializar en el formato de transporte. Esta flexibilidad permite a los desarrolladores implementar una potente lógica de recuperación y transformación de datos directamente en la API, lo que agiliza las operaciones del lado del cliente. Puedes consultar nuestro artículo sobre GraphQL frente a REST para obtener una guía detallada.(GraphQL vs REST: enlace al artículo que se agreará una vez publicado)
- Nombre y hora de la operación
En los ejemplos anteriores, utilizamos una sintaxis simplificada que omite la palabra clave query antes del conjunto de selección. Sin embargo, GraphQL permite definir explícitamente el tipo de operación y asignar un nombre único a la operación, lo que puede resultar especialmente útil para la depuración y el rastreo en entornos de producción. Por ejemplo, consideremos este ejemplo en el que la palabra clave de consulta se usa de forma explícita y la operación se denomina HeroNameAndFriends
- Consulta
query HeroNameAndFriends {
hero {
name
friends {
name
}
}
}
- Respuesta
{
"data": {
"hero": {
"name": "A2-H2",
"friends": [
{ "name": "Adam Oliver" },
{ "name": "James Theodore" },
{ "name": "Elijah Mathew" }
]
}
}
}
El tipo de operación (consulta, mutación o suscripción) define la intención de la operación. Por ejemplo, la consulta se usa para obtener datos, la mutación para realizar cambios y la suscripción para obtener actualizaciones en tiempo real. Si bien la palabra clave de consulta es opcional para las consultas sencillas, es obligatoria para las mutaciones y las suscripciones. Es muy recomendable añadir un nombre a la operación, como HeroNameAndFriends, incluso para operaciones individuales. Este nombre aporta claridad, ayuda a depurar errores y simplifica el registro en el servidor al facilitar la identificación de solicitudes específicas. Los nombres de las operaciones se vuelven esenciales cuando se incluyen varias operaciones en un único documento de GraphQL, ya que ayudan a distinguirlas. Piense en los nombres de las operaciones como los nombres de las funciones en la programación. Si bien las funciones anónimas son posibles, nombrar una función facilita la depuración, el registro y el mantenimiento. Del mismo modo, los nombres significativos para las consultas, mutaciones o fragmentos de GraphQL pueden mejorar significativamente la capacidad de mantenimiento y la trazabilidad de las interacciones de las API.
- Alias
En GraphQL, los campos de resultados de la respuesta coinciden con los nombres de los campos de la consulta. Sin embargo, dado que los argumentos no se reflejan en los campos de respuesta, no es posible consultar directamente el mismo campo con diferentes argumentos. Aquí es donde entran en juego los alias: permiten asignar nombres personalizados a los resultados de los campos, lo que evita conflictos y permite múltiples variaciones en una sola consulta.
- Consulta
query {
empireHero: hero(episode: EMPIRE) {
name
}
jediHero: hero(episode: JEDI) {
name
}
}
- Respuesta
{
"data": {
"empireHero": {
"name": "Adam Oliver"
},
"jediHero": {
"name": "A2-H2"
}
}
}
En este ejemplo, el campo héroe se consulta dos veces con argumentos diferentes. Sin alias, la consulta generaría un conflicto porque los campos de respuesta compartirían el mismo nombre. Al usar alias como EmpireHero y JediHero, ambas consultas pueden coexistir en una sola solicitud y la respuesta permanece clara y bien estructurada
- Variables
En muchas aplicaciones, los argumentos para las consultas de GraphQL son dinámicos. Codificar estos argumentos dinámicos directamente en la cadena de consulta no es lo ideal, ya que requeriría que el código del lado del cliente modificara la cadena de consulta en tiempo de ejecución y la formateara para GraphQL. En cambio, GraphQL ofrece una solución más elegante a través de variables. Las variables permiten separar los valores dinámicos de la propia consulta y pasarlos como un diccionario distinto. Para usar variables en una consulta, debe seguir estos pasos
- Sustituya el valor estático de la consulta por $variableName.
- Declare $variableName en la consulta como una variable aceptada.
- Pase variableName: value dentro de un diccionario independiente específico para el transporte, a menudo formateado como JSON.
He aquí un ejemplo
- Consulta
query HeroNameAndFriends($episode: Episode) {
hero(episode: $episode) {
name
friends {
name
}
}
}
- Variables
{
"episode": "JEDI"
}
- Respuesta
{
"data": {
"hero": {
"name": "A2-H2",
"friends": [
{ "name": "Adam Oliver" },
{ "name": "James Theodore" },
{ "name": "Elijah Mathew" }
]
}
}
}
Al usar variables, debes definir un tipo de operación y un nombre en el documento de GraphQL. Este enfoque elimina la necesidad de crear una nueva consulta para cada valor dinámico, lo que hace que el código de tu cliente sea más limpio y reutilizable. También garantiza una mejor seguridad y estructura al evitar la inserción de valores proporcionados por el usuario directamente en las cadenas de consulta.
- Variables predeterminadas
En GraphQL, puedes asignar valores predeterminados a las variables directamente dentro de la consulta especificando el valor predeterminado después de la declaración de tipo. Por ejemplo
- Consulta con valores predeterminados
query HeroNameAndFriends($episode: Episode = JEDI) {
hero(episode: $episode) {
name
friends {
name
}
}
}
Cuando se proporcionan valores predeterminados para todas las variables, la consulta se puede ejecutar sin pasar ninguna variable externa. Sin embargo, si las variables se proporcionan a través del diccionario de variables, anularán los valores predeterminados.
- Fragmentos
En los casos en los que una consulta implica estructuras de campo repetitivas, como comparar a dos héroes uno al lado del otro con sus amigos, la consulta puede resultar innecesariamente larga y repetitiva. Para solucionar este problema, GraphQL proporciona fragmentos, conjuntos de campos reutilizables que se pueden incluir en las consultas siempre que sea necesario.
- Ejemplo con fragmentos
query {
leftComparison: hero(episode: EMPIRE) {
...comparisonFields
}
rightComparison: hero(episode: JEDI) {
...comparisonFields
}
}
fragment comparisonFields on Character {
name
appearsIn
friends {
name
}
}
- Respuesta
{
"data": {
"leftComparison": {
"name": "Adam Oliver",
"appearsIn": ["NEWHOPE", "EMPIRE", "JEDI"],
"friends": [
{ "name": "James Theodore" },
{ "name": "Elijah Mathew" },
{ "name": "C-3PO" },
{ "name": "A2-H2" }
]
},
"rightComparison": {
"name": " A2-H2",
"appearsIn": ["NEWHOPE", "EMPIRE", "JEDI"],
"friends": [
{ "name": "Adam Oliver" },
{ "name": "James Theodore" },
{ "name": "Elijah Mathew" }
]
}
}
}
Al usar fragmentos, se evita la redundancia y se simplifican las consultas. En este ejemplo, el fragmento ComparisonFields agrupa los campos compartidos, que se pueden reutilizar tanto para LeftComparison como para RightComparison. Los fragmentos son especialmente útiles para gestionar consultas complejas y combinar los requisitos de datos de varios componentes de la interfaz de usuario en una consulta única y coherente.
- Uso de variables dentro de fragmentos
Los fragmentos de GraphQL también pueden utilizar variables definidas en la operación principal. Esto te permite crear fragmentos reutilizables que se adaptan dinámicamente en función de las variables pasadas a la operación.
- Ejemplo de consulta con variables en fragmentos(Se necesita creatividad para que los textos de Block.Texts se agreguen tal como están en una caja)
query HeroComparison($first: Int = 3) {
leftComparison: hero(episode: EMPIRE) {
...comparisonFields
}
rightComparison: hero(episode: JEDI) {
...comparisonFields
}
}
fragment comparisonFields on Character {
name
friendsConnection(first: $first) {
totalCount
edges {
node {
name
}
}
}
}
- Respuesta
{
"data": {
"leftComparison": {
"name": "Adam Oliver",
"friendsConnection": {
"totalCount": 4,
"edges": [
{ "node": { "name": "James Theodore" } },
{ "node": { "name": "Elijah Mathew" } },
{ "node": { "name": "C-3PO" } }
]
}
},
"rightComparison": {
"name": " A2-H2",
"friendsConnection": {
"totalCount": 3,
"edges": [
{ "node": { "name": "Adam Oliver" } },
{ "node": { "name": "James Theodore" } },
{ "node": { "name": "Elijah Mathew" } }
]
}
}
}
}
En esta consulta, la variable $first controla el número de amigos obtenidos tanto para LeftComparison como para RightComparison mediante el campo FriendsConnection. Esto garantiza una lógica coherente en varias consultas sin necesidad de codificar los valores en el fragmento.
- Fragmentos en línea
GraphQL admite la consulta de campos en los tipos de interfaz o unión mediante el uso de fragmentos en línea para acceder a los campos específicos del tipo concreto subyacente.
- Ejemplo de consulta con fragmentos en línea
query HeroForEpisode($ep: Episode!) {
hero(episode: $ep) {
name
... on Droid {
primaryFunction
}
... on Human {
height
}
}
}
- Variables
{
"ep": "JEDI"
}
- Respuesta
{
"data": {
"hero": {
"name": " A2-H2",
"primaryFunction": "Astromech"
}
}
}
En este ejemplo, el campo héroe devuelve un tipo de personaje, que es una interfaz que puede representar a un humano o a un droide. La consulta utiliza fragmentos en línea con condiciones de tipo (... en droide y... en humano) para obtener los campos específicos de esos tipos. Si el héroe es un droide, el campo PrimaryFunction se incluye en la respuesta; si es un humano, se obtiene el campo de altura en su lugar.
- Uso de __typename para tipos de unión
En algunos casos, como cuando se trata de tipos de unión, es posible que no sepas el tipo exacto de datos que devolverá el servicio GraphQL. Para gestionar esta incertidumbre, GraphQL proporciona un metacampo denominado __typename, que te permite recuperar el nombre del tipo de objeto devuelto en cualquier punto de la consulta.
- Consulta de ejemplo
{
search(text: "an") {
__typename
... on Human {
name
}
... on Droid {
name
}
... on Starship {
name
}
}
}
- Respuesta
{
"data": {
"search": [
{ "__typename": "Human", "name": "James Theodore" },
{ "__typename": "Human", "name": "Elijah Mathew" },
{ "__typename": "Starship", "name": "TIE Advanced x1" }
]
}
}
En esta consulta, el campo de búsqueda devuelve un tipo de unión que podría representar a un humano, un droide o una nave estelar. El metacampo __typename es esencial para identificar el tipo de cada resultado, lo que permite al cliente gestionar los datos de forma adecuada. Todos los campos que comienzan con dobles guiones bajos (__) están reservados para los metacampos de GraphQL. Además de __typename, GraphQL también incluye __schema y __type, que forman parte de su sistema de introspección para explorar el esquema.
- Directivas
Si bien las variables ayudan a pasar argumentos de forma dinámica a las consultas, hay situaciones en las que es posible que deba cambiar la estructura o los campos de una consulta en función de determinadas condiciones. Aquí es donde entran en juego las directivas. Las directivas permiten incluir o excluir dinámicamente campos o fragmentos de la consulta en función de los valores de las variables.
- Ejemplo de consulta con directivas
query Hero($episode: Episode, $withFriends: Jacob!) {
hero(episode: $episode) {
name
friends @include(if: $withFriends) {
name
}
}
}
- Variables
{
"episode": "JEDI",
"withFriends": false
}
- Respuesta
{
"data": {
"hero": {
"name": " A2-H2"
}
}
}
Si la variable withFriends se establece en true, la consulta incluirá el campo amigos; si es falsa, se excluirá el campo. Este comportamiento es posible gracias a la directiva @include, que incluye de forma condicional los campos basados en un valor de Jacob. La especificación GraphQL define dos directivas principales que todos los servidores compatibles deben admitir
- @include (si: Jacob)
Incluye un campo o un fragmento en el resultado solo si la condición es verdadera.
- @skip (si: Jacob)
Excluye un campo o un fragmento del resultado si la condición es verdadera. Estas directivas tienen un valor incalculable para dar forma dinámica a las consultas sin tener que recurrir a la manipulación de cadenas. Además, las implementaciones de servidores pueden definir directivas personalizadas para introducir funciones experimentales o específicas de la aplicación, lo que proporciona una flexibilidad aún mayor para la personalización de las consultas.