40

Qué es GraphQL

21836Puntos

hace 7 años

GraphQL es un lenguaje de queries que te permite definir qué datos pedirle a un API. Se trata de una tecnología que Facebook empezó a desarrollar en 2012, aunque fue anunciada en 2015. Según Facebook, proporciona una descripción completa y comprensible de los datos de su API, ofrece a los clientes la posibilidad de pedir exactamente lo que necesitan, facilita la evolución de las API con el paso del tiempo (escalabilidad) y habilita potentes herramientas para desarrolladores.

Lo interesante de GraphQL

Con GraphQL el cliente (el Frontend) decide qué datos pedir al servidor y de qué forma le conviene recibirlos, lo que quiere decir que, si llegara a necesitar información adicional, o si simplemente deja de necesitar un dato, no tiene que modificar el backend: basta con cambiar la query de GQL en el cliente ¡y listo!

Lo importante de GraphQL

GraphQL no es una librería o framework: es una especificación de cómo implementarlo en cualquier lenguaje. Además existen implementaciones ya creadas en lenguajes como JavaScript, Ruby, Python, Scala, Java, Clojure, Go, PHP, .NET, etc. También existen clientes para consumir un API GraphQL desde JS, iOS, Android, React, Angular, entre otras.

La interacción con GraphQL

Cuando tenemos un API Rest, la forma de interactuar con ella empieza con una petición HTTP que hacemos usando alguno de los métodos, GET, POST, PUT o DELETE, a ciertas URL únicas por recurso del API.

Por ejemplo, si hacemos una petición GET /api/careers/, esto nos devuelve la lista de carreras. Si le agregamos /:id, podemos pedir los datos de una carrera específica. Así también con POST podemos crear nuevas carreras, con PUT y DELETE nos sirve para actualizar y borrar carreras.

En cambio con GQL tenemos un solo endpoint, normalmente /graphql, y solo interactuamos con el mediante el método POST. Para decirle que datos queremos obtener, crear o modificar colocamos en el cuerpo de la petición alguna de estas tres cosas:

  • Query
  • Mutation
  • Subscription

Estas son las 3 formas de interactuar con un API GraphQL. Ahora vamos a ver de qué se trata cada una.

Query

El primer concepto importante de GQL es la Query. Una query es una consulta que hacemos a nuestra API:

query {
	getCourse("id": 1) {
		id
		title
		url
		concepts {
			title
			materials {
				title
				url
			}
		}
	}
}

Ese es un ejemplo de una query para pedir una lista de carreras. De cada carrera podemos pedir el id, el title, el badge, si está approved y sus cursos. También podemos pedir los mismos datos de la carrera para cada curso, más la URL. En ese caso, este es el contenido de la respuesta de dicha query:

{
	"data": {
		"getCourse": {
			"id": 1,
			"title": "Fundamentos de JavaScript",
			"url": "https://platzi.com/clases/fundamentos-javascript/",
			"concepts": [
				...
			]
		}
	}
}

Como vemos, lo que se obtiene es un objeto con nuestros datos. Adentro se encuentra el curso que pedimos al servidor, solamente con las propiedades que solicitamos. De esta forma, es nuestro cliente quien define los datos que necesita. El servidor simplemente se encarga de responder con esos datos específicamente.

Es cierto que un curso, un concepto y un material pueden tener muchos más datos, pero estos son los únicos que nosotros necesitamos. A diferencia de un API Restful, GQL nos devuelve exactamente lo que necesitamos para nuestra UI: ni más ni menos.

Mutation

Las queries nos permiten obtener datos, pero toda aplicación necesita una forma de crear, modificar, eliminar o interactuar con estos datos. Estas acciones son llamadas mutaciones. Una mutación es similar a una función: recibe ciertos parámetros, realiza un cambio y devuelve una respuesta. Así:

mutation {
	addToLearningPath(
		"id": 1
	) {
		title
		badge
		approved
		url
	}
}

