Cómo crear una API GraphQL segura de Node.js

Publicado: 2022-03-11

En este artículo, nuestro objetivo es presentar una guía rápida sobre cómo crear una API GraphQL segura de Node.js.

Algunas preguntas que pueden venir a la mente podrían ser:

  • ¿Cuál es el propósito de usar una API de GraphQL?
  • ¿Qué es una API de GraphQL?
  • ¿Qué es una consulta GraphQL?
  • ¿Cuál es el beneficio de GraphQL?
  • ¿Es GraphQL mejor que REST?
  • ¿Por qué usamos Node.js?

Todas esas son preguntas válidas, pero antes de responderlas, debemos sumergirnos en una breve descripción del estado actual del desarrollo web:

  • Casi todas las soluciones que encontrará hoy utilizan algún tipo de interfaz de programación de aplicaciones (API).
  • Incluso si solo usa una red social, como Facebook o Instagram, todavía está conectado a un front-end que consume una API.
  • Si tiene curiosidad, encontrará que casi todos los servicios de entretenimiento en línea usan un tipo diferente de API, incluidos servicios como Netflix, Spotify y YouTube.

En prácticamente todos los escenarios, encontrará una API que no necesita conocer en detalle, por ejemplo, no necesita saber cómo se crearon y no necesita usar la misma tecnología que solían usar. capaz de integrarlo en su propio sistema. La API que se proporciona le permite ofrecer una forma de comunicación entre servicios en un estándar común que tanto el servicio como el cliente pueden comunicarse sin tener que depender de una pila de tecnología específica.

Con una API bien estructurada, es posible tener una API sólida, mantenible y escalable que pueda servir a múltiples tipos de clientes y aplicaciones front-end.

Dicho esto, ¿qué es la API GraphQL?

GraphQL es un lenguaje de consulta para API, desarrollado para uso interno en Facebook y publicado para uso público en 2015. Admite lectura, escritura y actualizaciones en tiempo real. También es de código abierto y comúnmente se compara con REST y otras arquitecturas. Se basa, en pocas palabras, en:

  • Consultas de GraphQL : esto permite que el cliente lea y manipule cómo se deben recibir los datos.
  • Mutaciones de GraphQL : así es como se escriben datos en el servidor. Es la convención de GraphQL sobre cómo escribir datos en el sistema.

Aunque se supone que este artículo demuestra un escenario simple pero real sobre cómo crear y usar las API de GraphQL, no proporcionaremos una introducción detallada a GraphQL. La razón es simple, ya que el equipo de GraphQL proporciona documentación completa y enumera varias prácticas recomendadas en su Introducción a GraphQL.

¿Qué es una consulta GraphQL?

Como se describió anteriormente, una consulta es la forma en que un cliente puede leer y manipular datos de la API. Puede pasar el tipo de un objeto y seleccionar qué tipo de campos desea recibir. Una consulta simple sería como la siguiente:

 query{ users{ firstName, lastName } }

En esta consulta, intentamos llegar a todos los usuarios del esquema de nuestros usuarios, pero solo recibimos firstName y lastName . El resultado de esta consulta sería, por ejemplo:

 { "data": { "users": [ { "firstName": "Marcos", "lastName": "Silva" }, { "firstName": "Paulo", "lastName": "Silva" } ] } }

Es bastante simple para el uso del cliente.

¿Cuál es el propósito de usar una API de GraphQL?

El propósito de crear una API es la capacidad de tener un software como servicio que pueda ser integrado por otros servicios externos. Incluso si su aplicación es consumida por un solo front-end, puede considerar este front-end como un servicio externo, y para eso, podrá trabajar en diferentes proyectos cuando la comunicación entre los dos se proporciona a través de la API.

Si trabaja en un equipo grande, se puede dividir para crear un equipo de front-end y back-end, lo que permite que ambos usen la misma tecnología y faciliten su trabajo. Al diseñar una API, es importante elegir la mejor opción para el proyecto y lo que lo acerca a la solución deseada.

En este artículo, nos centraremos en un esqueleto para crear una API que use GraphQL.

¿Es GraphQL mejor que REST?

Puede que sea un poco evasivo, pero no puedo evitarlo: eso depende .

GraphQL es un enfoque que se adapta muy bien a varios escenarios. REST es un enfoque de arquitectura que también se ha probado en varios escenarios. Hoy en día, hay toneladas de artículos que explican por qué uno es mejor que el otro o por qué debería usar solo REST en lugar de GraphQL. Y también, muchas maneras en que puede usar GraphQL internamente y aún así mantener los puntos finales de la API como una arquitectura basada en REST.

