GraphQL

Un lenguaje de consultas para APIs

Una alternativa más eficiente a REST

Secciones de la APP

  1. Nombre del autor.
  2. Títulos de sus últimos post.
  3. Últimos tres seguidores.

Proceso de comunicación con la API REST

Proceso de comunicación con la API GraphQL

GraphQL: SDL

Lenguaje de Definición de Esquemas

Schema Definition Language

GraphQL SDL: Tipos de datos

type User {
  id: ID!
  name: String!
  age: Int!
  posts: [Post]!
  followers: [Person]!
}
type Post {
  id: ID!
  title: String!
  author: Person!
}

GraphQL SDL: Definición de las posibles consultas

# `Query` es una palabra clave que describe las posibles consultas que tiene la API
type Query {
  users(id: ID): [User]!
  # Recupera los seguidores de un usuario o,
  # si se pasa el parámetro \`last\`, recupera los últimos \`last\` seguidores
  followers(user: ID!, last: Int): [User]!
  posts(user: ID): [Post]!
}
# Ejemplo de una consulta donde se recuperan
# únicamente el nombre de todas las personas
{
  users {
    name
  }
}
# JSON resultante
{
  "data": {
    "users": [
      { "name": "Johnny" },
      { "name": "Sarah" },
      { "name": "Alice" }
    ]
  }
}

GraphQL SDL: Definición de las posibles mutaciones

# `Mutation` es una palabra clave que describe el conjunto de métodos para modificar datos
type Mutation {
  # Agrega un nuevo Usuario
  createUser(name: String!, age: Int!): User!
}
# Ejemplo de una inserción que como resultado
# devuelve los datos de un nuevo usuario
{
  mutation {
    # Se llama Bob y tiene 36 años
    createUser(name: "Bob", age: 36) {
      name
      age
    }
  }
}
# JSON resultante
{
  "data": {
    "createUser": {
      "name": "Bob",
      "age": 36
    }
  }
}

GraphQL: Implementación en el servidor

Definición del esquema

// Archivo `server/schemas/index.js`
const typeDefs = /* GraphQL */`

type Query {
  users(id: ID): [User]!
  # Recupera los seguidores de un usuario o,
  # si se pasa el parámetro \`last\`, recupera los últimos \`last\` seguidores
  followers(user: ID!, last: Int): [User]!
  posts(user: ID): [Post]!
}

type Mutation {
  # Agrega un nuevo Usuario
  createUser(name: String!, age: Int!): User!
}

type User {
  id: ID!
  name: String!
  age: Int!
  posts: [Post]!
  followers: [User]!
}

type Post {
  id: ID!
  title: String!
  user: User!
}
`

Implementación del esquema: Tipos de datos

// Archivo `server/schemas/index.js`
const User = {
  id: (root) => root.id,
  name: (root) => root.name,
  age: (root) => root.age,
  posts: (root) => datosDePrueba.posts.filter(post => post.id === root.id),
  followers: (root) => root.followers,
};
const Post = {
  id: (root) => root.id,
  title: (root) => root.title,
  // Considere la función `reduce` como un `findById` de una librería de consulta de DB
  user: (root) => datosDePrueba.users.reduce((user, actual) => (
    user || actual.id === root.user_id && actual
  )),
};

Implementación del esquema: Consultas

// Archivo `server/schemas/index.js`
const Query = {
  // Este método se encarga de manejar las dos posibles
  // consultas de `users`
  users: (last) => (
    !last
      ? datosDePrueba.users
      : datosDePrueba.users.slice(datosDePrueba.users.length - last, datosDePrueba.users.length)
  ),
  followers: (id, last) => (
    datosDePrueba.users
      .filter(user => user.id === id)
      .slice(
        !last ? 0 : datosDePrueba.users.length - last,
        datosDePrueba.users.length
      )
  ),
  posts: (id) => (
    !id
      ? datosDePrueba.posts
      : datosDePrueba.posts.filter(post => post.user_id === id)
  ),
};

Implementación del esquema: Mutaciones

// Archivo `server/schemas/index.js`
const Mutation = {
  createUser: (_, { name, age }) => {
    const user = {
      id: datosDePrueba.users.length + 1,
      name,
      age,
      followers: [],
    };

    datosDePrueba.users.push(user);

    return user;
  },
};

Implementación del esquema: Exportar configuración

// Archivo `server/schemas/index.js`
const resolvers = {
  Query,
  Mutation,

  Person,
  Post,
};

module.exports = makeExecutableSchema({
  typeDefs,
  resolvers,
});

GraphQL: Implementación en el cliente

Creación de una consulta

// Archivo `client/components/App.vue`

<template>
  <div id="app">
    <h4 v-if="loading">Loading...</h4>
    <h4 v-if="!loading">Listado de autores</h4>
    <ul>
      <li
        v-for="user in users"
        :key="user.id"
      >{{user.name}}
    </li>
    </ul>
  </div>
</template>

<script>
import gql from 'graphql-tag';

const USERS = gql`
query AllUsersQuery {
  users {
    id
    name
  }
}
`

export default {
  data() {
    return {
      users: [],
      loading: 0,
    };
  },
  apollo: {
    users: {
      query: USERS,
    },
  },
};
</script>

Creación de una mutación

// Archivo `client/components/App.vue`

<template>
  <div id="app">
+    <h4 v-if="!loading">Alta de autor</h4>
+    <div>
+      <input
+        v-model="user.name"
+        type="text"
+      >
+      <input
+        v-model="user.age"
+        type="number"
+      >
+    </div>
    <h4 v-if="loading">Loading...</h4>
    <h4 v-if="!loading">Listado de autores</h4>
    <ul>
      <li
        v-for="user in users"
        :key="user.id"
      >{{user.name}}
    </li>
    </ul>
  </div>
</template>

<script>
import gql from 'graphql-tag';

const USERS = gql`
query AllUsersQuery {
  users {
    id
    name
  }
}
`

+ const CREATE_USER = gql`
+  mutation CreateUserMutation($name: String!, $age: Int!) {
+    createUser(
+      name: $name,
+      age: $age,
+    ) {
+      id
+    }
+  }
+ `

export default {
  data() {
    return {
      users: [],
+      user: {
+        name: '',
+        age: 0,
+      },
      loading: 0,
    };
  },
  apollo: {
    users: {
      query: USERS,
    },
  },
+  methods: {
+    createUser() {
+      const { user: { name, age } } = this.$data;
+
+      this.$apollo.mutate({
+        mutation: CREATE_USER,
+        variables: {
+          name,
+          age,
+        },
+      }).then(({ data: { createUser: { id }}}) => {
+        const user = {
+          id,
+          name,
+          age,
+        };
+
+        this.users = [...this.users, user];
+        this.user = {
+          name: '',
+          age: 0,
+        };
+      });
+    },
+  },
};
</script>

¿Qué es GraphQL?

By Luciano Graziani

¿Qué es GraphQL?

Introduce los conceptos claves de GraphQL de forma general para poder compararlo con REST. A su vez ofrece un ejemplo de cómo implementar una API.

  • 757