En este caso, esa mutación le indica al servidor que queremos agregar el curso con el ID 1 a nuestro plan de estudios. Entonces le decimos que nos devuelva como respuesta: el título, el badge, la aprobación y la URL. La respuesta sería algo así:

{
	"data": {
		"addToLearningPath": {
			"title": "Fundamentos de JavaScript",
			"badge": "https://static.platzi.com/media/achievements/badge-Fundamentos-js.png",
			"approved": false,
			"url": "https://platzi.com/clases/fundamentos-javascript/"
		}
	}
}

Aquí hemos visto que la respuesta nos entrega únicamente los datos que pedimos del curso que agregamos al plan de estudios. Luego, con esos datos podemos agregar nuestro curso a la UI de un plan de estudios.

Subscription

Otra opción importante de GQL es la suscripción. Esto no está implementado por todas las librerías para backend, ya que hace poco se volvió parte oficial de la especificación. Lo que nos permiten las suscripciones es, como su nombre dice, suscribirnos a cambios que ocurran en el servidor.

Por ejemplo: es posible suscribirnos a nuevas notificaciones para enterarnos en tiempo real de lo que ocurre con nuestra aplicación. Se trata, básicamente, de mantener una conexión por WebSockets. Veamos un ejemplo:

subscription {
	notificationOf("type": "answer") {
		id
		type
		data {
			discussion {
				id
				title
			}
			answer {
				author {
					avatar
					username
				}
				date
			}
		}
	}
}

Con esta suscripción podemos enterarnos, por ejemplo, de cuándo nos responden una pregunta. De cada notificación vamos a obtener el ID, el tipo (siempre answer gracias al filtro) y otros datos específicos como el ID, título de la pregunta, el autor (con su nombre y avatar) y la fecha de la respuesta.

Al llegar una notificación recibiremos un objeto similar a este:

{
	"data": {
		"id": 123,
		"type": "answer",
		"data": {
			"discussion": {
				"id": 456,
				"title": "¿Cómo funciona GraphQL?"
			},
			"answer": {
				"author": {
					"avatar": "",
					"username": "sergiodxa"
				},
				"date": "2017-05-28T23:48:27.752Z"
			}
		}
	}
}

Schema

Cuando desarrollamos un API GQL debemos definir nuestros esquemas (schemas) de datos. Un esquema en GQL puede ser la definición de un tipo de dato, o la definición de las formas de obtener e interactuar con ellos. Por ejemplo, podríamos definir un esquema similar a este:

type Course {
	id: Int!
	title: String!
	badge: String!
	approved: Boolean
	url: String!
	description: String
	concepts: [Concept]
}

Aquí estamos definiendo que en nuestra aplicación existe un objeto de tipo Course que tiene los siguientes datos:

  • id un número, obligatorio
  • title un string, obligatorio
  • badge un string, obligatorio
  • approved un booleano, opcional
  • url un string, obligatorio
  • description un string, opcional
  • concepts una lista de objetos de tipo Concept, opcional

La propiedad concepts es básicamente una relación que indica que un curso tiene varios conceptos. Luego, necesitamos definir las posibles formas de obtener este curso.

type Query {
	getCourse(id: Int!): Courses
	getCourses(): [Courses]
}

Esto nos permite ejecutar una query llamada getCourse pasándole un id, obtener un único curso o ejecutar getCourses para obtener una lista de todos los cursos. Similar a la query que vimos antes. Después necesitamos definir las posibles mutaciones: las formas de interactuar con nuestra API:

type Mutation {
	addToLearningPath(id: String!): Course
	createCourse(
		title: String!
		badge: String!
		url: String
		description: String
	): Course
	updateCourse(
		title: String
		badge: String
		url: String
		description: String
	): Course
}

Estas mutaciones que definimos en nuestro esquema son las formas de interactuar con nuestros cursos. Podemos agregarlos a nuestro plan de estudios, crear o modificar cursos. También hay que decidir a qué datos podemos suscribirnos:

type Subscription {
	notificationOf(type: String!): Notification
}

