DEV Community

Cover image for Fundamentos de GraphQL
Gustavo Garsaky
Gustavo Garsaky

Posted on

Fundamentos de GraphQL

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:

🗒 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í:

{
  "projectId": "83ngrgn48t5rdg4tedgasdgasg",
  "name": "Bot integration for Messenger",
  "manager": {
    "managerId": "734gnrfgnfngvffasdgfasdngf",
    "firstname": "John",
    "lastname": "Doe",
    "area": "TI"
  }
}
Enter fullscreen mode Exit fullscreen mode

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:

const managerArea = project.manager.area
Enter fullscreen mode Exit fullscreen mode

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:

query {
  getProject(id: "83ngrgn48t5rdg4tedgasdgasg") {
    manager {
      area {
        name
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Donde obtendríamos como respuesta:

{
  data: {
    getProject: "TI"
  }
}
Enter fullscreen mode Exit fullscreen mode

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:

@Entity()
class User {
  @PrimaryGeneratedColumn()
  id: int
  @Column()
  name: string
  @Column()
  email: string
}
Enter fullscreen mode Exit fullscreen mode

La definición anterior corresponde a Typescript usando el ORM TypeORM

Podemos mapear nuestro tipo User de la siguiente manera:

type User {
  id: Int
  name: String
  email: String
}
Enter fullscreen mode Exit fullscreen mode

De manera que GraphQL ahora sabe qué propiedades de la entidad coger 😉. Puedes ver los tipos de datos de GraphQL aquí.

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í:

Query {
  user(id: Int!): User
}
Enter fullscreen mode Exit fullscreen mode

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:

{
  user(id: 4575) {
    name,
    email
  }
}
Enter fullscreen mode Exit fullscreen mode

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:

type Mutation {
  createUser(name: String!, email: String!) : User
}
Enter fullscreen mode Exit fullscreen mode

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

{
  createUser(name: "Gustavo", email: "gusgarzaki@gmail.com") {
    id # solo queremos el id
  }
}
Enter fullscreen mode Exit fullscreen mode

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.

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

type Mutation {
  createUser(data: UserInput!): User
}
Enter fullscreen mode Exit fullscreen mode

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

{
  firstName: "Gustavo",
  lastName: "Garzaki",
  cardId: 35551232,
  email: "gusgarzaki@gmail.com"
}
Enter fullscreen mode Exit fullscreen mode

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:

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

fragment PersonalData on User {
  firstName
  lastName
  cardId
  address
}
Enter fullscreen mode Exit fullscreen mode

Y podemos reusar ese fragmento en cualquier consulta:

{
  findUser(id: 1099) {
    ...PersonalData
  }
}
Enter fullscreen mode Exit fullscreen mode

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:

query FindUser($id: Int!) {
  user(id: $id) {
    ...PersonalData # hacemos uso de un fragmento para reforzar 😁
  }
}
Enter fullscreen mode Exit fullscreen mode

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.

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

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. 😉

Top comments (2)

Collapse
 
likaronav profile image
Likaro-nav

Andaba buscando una lectura que me permitiera comprender un poco más sobre GraphQL. ¡Muchas gracias por el tiempo de escribirla!
Creo recordar que el ! se tipea para indicar que dicho tipo de dato es requerido, ¿es así o me equivoco?

Saludos!

Collapse
 
gugadev profile image
Gustavo Garsaky

¡Muchas gracias Efren!