La mejor orientación sería conocer los beneficios de cada enfoque, analizar la solución que está creando, evaluar qué tan cómodo se siente su equipo trabajando con la solución y evaluar si podrá o no guiar a su equipo para aprender y ponerse al día. velocidad rápido antes de elegir entre enfoques.

Este artículo es más una guía práctica que una comparación subjetiva de GraphQL y REST. En caso de que desee leer una comparación detallada de los dos, le sugiero que consulte otro de nuestros artículos, GraphQL vs. REST: un tutorial de GraphQL.

En el artículo de hoy, nos centraremos en crear una API de GraphQL con Node.js.

¿Por qué usamos Node.js?

GraphQL tiene varias bibliotecas diferentes que puede usar. A los efectos de este artículo, decidimos seguir con la idea de usar JavaScript con Node.js debido a su uso generalizado y al hecho de que Node.js permite a los desarrolladores usar una sintaxis familiar para el desarrollo del lado del servidor.

También es útil compararlo con nuestro enfoque con una API basada en REST, similar a la que se demostró en otro artículo del blog de ingeniería de Toptal: Creación de una API REST segura en Node.js. Este artículo también muestra el uso de Node.js con Express para desarrollar una API REST básica que le permitirá comparar algunas diferencias entre estos dos enfoques. Node.js también se diseñó con aplicaciones de red escalables, una comunidad global y varias bibliotecas de código abierto que puede encontrar en el sitio web de npm.

¡Esta vez, vamos a mostrar cómo crear una API de esqueleto con GraphQL, Node.js y Express!

Tutorial práctico de GraphQL

Como se describió anteriormente, crearemos una idea básica para la API de GraphQL y deberá conocer los conceptos básicos de Node.js y Express antes de continuar. El código fuente del proyecto realizado para este ejemplo de GraphQL está disponible aquí.

Vamos a manejar dos tipos de recursos:

  • Usuarios, para lo cual manejaremos un CRUD básico.
  • Productos, para los cuales tendremos un poco de detalle para mostrar más del poder de GraphQL.

Los usuarios contendrán la siguiente estructura:

  • identificación
  • primer nombre
  • apellido
  • Email
  • clave
  • nivel de permiso

Los productos contendrán la siguiente estructura:

  • identificación
  • nombre
  • descripción
  • precio

En cuanto al estándar de codificación, usaremos TypeScript para este proyecto. En el archivo fuente, podrá configurar todo para comenzar a codificar con TypeScript.

¡Codifiquemos!

En primer lugar, asegúrese de tener instalada la última versión de Node.js. En el momento de la publicación, la versión actual es 10.15.3, según Nodejs.org.

Inicializar el proyecto

Comencemos en una nueva carpeta que podemos node-graphql . Allí, podemos abrir una terminal o una consola Git CLI y comenzar la magia usando el siguiente comando: npm init .

Configurando nuestras Dependencias y TypeScript

Para acelerar el proceso, reemplazar su package.json con lo siguiente en nuestro repositorio de Git debería contener todas las dependencias necesarias:

 { "name": "node-graphql", "version": "1.0.0", "description": "", "main": "dist/index.js", "scripts": { "tsc": "tsc", "start": "npm run tsc && node ./build/app.js" }, "author": "", "license": "ISC", "dependencies": { "@types/express": "^4.16.1", "@types/express-graphql": "^0.6.2", "@types/graphql": "^14.0.7", "express": "^4.16.4", "express-graphql": "^0.7.1", "graphql": "^14.1.1", "graphql-tools": "^4.0.4" }, "devDependencies": { "tslint": "^5.14.0", "typescript": "^3.3.4000" } }

Con el package.json actualizado.json, solo presione la terminal nuevamente y use: npm install . Instalará todas las dependencias necesarias para ejecutar esta API GraphQL dentro de Node.js y Express.

La siguiente pieza es configurar nuestro modo TypeScript. Necesitamos un archivo llamado tsconfig.json en nuestra carpeta raíz con lo siguiente:

 { "compilerOptions": { "target": "ES2016", "module": "commonjs", "outDir": "./build", "strict": true, "esModuleInterop": true } }

La lógica del código para esta configuración estará presente en la carpeta de la aplicación. Allí podemos crear un archivo app.ts y para pruebas básicas agregar el siguiente código allí:

 console.log('Hello Graphql Node API tutorial');