Para definir las suscripciones simplemente indicamos el nombre, los parámetros que quiero recibir y lo que espero que devuelva. Igualmente con las mutaciones. Por último, es necesario estructurar nuestro esquema final, que siempre es algo así:

type Schema {
	query: Query
	mutation: Mutation
	subscription: Subscription
}

En este punto se trata simplemente indicar que nuestro esquema tiene query, mutation y subscription, y que cada uno corresponde con lo que definimos antes.

Resolvers

Los esquemas por sí solos no hacen nada: únicamente definen qué se puede hacer en nuestra APIa través de los resolvers, que son funciones que se encargan de procesar cada posible query, mutación o suscripción de nuestra API y de responder con los datos necesarios, según la definición de nuestro esquema. Por ejemplo:

const getCourse = async id => Course.findById(id)

Esta función de una línea se encarga obtener un curso mediante el ID usando el modelo Course de nuestra base de datos. En este caso, estamos pidiendo todos los posibles campos de nuestra tabla “curso” de la base de datos. Pero también es posible (y de hecho es la idea de GQL) pedir a nuestra BD únicamente los datos que indicó el cliente. Así se optimiza la lectura y escritura de la BD.

GraphiQL

GraphQL tiene su propio IDE, creado por Facebook, llamado GraphiQL (pronunciado grafical). Este IDE funciona mediante web, mostrándose, normalmente, en la URL de nuestro API cuando entramos desde un navegador. Este se conecta con nuestros esquemas de datos para mostrarnos documentación del API y nos deja probarlo, dando sugerencias de autocomplete y mostrándonos las respuestas a las distintas peticiones, mutaciones y suscripciones.

GraphiQL

Aquí hemos visto un ejemplo de cómo se ve GraphiQL. En la esquina superior izquierda se encuentra nuestra petición; en la parte inferior tenemos variables y a la derecha se muestran los resultados de correr esa petición.

GraphQL vs Restful

Un problema común de los API Rest es que requieren muchas peticiones para obtener todos los datos necesarios en una vista de la aplicación. Estos nos permiten hacer dichas peticiones -con lo que podemos estar consumiendo datos de nuestros usuarios y tardar mucho en obtener todas las respuestas-, o crear URLs personalizadas para responder con todos los datos necesarios, -en cuyo caso estaríamos creando endpoints en nuestro API que nunca más se van a volver a usar dado su alto nivel de personalización-.

La buena noticia es que ambos problemas son resueltos por GraphQL, permitiendo que el cliente decida qué datos quiere en cada momento y que pueda manejar todas las peticiones con una sola URL. Así, en vez de crear URLs personalizadas, simplemente definimos los posibles datos de nuestro API y la forma de interactuar con él… el resto se lo dejamos al frontend.

Palabras finales

Rest no está muerto. Todavía puede ser útil para la comunicación entre servicios de backend, pero para un API, de cara al cliente que use web, mobile, desktop, GraphQL tiene muchas ventajas. Gracias a clientes como Apollo o Relay Modern es muy fácil empezar a usarlo en cualquier tipo de aplicación frontend.

Si quieres aprender más sobre este lenguaje de queries, entra ya al Curso de GraphQL.

Sergio Daniel
Sergio Daniel
sergiodxa

21836Puntos

hace 7 años

Todas sus entradas
Escribe tu comentario
+ 2
1
14321Puntos

Una Pregunta? Si quiero consultar un enlace a una url de GraphQL, pero no sé sus datos específicos como puedo hacer la consulta si ella me pide que especifique los datos?

1
48Puntos

Buenas Sergio daniel, felicitaciones por tu explicacion, estoy intentando crear un proyecto con grapql y node y al inicio es complejo, pues todos los ejemplos son simples, pocos con relaciones entre tablas, pero se le va aprendiendo. Mi consulta es sobre algo que mencionaste sobre no consultar el objeto completo de la bd sino lo que se pidio desde el cliente graphql, como puedo hacer esto, en el curso de platzi que recomiendas tocan estos temas a profundidad. Quiero un curso en nodejs express y graphql. que me recomienda, saludos