Fundamentos de GraphQL
Tags: explainlikeimfive, graphql
Published: 2019-01-24 15:41:39

No he visto mucho material de GraphQL en Español, así que, aprovechando que tengo un poco de tiempo libre, he preparado esta pequeña introducción a esta genial tecnología.

Descripción oficial de [GraphQL](https://graphql.org/learn/):

> 🗒 GraphQL es un lenguaje de consultas para APIs y un entorno server-side para ejecutar consultas usando un sistema de tipos definido por ti en tus datos. GraphQL no está ligado a ninguna base de datos o motor de almacenamiento, en su lugar, está respaldado por tu código existente y tus datos.

Entendemos por GraphQL como un **lenguaje de consultas**, el cual usa un sistema de _tipos_ previamente definido por nosotros. ¿Por qué entonces, es tan importante GraphQL?

Veamos el siguiente escenario para poder comprender mejor el funcionamiento de GraphQL. Supongamos que tenemos el siguiente endpoint:

- `/api/v1/project/:id`

El cual nos trae un proyecto de la base de datos por medio de su  `id`. Cada `Project` mantiene una dependencia con `Manager`. Nuestro endpoint nos traerá algo así:

```json
{
  "projectId": "83ngrgn48t5rdg4tedgasdgasg",
  "name": "Bot integration for Messenger",
  "manager": {
    "managerId": "734gnrfgnfngvffasdgfasdngf",
    "firstname": "John",
    "lastname": "Doe",
    "area": "TI"
  }
}
```

Bien, hasta aquí perfecto, tenemos nuestro proyecto junto con su manager. Pero, digamos que a mi no me interesa los datos del proyecto ni del manager, **solo me interesa saber a que área pertenece** el manager de dicho proyecto. ¿Cómo lo haces?

La manera tradicional es haciendo un filtrado de datos:

```javascript
const managerArea = project.manager.area
```

Ya lo tengo, simple. ¿Cuál es el chiste con GraphQL entonces? 🙄 Tranquilo, no te emociones. Te pregunto algo: **¿Es esto eficiente?**

Digamos que `Manager` tiene una dependencia `Area` y `Area` otra dependencia con `Organizatinn` y así sucesivamente hasta tener una cantidad considerable de documentos anidados. ¿Crees que sería **eficiente** obtener todos estos documentos para obtener solo uno o dos datos?

GraphQL viene a solucionar este problema de una manera limpia y eficiente. Lo anterior, usando GraphQL quedaría de la siguiente manera:

```javascript
query {
  getProject(id: "83ngrgn48t5rdg4tedgasdgasg") {
    manager {
      area {
        name
      }
    }
  }
}
```
Donde obtendríamos como respuesta:

```javascript
{
  data: {
    getProject: "TI"
  }
}
```

Ahora nuestra solución es mucho más limpia y sobre todo, liviana, optimizando los recursos de red del usuario, sobre todo en dispositivos móviles. No te preocupes si no entiendes aún cómo funciona, lo veremos progresivamente en este tutorial.

## Types

Ya vi cómo se usa, pero, ¿Cómo sabe GraphQL que datos debe seleccionar de la base de datos? 🤔

GraphQL no selecciona nada de la base de datos; lo que hace es hacer un **mapeo de entidades** a lo que se conoce como _types_. Un tipo es una representación de algún objeto o suceso dentro de nuestra aplicación. Por ejemplo, si tenemos un modelo `User` con la siguiente definición:

```javascript
@Entity()
class User {
  @PrimaryGeneratedColumn()
  id: int
  @Column()
  name: string
  @Column()
  email: string
}
```

> La definición anterior corresponde a Typescript usando el ORM [TypeORM](http://typeorm.io)
> 

Podemos mapear nuestro tipo `User` de la siguiente manera:

```javascript
type User {
  id: Int
  name: String
  email: String
}
```

De manera que GraphQL ahora sabe qué propiedades de la entidad coger 😉. Puedes ver los tipos de datos de GraphQL [aquí](https://graphql.org/learn/schema/#type-system).

## Queries

Vale, hasta aquí entiendo bien. Pero, ¿Cómo puedo obtener una respuesta de GraphQL? No entiendo esa parte 🤔. Calma, vamos a ver ello.

Para que GraphQL responda, necesitamos hacerle una **consulta**. Para que GraphQL sepa las consultas que tiene disponibles, necesitamos primero **definir** su estructura. Esto se hace así:

```javascript
Query {
  user(id: Int!): User
}
```

Es súper sencillo: solo definimos el **nombre**, los **argumentos** que recibirá y el **tipo de dato que retornará**. Si prestamos atención es similar una definición de una función. Así mismo, para ejecutar esa consulta, es similar a la ejecución de una función:

```javascript
{
  user(id: 4575) {
    name,
    email
  }
}
```

Dado que el tipo de retorno es de tipo `User` podemos decidir que tipos de datos queremos obtener. En este caso, solo necesitamos `name` y `email`.

## Mutation

Un `Mutation` es muy similar a una `Query`, de hecho son prácticamente lo mismo. La diferencia radica en la naturaleza de su uso: `Query` está destinado a **lectura** de datos mientras que `Mutation` a **escritura**. Hay algunas diferencias a nivel técnico, pero nada que impacte a nivel de desarrollo.

Al igual que las `Query`, las mutaciones se definen:

```javascript
type Mutation {
  createUser(name: String!, email: String!) : User
}
```

Y se ejecutan de la misma forma; pudiendo también obtener los campos que necesitemos.

```javascript
{
  createUser(name: "Gustavo", email: "gusgarzaki@gmail.com") {
    id # solo queremos el id
  }
}
```

## Input

Algunas veces necesitamos enviar un conjunto de valores como un solo parámetro sin necesidad definir cada propiedad como argumento. Para este propósito existen los `input` types.

```javascript
input UserInput {
  firstName: String
  lastName: String
  cardId: Int
  email: String
}

type Mutation {
  createUser(data: UserInput!): User
}
```

Y de esta manera podemos pasarle un objeto con esas propiedades:

```javascript
{
  firstName: "Gustavo",
  lastName: "Garzaki",
  cardId: 35551232,
  email: "gusgarzaki@gmail.com"
}
```

## Fragment

Ahora que sabemos qué es un `Type`, un `Query` y un `Mutation` y para qué se usan, vamos a ver una estructura utilitaria. Un `Fragment` te permite definir un conjunto de campos y luego incluirlos en las consultas. Por ejemplo:

```javascript
type User {
  id: Int
  firstName: String
  lastName: String
  cardId: Int
  address: String
  email: String
  phone: String
}

fragment PersonalData on User {
  firstName
  lastName
  cardId
  address
}
```

Y podemos reusar ese fragmento en cualquier consulta:

```javascript
{
  findUser(id: 1099) {
    ...PersonalData
  }
}
```

## Variables

Tan simples pero a la vez tan esenciales. ¡No podían faltar! En GraphQL, podemos realizar consultas dinámicas a través de variables. Veamos un ejemplo:

```javascript
query FindUser($id: Int!) {
  user(id: $id) {
    ...PersonalData # hacemos uso de un fragmento para reforzar 😁
  }
}
```

De esta manera podemos tener un catálogo de queries a las cuales solo les pasamos variables en lugar de tener que escribir las consultas cada vez que vamos a realizarlas.

## Directives

Hemos visto como las variables nos ayudan a reutilizar consultas. Incluso esto no puede ser suficiente en algunos casos; quizás queramos incluir o excluir cierta información de forma dinámica de acuerdo a algún indicador. Para esto existen las directivas, que, por medio de una variable, nos permiten incluir o excluir ciertos campos. Veamos un ejemplo.

```javascript
query FindUser($id: Int!, $onlyPersonalData: Boolean! = false) {
  user(id: $id) {
    ...PersonalData
    email @skip(if: $onlyPersonalData)
    phone @skip(if: $onlyPersonalData)
  }
}
```

Las directivas nos permiten obtener o evitar campos de manera dinámica. En el ejemplo anterior, definimos dos variables: `$id` que representa el id del usuario y `$onlyPersonalData` que indica si queremos incluir solo los datos personales, de esta manera, el resultado variará de acuerdo a si pasas `true` o `false`.

> **💡** GraphQL nos da dos directivas:
> 
> - `@include(if: Boolean)`, para incluir datos en el resultado solo si el argumento es `true`.
> - `@skip(if: Boolean)`, para excluir datos en el resultado solo si el argumento es `true`.

---

## Conclusión

Como puedes ver, GraphQL es radicalmente diferente a las APIs convencionales basadas en REST o SOAP; este tiene su propio lenguaje y te permite obtener datos precisos de forma eficiente gracias a su tremenda flexibilidad.

Recuerda que si bien la arquitectura GraphQL **no es un reemplazo** de las APIs convencionales, todo depende de tus necesidades. 😉