Según nuestra configuración, ahora podemos ejecutar npm start y esperar una compilación y poder probar que todo funciona correctamente. En la consola de su terminal, debería ver nuestro "Tutorial de la API de Hello GraphQL Node". En la escena posterior, la configuración básicamente compila el código TypeScript en JavaScript puro y luego ejecuta nuestra compilación en la carpeta de build .

Ahora configuremos un esqueleto básico para nuestra API GraphQL. Para comenzar nuestro proyecto, vamos a agregar tres importaciones básicas:

  • Rápido
  • Express-graphql
  • Graphql-herramientas

Vamos a empezar a poner todo junto:

 import express from 'express'; import graphqlHTTP from 'express-graphql'; import {makeExecutableSchema} from 'graphql-tools';

Ahora deberíamos poder comenzar a codificar un poco. El siguiente paso es manejar nuestra aplicación en Express y la configuración básica de GraphQL como:

 import express from 'express'; import graphqlHTTP from 'express-graphql'; import {makeExecutableSchema} from 'graphql-tools'; const app: express.Application = express(); const port = 3000; let typeDefs: any = [` type Query { hello: String } type Mutation { hello(message: String) : String } `]; let helloMessage: String = 'World!'; let resolvers = { Query: { hello: () => helloMessage }, Mutation: { hello: (_: any, helloData: any) => { helloMessage = helloData.message; return helloMessage; } } }; app.use( '/graphql', graphqlHTTP({ schema: makeExecutableSchema({typeDefs, resolvers}), graphiql: true }) ); app.listen(port, () => console.log(`Node Graphql API listening on port ${port}!`));

Lo que estamos haciendo es:

  • Habilitando el puerto 3000 para nuestra aplicación de servidor Express.
  • Definiendo qué consultas y mutaciones queremos usar como ejemplo rápido.
  • Definición de cómo van a funcionar las consultas y mutaciones.

Bien, pero ¿qué está pasando con typeDefs y resolutores, así como con la relación con consultas y mutaciones?

  • typeDefs : la definición de nuestro esquema de lo que podemos esperar de las consultas y mutaciones.
  • Resolvedores : en lugar de la expectativa de campos o parámetros requeridos, aquí definimos las funciones y comportamientos de cómo deberían funcionar las consultas y mutaciones.
  • Queries - Los "gets" que queremos leer del servidor.
  • Mutaciones : nuestras solicitudes que van a afectar los datos que tenemos en nuestro propio servidor.

Ahora, ejecutemos npm start de nuevo para ver qué tenemos allí. Esperamos que la aplicación se ejecute con el siguiente mensaje: Node Graphql API listening on port 3000!

Ahora podemos intentar consultar y probar la API de GraphQL en nuestro propio servidor a través de: http://localhost:3000/graphql

Tutorial de GraphQL: prueba de servidor

Genial, ahora podemos escribir nuestra primera consulta propia que se definió como "hola".

Tutorial de GraphQL: primera consulta

Tenga en cuenta que la forma en que lo definimos en typeDefs , la página puede ayudarnos a construir la consulta.

Eso es genial, pero ¿cómo podemos cambiar el valor? ¡Mutaciones!

Ahora, veamos qué sucede cuando cambiamos nuestro valor en memoria con una mutación:

Tutorial de GraphQL: demostración de mutación

Ahora podemos realizar operaciones CRUD básicas con nuestra API GraphQL Node.js. Avancemos con nuestro código ahora.

productos

Para productos, usaremos un módulo llamado productos. Como un esfuerzo por simplificar este artículo, vamos a utilizar una base de datos en memoria solo para demostración. Definiremos un modelo y un servicio para gestionar productos.

Nuestro modelo se basará de la siguiente manera:

 export class Product { private id: Number = 0; private name: String = ''; private description: String = ''; private price: Number = 0; constructor(productId: Number, productName: String, productDescription: String, price: Number) { this.id = productId; this.name = productName; this.description = productDescription; this.price = price; } }

El servicio que se comunicará con GraphQL se definirá como:

 export class ProductsService { public products: any = []; configTypeDefs() { let typeDefs = ` type Product { name: String, description: String, id: Int, price: Int } `; typeDefs += ` extend type Query { products: [Product] } `; typeDefs += ` extend type Mutation { product(name:String, id:Int, description: String, price: Int): Product! }`; return typeDefs; } configResolvers(resolvers: any) { resolvers.Query.products = () => { return this.products; }; resolvers.Mutation.product = (_: any, product: any) => { this.products.push(product); return product; }; } }

Usuarios

