GraphQL es una especificación para la consulta de datos, creado por facebook en el 2012 pero liberado como proyecto open source en el 2015 y que viene a ser una alternativa a las APIs basadas en el modelo Rest.
La idea con GraphQL es que el cliente, ya sea una aplicación Web, móvil o cualquier otro que quiera acceder a nuestros datos, pueda acceder a los datos que realmente necesita usar, para tener consultas más optimas al servidor. Esto se diferencia mucho del modelo Rest donde los recursos a los que voy acceder están definidos por los endpoints que estoy consultando.
Normalmente en Rest tenemos endpoints como el siguiente: GET: /api/v1/posts Y este endpoint me devuelve un json como el siguiente:
{
"posts": [
{
"userId": 1,
"id": 1,
"title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
"body": "quia et suscipit\\nsuscipit recusandae consequuntur expedita et cum\\nreprehenderit molestiae ut ut quas totam\\nnostrum rerum est autem sunt rem eveniet architecto"
},
{
"userId": 1,
"id": 2,
"title": "qui est esse",
"body": "est rerum tempore vitae\\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\\nfugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\\nqui aperiam non debitis possimus qui neque nisi nulla"
},
{
"userId": 1,
"id": 3,
"title": "ea molestias quasi exercitationem repellat qui ipsa sit aut",
"body": "et iusto sed quo iure\\nvoluptatem occaecati omnis eligendi aut ad\\nvoluptatem doloribus vel accusantium quis pariatur\\nmolestiae porro eius odio et labore et velit aut"
},
{
"userId": 1,
"id": 4,
"title": "eum et est occaecati",
"body": "ullam et saepe reiciendis voluptatem adipisci\\nsit amet autem assumenda provident rerum culpa\\nquis hic commodi nesciunt rem tenetur doloremque ipsam iure\\nquis sunt voluptatem rerum illo velit"
}
]
}
Siempre que consulte este endpoint voy a conseguir estos datos con esta misma estructura, pero que pasa si de todos estos datos solo necesito el id y el title?, en este caso tenemos dos opciones:
- Modificar el endpoint para devolver solo los campos `id` y `title`
- Crear un nuevo endpoint que devuelva estos campos
La desventaja de la primera opción es que puede que existan otros lugares de nuestra aplicación o otros clientes que si usen los campos body y userId por lo que tendríamos fallos en esos lugares, y la desventaja con la segunda opción es que a lo largo del tiempo nuestra API tiende a crecer bastante para poder cumplir estas necesidades.
Justamente estos inconvenientes fueron los que llevaron a Facebook a crear GraphQL, en sus aplicaciones móviles estaban teniendo problemas con el rendimiento de sus peticiones, y justamente era por que descargaban datos que a la larga no utilizaban, por lo que crearon GraphQL para descargar solo los datos que se requerían.
Pero ahora, ¿Cómo se hace una petición en GraphQL?, la misma petición de arriba luciría de la siguiente manera:
query {
posts {
userId
id
title
body
}
}
Esta consulta nos devolvería la misma estructura que tenemos arriba, pero ahora si queremos solo el id y el title debemos eliminar userId y body de nuestra query.
query {
posts {
id
title
}
}
Esta query nos devolveria lo siguiente:
{
posts: [
{
"id": 1,
"title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
},
{
"id": 2,
"title": "qui est esse",
},
{
"id": 3,
"title": "ea molestias quasi exercitationem repellat qui ipsa sit aut",
},
{
"id": 4,
"title": "eum et est occaecati",
}
]
}
Como podemos ver en la respuesta de la petición, ahora solo nos llegan lo que explícitamente le indicamos en nuestra query, sin necesidad de crear un nuevo endpoint, el cliente, ya sea una aplicación web o una aplicación móvil es quien decide que campos quiere recibir.
Para entender un poco mejor como funciona esto debemos revisar algunos conceptos fundamentales de GraphQL.
Tipos personalizados
Los tipos personalizados es la forma que nos provee GraphQL de definir la forma que puede tener nuestros datos, por ejemplo para el caso de los posts el tipo seria así:
type Post {
id: Int!
userId: Int!
title: String!
body: String!
}
En este caso he definido el tipo Post donde en su esquema están los campos que más tarde mi cliente puede consultar.
GraphQL al ser tipado me obliga a decir de que tipo van a ser cada uno de mis campos, en el caso anterior podemos ver Int y String, y la lista completa de estos tipos son:
- Int
- String
- Boolean
- ID
- Float
El signo ! representa que el campo que estamos definiendo es obligatorio, es decir que no acepta valores nulos, este signo es opcional, por lo que podemos ponerlo o no dependiendo si queremos que el campo pueda ser nulo o no.
Además de los tipos anteriormente mencionados dentro de nuestros campos también podemos poner otros tipos personalizados, por ejemplo:
type User {
id: Int!
name: String!
email: String!
}
type Comment {
id: Int!
content: String!
date: String!
author: User!
}
type Post {
id: Int!
userId: Int!
title: String!
body: String!
comments: [Comment]!
}
En el anterior ejemplo a nuestro tipo Post le añadimos el campo comments que nos va a retornar un array con elementos del tipo Comment, este a su vez va a contener además de los campos id, content y date, tendrá un campo personalizado User que me va a representar la información del usuario que hizo el comentario.
Query
Los Query es la herramienta que nos provee GraphQL para definir que información vamos a exponer a nuestros clientes, se define de la siguiente manera:
type Query {
posts: [Post]!
}
Aquí estamos definiendo un query llamado posts con el cual nuestros usuarios podrán conseguir un array de nuestro tipo Post, para consultar esta información bastaría hacer la petición anteriormente mostrada:
query {
posts {
id
title
}
}
A esa petición le podríamos añadir el body o el userId si así lo deseáramos, incluso como en nuestro tipo Post estamos definiendo el campo comments, lo podemos consultar de la siguiente manera cuando así lo requiramos:
query {
posts {
id
title
comments {
id
content
}
}
}
Esta query nos devolvería la siguiente respuesta:
{
"posts": [
{
"id": 1,
"title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
"comments": [
{
"id": 1,
"content": "Muy buen artículo"
},
{
"id": 2,
"content": "Muy bien pero solo me queda una duda"
},
]
},
{
"id": 2,
"title": "qui est esse",
"comments": [
{
"id": 5,
"content": "Muy buen artículo, espero el próximo"
},
{
"id": 6,
"content": "No me quedo muy claro"
},
]
},
{
"id": 3,
"title": "ea molestias quasi exercitationem repellat qui ipsa sit aut",
"comments": [
{
"id": 7,
"content": "Muy buen artículo"
},
{
"id": 8,
"content": "Puede mejorar"
},
]
},
{
"id": 4,
"title": "eum et est occaecati",
"comments": [
{
"id": 9,
"content": "Muy buen artículo para empezar"
},
{
"id": 10,
"content": "Excelente!"
},
]
}
]
}
Y de ser necesario también podemos solicitar la información del autor, ya que el tipo comments incluye este campo
query {
posts {
id
title
comments {
id
content
author {
id
name
email
}
}
}
}
Como pueden ver la flexibilidad que esto nos provee es bastante grande, solo debemos indicar en nuestra query que campos queremos recibir y nuestro servidor internamente se encargara de enviarnos únicamente esos campos que solicitamos y nada mas, haciendo nuestras peticiones mucho más pequeñas, ya que nunca estaremos cargando campos que no necesitemos.
Ahora, si queremos una query que nos consulte solo un post, podemos agregar esta query a nuestro esquema de la siguiente manera:
type Query {
posts: [Post]!
post(id: Int!): Post!
}
Con esto estamos agregando nuestra query post, donde esta recibe como parámetro un id que representara el id del post que queremos conseguir, para ejecutar este query solo debemos hacer lo siguiente:
query {
post(id: 1) {
id
title
}
}
{
"post": {
"id": 1,
"title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit"
}
}
Esta query solo nos devolverá el id y el title del post que tenga el id 1, y como post tiene el campo comments, aquí también podríamos agregarlo y así conseguir los comentarios del post con el id 1.
query {
post(id: 1) {
id
title
comments {
id
content
}
}
}
Con esta query conseguiríamos la siguiente respuesta:
{
"post": {
"id": 1,
"title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
"comments": [
{
"id": 1,
"content": "Muy buen artículo"
},
{
"id": 2,
"content": "Muy bien pero solo me queda una duda"
},
]
}
}
Mutation
Las Query si lo comparamos con el modelo de Rest es equivalente a una operación GET, es decir es una operación de lectura de nuestros datos, en cambio las mutaciones en GraphQL nos servirán como su nombre lo indica para mutar o modificar nuestra información, lo cual seria equivalente a los verbos POST, PUT y DELETE.
Para definir las mutaciones en GraphQl, se hace de la siguiente manera:
type Message {
message: String!
}
type Mutation {
addPost(userId: Int!, title: String!, body: String!): Post
deletPost(id: Int!): Message
updatePost(title: String!, body: String!): Post
}
En este caso hemos definido tres mutaciones, una para agregar un post, otra para borrar un post y la ultima para actualizarlo.
Entre los paréntesis de cada mutación estamos definiendo los parámetros que esta necesita, el signo ! como se menciono antes, me indica que es un parámetro obligatorio y después de los dos puntos se indica que tipo de valor va a retornar nuestra mutación, en la mutación addPost y updatePost seria un elemento de tipo Post y en la mutación deletPost seria un objeto del tipo `} Message que nosotros mismos definimos.
Para hacer uso de estas mutaciones seria de la siguiente manera:
mutation {
addPost(userId: 1, title: "Introducción a GraphQl", body: "Contenido del post") {
id
title
}
}
Esta mutación nos retornaría lo siguiente
{
"addPost" : {
"id": 5,
"title": "Introducción a GraphQl"
}
}
De esta forma podemos hacer uso de nuestra mutación addPost y dentro de los corchetes podemos obtener la información que queramos sobre el post recién creado, y dado que es un objeto del tipo Post podemos hacer todas sus consultas permitidas.
Ahora usemos nuestra mutación deletPost :
mutation {
deletPost(id: 1) {
message
}
}
La mutación deletPost recibe el id del post como su parámetro y me retorna un objeto del tipo Message, y así estamos accediendo al campo message.
{
"message": "Post eliminado con exito"
}
Antes de terminar unas ultimas consideraciones:
- GraphQL es independiente del lenguaje de programación puede ser implementado en cualquier lenguaje de backend, para mas información te invito a ver el siguiente link
- GraphQL es independiente de la base de datos, es decir que podemos usar cualquier fuente de datos al momento de desarrollar nuestra API basada en GraphQL, ya sea una base de datos relacional o no relacional, incluso podriamos consumir estos datos consumiendo otras APIs
Espero que te haya gustado este articulo, en próximos contenidos estaremos aprendiendo a como crear nuestra propia API con GraphQL y aprenderemos a consumirla desde React, hasta la próxima.
