Creación de API de GraphQL con Node

Publicado: 2022-12-20

GraphQL es la nueva palabra de moda en el desarrollo de API. Si bien las API RESTful siguen siendo la forma más popular de exponer datos de aplicaciones, vienen con 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 datos en una API.

Esta guía explorará cómo construir un proyecto GraphQL Node.js. Usaremos GraphQL para crear una aplicación Todo en el marco web Express.js para Node.

¿Qué es GraphQL?

De la documentación oficial: “GraphQL es un lenguaje de consulta para API y un tiempo de ejecución para cumplir con esas consultas con sus datos existentes. GraphQL brinda una descripción completa y comprensible de los datos en su API, brinda a los clientes el poder de solicitar exactamente lo que necesitan y nada más, facilita la evolución de las API con el tiempo y habilita herramientas poderosas para desarrolladores”.

GraphQL es un tiempo de ejecución del lado del servidor para ejecutar consultas utilizando el sistema de tipos que definió para sus datos. Además, GraphQL no está vinculado a ninguna base de datos o motor de almacenamiento específico. En cambio, está respaldado por su código existente y almacén de datos. Puede obtener una comparación detallada de estas tecnologías con la guía GraphQL vs. RESTful API.

Para crear un servicio GraphQL, comienza definiendo tipos de esquema y creando campos usando esos tipos. A continuación, proporciona una función de resolución para que se ejecute en cada campo y escriba cada vez que el lado del cliente solicite datos.

Terminología de GraphQL

El sistema de tipos GraphQL se usa para describir qué datos se pueden consultar y qué datos se pueden manipular. Es el núcleo de GraphQL. Analicemos las diferentes formas en que podemos describir y manipular datos en GraphQ.

Tipos

Los tipos de objetos de GraphQL son modelos de datos que contienen campos fuertemente tipados. Debería haber un mapeo 1 a 1 entre sus modelos y los tipos de 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 de GraphQL. Debe definir un RootQuery que contendrá todas las consultas existentes por convención.

A continuación, definimos y asignamos las consultas a la API RESTful correspondiente:

 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 de GraphQL son solicitudes GET , las mutaciones son solicitudes POST , PUT , PATCH y DELETE que manipulan la API de GraphQL.

Pondremos todas las mutaciones en una sola RootMutation para demostrar:

 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 }

Notaste el uso de tipos de entrada para las mutaciones como UserInput , TodoInput . Siempre es una buena práctica definir siempre los tipos de entrada para crear y actualizar sus recursos.

Puede definir los tipos de entrada como el siguiente:

 input UserInput { firstname: String! lastname: String email: String! username: String! }

Resolutores

Los solucionadores 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 la base de datos para realizar las operaciones CRUD (crear, leer, actualizar, eliminar), acceder a un punto final interno de API RESTful o llamar a un microservicio para cumplir con la solicitud del cliente.

Puede crear un nuevo archivo resolvers.js y agregar 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 de 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 muestra cómo exponer tipos, consultas y mutaciones al mundo:

 schema { query: RootQuery mutation: RootMutation }

En el script anterior, incluimos RootQuery y RootMutation que creamos anteriormente para exponerlos al mundo.

¿Cómo funciona GraphQL con Nodejs y Expressjs?

GraphQL proporciona una implementación para todos los principales lenguajes de programación y Node.js no está exento. En el sitio web oficial de GraphQL, hay una sección para compatibilidad con JavaScript, y también hay otras implementaciones de GraphQL para simplificar la escritura y la codificación en GraphQL.

GraphQL Apollo proporciona una implementación para Node.js y Express.js y facilita comenzar con GraphQL.

En la siguiente sección, aprenderá cómo crear y desarrollar su primera aplicación GraphQL en Nodes.js y Express.js backend framework usando GraphQL Apollo.

Configuración de GraphQL con Express.js

Comenzar a construir un servidor API GraphQL con Express.js es sencillo. En esta sección, exploraremos cómo construir un servidor GraphQL.

Inicializar proyecto con Express

Primero, debe instalar y configurar un nuevo proyecto Express.js.

Cree una carpeta para su proyecto e instale Express.js usando 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 su proyecto.

A continuación, estructuraremos nuestro proyecto como se muestra en la imagen de abajo. Contendrá diferentes módulos para las características del proyecto, como usuarios, todos, etc.

Una lista de archivos en graphql-todo.
Archivos para graphql-todo .

Inicializar GraphQL

Comencemos instalando las dependencias de GraphQL Express.js. Ejecute el siguiente comando para instalar:

 npm install apollo-server-express graphql @graphql-tools/schema --save