Para los usuarios, seguiremos la misma estructura que el módulo de productos. Tendremos un modelo y un servicio para los usuarios. El modelo se definirá como:

 export class User { private id: Number = 0; private firstName: String = ''; private lastName: String = ''; private email: String = ''; private password: String = ''; private permissionLevel: Number = 1; constructor(id: Number, firstName: String, lastName: String, email: String, password: String, permissionLevel: Number) { this.id = id; this.firstName = firstName; this.lastName = lastName; this.email = email; this.password = password; this.permissionLevel = permissionLevel; } }

Mientras tanto, nuestro servicio será como:

 const crypto = require('crypto'); export class UsersService { public users: any = []; configTypeDefs() { let typeDefs = ` type User { firstName: String, lastName: String, id: Int, password: String, permissionLevel: Int, email: String } `; typeDefs += ` extend type Query { users: [User] } `; typeDefs += ` extend type Mutation { user(firstName:String, lastName: String, password: String, permissionLevel: Int, email: String, id:Int): User! }`; return typeDefs; } configResolvers(resolvers: any) { resolvers.Query.users = () => { return this.users; }; resolvers.Mutation.user = (_: any, user: any) => { let salt = crypto.randomBytes(16).toString('base64'); let hash = crypto.createHmac('sha512', salt).update(user.password).digest("base64"); user.password = hash; this.users.push(user); return user; }; } }

Como recordatorio, el código fuente está disponible para su uso desde este enlace.

Ahora podemos jugar y probar nuestro código. npm start . Tendremos el servidor ejecutándose en el puerto 3000. Ahora podemos acceder a GraphQL para realizar pruebas en http://localhost:3000/graphql.

Probemos una mutación para agregar un artículo a nuestra lista de productos:

Demostración de mutación GraphQL de Node.js

Para probar si funcionó, ahora usaremos una consulta de productos, pero recibiendo solo id , name y price :

 query{ products{ id, name, price } } The response will be: { "data": { "products": [ { "id": 100, "name": "My amazing product", "price": 400 } ] } }

Y eso es; el producto funciona como se esperaba. Ahora podemos jugar y cambiar los campos si queremos. Puedes intentar añadir una descripción:

 query{ products{ id, name, description, price } }

Ahora podemos tener las descripciones de nuestros productos. Probemos los usuarios ahora.

 mutation{ user(id:200, firstName:"Marcos", lastName:"Silva", password:"amaz1ingP4ss", permissionLevel:9, email:"[email protected]") { id } }

Y una consulta será como:

 query{ users{ id, firstName, lastName, password, email } }

Con una respuesta como:

 { "data": { "users": [ { "id": 200, "firstName": "Marcos", "lastName": "Silva", "password": "kpj6Mq0tGChGbZ+BT9Nw6RMCLReZEPPyBCaUS3X23lZwCCp1Ogb94/oqJlya0xOBdgEbUwqRSuZRjZGhCzLdeQ==", "email": "[email protected]" } ] } }

¡Y ahora nuestro esqueleto de GraphQL está listo! Hay toneladas de pasos desde aquí hacia una API útil y completamente funcional, pero el núcleo básico ya está configurado.

Resumen y reflexiones finales

Incluso los bordes para acortar, el artículo es bastante extenso con mucha información básica sobre el desarrollo de una API GraphQL Node.js.

Repasemos lo que hemos cubierto hasta ahora:

  • Uso de Node.js con Express y GraphQL para crear una API de GraphQL;
  • Uso básico de GraphQL;
  • Uso básico de consultas y mutaciones;
  • Enfoque básico para crear módulos para su proyecto;
  • Probando nuestra API GraphQL;

Para centrarnos más en el lado del desarrollo, evitamos varios elementos importantes que se pueden resumir brevemente de la siguiente manera:

  • Validaciones para artículos nuevos;
  • Manejar correctamente los errores con un servicio de errores genérico;
  • Validar campos que un usuario puede utilizar en cada solicitud con un servicio genérico;
  • Agregue un interceptor JWT para proteger la API;
  • Maneje el hash de contraseña con un enfoque más efectivo;
  • Agregue pruebas unitarias y de integración;

Recuerda que tenemos el código fuente completo en este enlace de Git. ¡Siéntase libre de usar, bifurcar, abrir problemas, hacer solicitudes de extracción y jugar con él! Tenga en cuenta que todas las normas y sugerencias hechas en este artículo no están talladas en piedra.

Este es solo uno de los muchos enfoques que se pueden usar para comenzar a diseñar su propia API de GraphQL. Además, asegúrese de leer y explorar GraphQL con más detalle, aprendiendo lo que tiene para ofrecer y cómo puede mejorar aún más sus API.