GraphQL es la nueva palabra de moda en el desarrollo de las API. Aunque las API RESTful siguen siendo la forma más popular de exponer los datos de las aplicaciones, tienen muchas limitaciones que GraphQL pretende resolver.
GraphQL es un lenguaje de consulta creado por Facebook, que se convirtió en un proyecto de código abierto en 2015. Ofrece una sintaxis intuitiva y flexible para describir y acceder a los datos de una API.
Esta guía explorará cómo construir un proyecto Node.js con GraphQL. Usaremos GraphQL para construir una aplicación Todo en el framework web Express.js para Node.
¿Qué Es GraphQL?
Según la documentación oficial: «GraphQL es un lenguaje de consulta para API y un tiempo de ejecución para realizar esas consultas con los datos existentes. GraphQL proporciona una descripción completa y comprensible de los datos de tu API, da a los clientes el poder de pedir exactamente lo que necesitan y nada más, facilita la evolución de las API con el tiempo y habilita potentes herramientas para desarrolladores.»
GraphQL es un tiempo de ejecución del lado del servidor para ejecutar consultas utilizando el sistema de tipos que hayas definido para tus datos. Además, GraphQL no está vinculado a ninguna base de datos o motor de almacenamiento específico. En su lugar, se apoya en tu código y almacén de datos existentes. Puedes obtener una comparación detallada de estas tecnologías con la guía GraphQL vs. API RESTful.
Para crear un servicio GraphQL, empieza definiendo tipos de esquema y creando campos que utilicen esos tipos. A continuación, proporcionas una función de resolución que se ejecutará en cada campo y tipo cada vez que el cliente solicite datos.
Terminología GraphQL
El sistema de tipos GraphQL se utiliza para describir qué datos se pueden consultar y qué datos se pueden manipular. Es el core de GraphQL. Vamos a discutir las diferentes formas en que podemos describir y manipular datos en GraphQL.
Tipos de Objeto
Los tipos de objetos GraphQL son modelos de datos que contienen campos fuertemente tipificados. Debe haber una correspondencia 1 a 1 entre tus modelos y los tipos GraphQL. A continuación se muestra un ejemplo de Tipo GraphQL:
type User {
id: ID! # The "!" means required
firstname: String
lastname: String
email: String
username: String
todos: [Todo] # Todo is another GraphQL type
}
Consultas
GraphQL Query define todas las consultas que un cliente puede ejecutar en la API GraphQL. Debes definir un RootQuery
que contendrá todas las consultas existentes por convención.
A continuación definimos y asignamos las consultas a la correspondiente API RESTful:
type RootQuery {
user(id: ID): User # Corresponds to GET /api/users/:id
users: [User] # Corresponds to GET /api/users
todo(id: ID!): Todo # Corresponds to GET /api/todos/:id
todos: [Todo] # Corresponds to GET /api/todos
}
Mutaciones
Si las Consultas GraphQL son peticiones GET
, las mutaciones son peticiones POST
, PUT
, PATCH
, y DELETE
que manipulan la API GraphQL.
Pondremos todas las mutaciones en un único RootMutation
para demostrarlo:
type RootMutation {
createUser(input: UserInput!): User # Corresponds to POST /api/users
updateUser(id: ID!, input: UserInput!): User # Corresponds to PATCH /api/users
removeUser(id: ID!): User # Corresponds to DELETE /api/users
createTodo(input: TodoInput!): Todo
updateTodo(id: ID!, input: TodoInput!): Todo
removeTodo(id: ID!): Todo
}
Ya has comprobado el uso de tipos -input
para las mutaciones como UserInput
, TodoInput
. Siempre es la mejor práctica para definir los tipos de entrada para la creación y actualización de tus recursos.
Puedes definir los tipos de entrada como se indica a continuación:
input UserInput {
firstname: String!
lastname: String
email: String!
username: String!
}
Resolvers
Los Resolvers le dicen a GraphQL qué hacer cuando se solicita cada consulta o mutación. Es una función básica que hace el trabajo duro de acceder a la capa de base de datos para realizar las operaciones CRUD (crear, leer, actualizar, eliminar), acceder a un punto final RESTful API interno o llamar a un microservicio para satisfacer la solicitud del cliente.
Puedes crear un nuevo archivo resolvers.js y añadir el siguiente código:
import sequelize from '../models';
export default function resolvers () {
const models = sequelize.models;
return {
// Resolvers for Queries
RootQuery: {
user (root, { id }, context) {
return models.User.findById(id, context);
},
users (root, args, context) {
return models.User.findAll({}, context);
}
},
User: {
todos (user) {
return user.getTodos();
}
},
}
// Resolvers for Mutations
RootMutation: {
createUser (root, { input }, context) {
return models.User.create(input, context);
},
updateUser (root, { id, input }, context) {
return models.User.update(input, { ...context, where: { id } });
},
removeUser (root, { id }, context) {
return models.User.destroy(input, { ...context, where: { id } });
},
// ... Resolvers for Todos go here
}
}
Esquema
El esquema GraphQL es lo que GraphQL expone al mundo. Por lo tanto, los tipos, consultas y mutaciones se incluirán dentro del esquema para ser expuestos al mundo.
A continuación se explica cómo exponer al mundo los tipos, consultas y mutaciones:
schema {
query: RootQuery
mutation: RootMutation
}
En el script anterior, incluimos el RootQuery
y el RootMutation
que creamos anteriormente para exponerlos al mundo.
¿Cómo Funciona GraphQL con Node.js y Express.js?
GraphQL proporciona una implementación para los principales lenguajes de programación, y Node.js no está exento. En el sitio web oficial de GraphQL, hay una sección para la compatibilidad con JavaScript, y además, hay otras implementaciones de GraphQL para simplificar la escritura y la programación.
GraphQL Apollo proporciona una implementación para Node.js y Express.js y facilita los primeros pasos con GraphQL.
En la siguiente sección aprenderás a crear y desarrollar tu primera aplicación GraphQL en Node.js y el framework backend Express.js utilizando GraphQL Apollo.
Configurar GraphQL con Express.js
Construir un servidor API GraphQL con Express.js es sencillo para comenzar. En esta sección, exploraremos cómo construir un servidor GraphQL.
Inicializar el Proyecto con Express
En primer lugar, necesitas instalar y configurar un nuevo proyecto Express.js. Crea una carpeta para tu proyecto e instala Express.js utilizando este comando:
cd <project-name> && npm init -y
npm install express
El comando anterior crea un nuevo archivo package.json e instala la biblioteca Express.js en tu proyecto.
A continuación, estructuraremos nuestro proyecto como se muestra en la siguiente imagen. Contendrá diferentes módulos para las características del proyecto, como usuarios, todos, etc.
Inicializar GraphQL
Comencemos instalando las dependencias de GraphQL Express.js. Ejecuta el siguiente comando para instalar:
npm install apollo-server-express graphql @graphql-tools/schema --save
Crear Esquemas y Tipos
A continuación, vamos a crear un archivo index.js dentro de la carpeta modules y a añadir el siguiente fragmento de código:
const { gql } = require('apollo-server-express');
const users = require('./users');
const todos = require('./todos');
const { GraphQLScalarType } = require('graphql');
const { makeExecutableSchema } = require('@graphql-tools/schema');
const typeDefs = gql`
scalar Time
type Query {
getVersion: String!
}
type Mutation {
version: String!
}
`;
const timeScalar = new GraphQLScalarType({
name: 'Time',
description: 'Time custom scalar type',
serialize: (value) => value,
});
const resolvers = {
Time: timeScalar,
Query: {
getVersion: () => `v1`,
},
};
const schema = makeExecutableSchema({
typeDefs: [typeDefs, users.typeDefs, todos.typeDefs],
resolvers: [resolvers, users.resolvers, todos.resolvers],
});
module.exports = schema;
Recorrido del Código
Vamos a trabajar con el fragmento de código y a desglosarlo:
Paso 1
En primer lugar, importamos las bibliotecas necesarias y creamos los tipos de consulta y mutación predeterminados. Por ahora, la consulta y la mutación sólo establecen la versión de la API GraphQL. Sin embargo, ampliaremos la consulta y la mutación para incluir otros esquemas a medida que avancemos.
Paso 2:
A continuación creamos un nuevo tipo escalar para el tiempo y nuestro primer resolver para la consulta y mutación creadas anteriormente. Además, también generamos un esquema utilizando la función makeExecutableSchema
.
El esquema generado incluye todos los demás esquemas que importamos y también incluirá más cuando los creemos e importemos.
El fragmento de código anterior muestra que hemos importado diferentes esquemas en la función makeExecutableSchema. Este enfoque nos ayuda a estructurar la aplicación para que sea más compleja. A continuación, vamos a crear los esquemas Todo y Usuario que hemos importado.
Creación del Esquema Todo
El esquema Todo muestra operaciones CRUD sencillas que pueden realizar los usuarios de la aplicación. A continuación se muestra el esquema que implementa la operación CRUD Todo.
const { gql } = require('apollo-server-express');
const createTodo = require('./mutations/create-todo');
const updateTodo = require('./mutations/update-todo');
const removeTodo = require('./mutations/delete-todo');
const todo = require('./queries/todo');
const todos = require('./queries/todos');
const typeDefs = gql`
type Todo {
id: ID!
title: String
description: String
user: User
}
input CreateTodoInput {
title: String!
description: String
isCompleted: Boolean
}
input UpdateTodoInput {
title: String
description: String
isCompleted: Boolean
} extend type Query {
todo(id: ID): Todo!
todos: [Todo!]
}
extend type Mutation {
createTodo(input: CreateTodoInput!): Todo
updateTodo(id: ID!, input: UpdateTodoInput!): Todo
removeTodo(id: ID!): Todo
}
`;
// Provide resolver functions for your schema fields
const resolvers = {
// Resolvers for Queries
Query: {
todo,
todos,
},
// Resolvers for Mutations
Mutation: {
createTodo,
updateTodo,
removeTodo,
},
};
module.exports = { typeDefs, resolvers };
Recorrido del Código
Analicemos el fragmento de código y desglosémoslo:
Paso 1:
En primer lugar, creamos un esquema para nuestro Todo utilizando GraphQL type
, input
, y extend
. La palabra clave extend
se utiliza para heredar y añadir nuevas consultas y mutaciones a la consulta root y mutación existentes que creamos anteriormente.
Paso 2:
A continuación, creamos un resolver, que se utiliza para recuperar los datos correctos cuando se llama a una consulta o mutación determinada.
Una vez creada la función resolver, podemos crear métodos individuales para la lógica de negocio y la manipulación de la base de datos, como se muestra en el ejemplo create-todo.js.
Crea un archivo create-user.js en la carpeta ./mutations
y añade la lógica de negocio para crear un nuevo Todo en tu base de datos.
const models = require('../../../models');
module.exports = async (root, { input }, context) => {
return models.todos.push({ ...input });
};
El fragmento de código anterior es una forma simplificada de crear un nuevo Todo en nuestra base de datos utilizando el ORM Sequelize. Puedes obtener más información sobre Sequelize y cómo configurarlo con Node.js.
Puedes seguir el mismo paso para crear muchos esquemas dependiendo de tu aplicación o puedes clonar el proyecto completo desde GitHub.
A continuación, vamos a configurar el servidor con Express.js y ejecutar la aplicación Todo recién creada con GraphQL y Node.js.
Configurar y Ejecutar el Servidor
Por último, vamos a configurar nuestro servidor utilizando la biblioteca apollo-server-express
que instalamos anteriormente y configurarlo.
apollo-server-express
es un simple wrapper del Servidor Apollo para Express.js, se recomienda porque ha sido desarrollado para adaptarse en el desarrollo de Express.js.
Utilizando los ejemplos que hemos comentado anteriormente, vamos a configurar el servidor Express.js para que funcione con la recién instalada apollo-server-express
.
Crea un archivo server.js en el directorio root y pega el siguiente código:
const express = require('express');
const { ApolloServer } = require('apollo-server-express');
const schema = require('./modules');
const app = express();
async function startServer() {
const server = new ApolloServer({ schema });
await server.start();
server.applyMiddleware({ app });
}
startServer();
app.listen({ port: 3000 }, () =>
console.log(`Server ready at http://localhost:3000`)
);
Con el código anterior, has creado con éxito tu primer servidor CRUD GraphQL para Todos y Usuarios. Puedes iniciar tu servidor de desarrollo y acceder al playground utilizando http://localhost:3000/graphql. Si todo va bien, debería aparecerte la siguiente pantalla:
Resumen
GraphQL es una tecnología moderna respaldada por Facebook que simplifica el tedioso trabajo que supone crear API a gran escala con patrones arquitectónicos RESTful.
Esta guía ha esclarecido GraphQL y demostrado cómo desarrollar tu primera API GraphQL con Express.js.
Haznos saber qué construyes utilizando GraphQL en los comentarios a continuación.
Deja una respuesta