Creación de esquemas y tipos

A continuación, crearemos un archivo index.js dentro de la carpeta de módulos y agregaremos 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;

Tutorial de código

Analicemos el fragmento de código y desglosémoslo:

Paso 1

Primero, importamos las bibliotecas requeridas y creamos tipos de consulta y mutación predeterminados. La consulta y la mutación solo establecen la versión de la API de GraphQL por ahora. Sin embargo, ampliaremos la consulta y la mutación para incluir otros esquemas a medida que avancemos.

Una interfaz de línea de comandos que muestra el código "const" para importar GraphQL y otras extensiones.
Importación de GraphQL y extensiones.
Paso 2:

Luego, creamos un nuevo tipo escalar para el tiempo y nuestro primer solucionador para la consulta y la mutación creadas anteriormente. Además, también generamos un esquema utilizando la función makeExecutableEchema .

El esquema generado incluye todos los demás esquemas que importamos y también incluirá más cuando los creemos e importemos.

¿Luchando con el tiempo de inactividad y los problemas de WordPress? ¡Kinsta es la solución de hospedaje diseñada para ahorrarle tiempo! Echa un vistazo a nuestras características
Una interfaz de línea de comandos que muestra el código "const" para crear nuestro tipo escalar y nuestro primer resolver.
Creando un tipo escalar para el tiempo, así como nuestro primer resolver.

El fragmento de código anterior muestra que importamos diferentes esquemas a la función makeExecutableEchema. Este enfoque nos ayuda a estructurar la aplicación para la complejidad. A continuación, vamos a crear los esquemas Todo y Usuario que importamos.

Creación de un esquema Todo

El esquema Todo muestra operaciones CRUD simples que pueden realizar los usuarios de la aplicación. A continuación se muestra el esquema que implementa la operación Todo CRUD.

 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 };

Tutorial de código

Analicemos el fragmento de código y desglosémoslo:

Paso 1:

Primero, creamos un esquema para nuestro Todo usando GraphQL type , input y extend . La palabra clave extend se usa para heredar y agregar nuevas consultas y mutaciones a la consulta raíz y la mutación existentes que creamos anteriormente.

Una interfaz de línea de comandos que muestra el esquema de nuestro script Todo, incluidas las nuevas entradas.
Creando el esquema para nuestro Todo.
Paso 2:

A continuación, creamos un resolver, que se usa para recuperar los datos correctos cuando se llama a una consulta o mutación en particular.

Una interfaz de línea de comandos que muestra el código para crear un resolver para nuestro Todo.
Creando un resolutor.

Con la función de resolución en su lugar, podemos crear métodos individuales para la lógica empresarial y la manipulación de la base de datos como se muestra en el ejemplo de create-todo.js .

Cree un archivo create-user.js en la carpeta <code>./mutations</code> y agregue la lógica comercial para crear un Todo nuevo en su 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 Todo nuevo en nuestra base de datos utilizando el ORM Sequelize. Puede obtener más información sobre Sequelize y cómo configurarlo con Node.js.

Puede seguir el mismo paso para crear muchos esquemas según su aplicación o puede clonar el proyecto completo desde GitHub.

A continuación, configuraremos el servidor con Express.js y ejecutaremos la aplicación Todo recién creada con GraphQL y Node.js.

Configuración y ejecución del servidor

Por último, configuraremos nuestro servidor utilizando la biblioteca apollo-server-express que instalamos anteriormente y lo configuraremos.

El apollo-server-express es un contenedor simple de Apollo Server para Express.js. Se recomienda porque se ha desarrollado para adaptarse al desarrollo de Express.js.

Usando los ejemplos que discutimos anteriormente, configuremos el servidor Express.js para que funcione con el apollo-server-express recién instalado.

Cree un archivo server.js en el directorio raíz y pegue 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`) );

En el código anterior, ha creado con éxito su primer servidor CRUD GraphQL para Todos y Usuarios. Puede iniciar su servidor de desarrollo y acceder al área de juegos usando http://localhost:3000/graphql. Si todo es exitoso, se le debe presentar la siguiente pantalla:

Una interfaz de desarrollo que muestra una consulta simple como respuesta.
La pantalla de verificación.

Resumen

GraphQL es una tecnología moderna respaldada por Facebook que simplifica el tedioso trabajo que implica la creación de API a gran escala con patrones de arquitectura RESTful.

Esta guía ha aclarado GraphQL y demostrado cómo desarrollar su primera API de GraphQL con Express.js.

Háganos saber lo que crea con GraphQL.