Cómo crear una API GraphQL con Nest.js

En este artículo, aprenderá cómo crear una API GraphQL con NestJS aprovechando su arquitectura limpia y seguridad de tipos.

NestJS es un marco de NodeJS que ayuda a los usuarios a crear aplicaciones backend escalables de nivel empresarial al proporcionar seguridad de tipos y un patrón arquitectónico estricto.

GraphQL es un lenguaje de consulta para interfaces de programación de aplicaciones (API, del inglés “application programming interfaces”) muy utilizado como alternativa a REST por su velocidad y estabilidad, que logra respondiendo a las consultas con precisión los datos que solicitan.

Esta característica de GraphQL resuelve el problema de recuperación excesiva e insuficiente que plantea REST.

Configurando su entorno de desarrollo

Si no tiene la CLI de Nest.js instalada globalmente en su sistema, ejecute el siguiente comando para instalarla:

npm i -g @nestjs/cli

La CLI de Nest.js contiene varios comandos que le ayudan a generar código repetitivo para facilitar el proceso de desarrollo.

A continuación, cree el proyecto y genere los archivos de inicio necesarios ejecutando el siguiente comando:

nest new graphql-api

Luego, ejecute el siguiente comando para ingresar al directorio de su proyecto:

cd graphql-api

A continuación, instale las dependencias requeridas ejecutando el siguiente comando:

npm i @nestjs/graphql @nestjs/apollo graphql apollo-server-express

Las dependencias instaladas anteriormente incluyen:

  • nest/graphql
  • nestjs/apollo
  • graphql
  • apollo-server-express

A continuación, instale el paquete TypeORM y el controlador de la base de datos sqlite ejecutando el siguiente comando:

npm install @nestjs/typeorm typeorm sqlite3

Necesitará los paquetes instalados anteriormente para conectarse, leer y escribir una base de datos.

Configurar GraphQL en Nest.js

Nest.js ofrece dos formas de crear una API GraphQL: enfoques de código primero y esquema primero.

El enfoque de código primero presenta el uso de decoradores y clases de TypeScript para generar el esquema GraphQL correspondiente.

El enfoque de esquema primero presenta el uso de archivos del lenguaje de definición de esquemas (SDL) GraphQL, mientras que Nest.js genera definiciones de TypeScript utilizando clases o interfaces basadas en el esquema GraphQL.

Este tutorial presentará el uso del enfoque de código primero para evitar el cambio de contexto entre diferentes lenguajes.

Para utilizar el primer método de código, navegue hasta el archivo de entrada de su aplicación, app.module.ts, e importe GraphQLModule desde @nestjs/graphql. De esta forma:

import { GraphQLModule } from '@nestjs/graphql';

Luego, en su matriz imports, llame al método forRoot de GraphQLModule. Este método toma un objeto de configuración como argumento.

Las opciones de configuración se pasan a través del controlador GraphQL subyacente (este tutorial presenta el uso del controlador Apollo).

Establezca la propiedad autoSchemaFile en el objeto de configuración en schema.gql. De esta forma:

//app.module.ts
GraphQLModule.forRoot({ autoSchemaFile: 'schema.gql' }),

La propiedad autoSchemaFile es la ruta del archivo donde se almacenará el esquema generado automáticamente.

Conexión a una base de datos

Para conectar su aplicación a una base de datos, navegue hasta su archivo app.module.ts e importe TypeOrmModule desde @nestjs/typeorm.

De esta forma:

import { TypeOrmModule } from '@nestjs/typeorm';

A continuación, agregue el siguiente bloque de código a su matriz imports:

TypeOrmModule.forRoot({
      type: 'sqlite',
      database: ':memory:',
      entities: ['dist/**/*.entity{.ts,.js}'],
      synchronize: true,
    }),

El bloque de código anterior crea una conexión de base de datos entre su aplicación y una base de datos Sqlite y comparte la conexión entre todos los módulos de su aplicación.

Generando recursos

A continuación, cree un módulo para organizar su código ejecutando el siguiente comando:

nest g module blog

El comando anterior genera un módulo de blog para su aplicación.

A continuación, ejecute el siguiente comando para crear un servicio:

nest g service blog

El bloque de código anterior genera y registra un servicio en su aplicación. Este servicio contendrá y manejará toda la lógica empresarial de su aplicación.

A continuación, ejecute el siguiente comando para crear un solucionador:

nest g resolver

El bloque de código anterior genera y registra un solucionador en su aplicación. Un solucionador especifica instrucciones para convertir consultas y mutaciones GraphQL en datos y devuelve la forma de los datos especificados en un esquema de forma sincrónica o asincrónica.

Creando una entidad

Una entidad es una colección de campos que especifica cómo se almacenan los datos en una base de datos.

Para crear una Entidad, en la carpeta de su módulo (blog), cree un archivo de entidad siguiendo la convención <name>.entity.ts, por ejemplo blog.entity.ts.

En su archivo de entidad, importe Column, Entity, y  PrimaryGeneratedColumn desde typeorm. De esta forma:

import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';

El decorador Column agrega los campos que anota como columnas en una base de datos. El decorador Entity marca la clase que anota como una entidad.

El decorador PrimaryGeneratedColumn marca la propiedad que anota como identificador único para su tabla y la genera automáticamente.

A continuación, cree y exporte una clase, Blog. Luego, anota la clase con el decorador Entity para marcarla como una entidad. De esta forma:

@Entity()
export class Blog {}

Dado que este tutorial presenta una API de blog, agregue una propiedad de identificación, título, cuerpo, autor y fecha a su clase.

Luego, anote cada una de las propiedades con el decorador Column excepto la propiedad id. Anote la propiedad id con el decorador PrimaryGeneratedColumn.

De esta forma:

import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';

@Entity()
export class Blog {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  title: string;

  @Column()
  body: string;

  @Column()
  author: string;

  @Column()
  date: string;
}

Crear un tipo de objeto

Un tipo de objeto es una definición de esquema GraphQL que representa el objeto con el que el cliente necesita interactuar. Por ejemplo, si está creando una API de blog, deberá definir el esquema/entidad del blog con una clase TypeScript y anotar cada campo con los decoradores adecuados.

A continuación, en su archivo de entidad, importe el tipo Int y los decoradores Field y ObjectType de @nestjs/graphql. De esta forma:

import { Field, Int, ObjectType } from '@nestjs/graphql';

El decorador Field marca una propiedad de clase específica como un campo GraphQL. Sólo las propiedades anotadas con este decorador se definirán en el esquema. Acepta una función de tipo de retorno opcional y un objeto de configuración como argumentos.

El tipo Int es necesario para eliminar posibles ambigüedades entre los sistemas de tipos TypeScript y GraphQL. Solo es necesario para propiedades con valores numéricos, ya que pueden ser números enteros o flotantes.

El decorador ObjectType marca una clase como tipo GraphQL.

A continuación, anote su clase Blog con el decorador ObjectType para marcarla como un tipo GraphQL. De esta forma:

@ObjectType()
export class Blog {}

Finalmente, especifique las propiedades requeridas de su tipo dentro de la clase y anote cada una de ellas con el decorador Field. Por ejemplo:

//blog.entity.ts
import { Field, Int, ObjectType } from '@nestjs/graphql';
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';

@Entity()
@ObjectType()
export class Blog {
  @PrimaryGeneratedColumn()
  @Field((type) => Int)
  id: number;

  @Column()
  @Field()
  title: string;

  @Column()
  @Field()
  body: string;

  @Column()
  @Field()
  author: string;

  @Column()
  @Field()
  date: string;
}

Su tipo de objeto terminado debería verse como el bloque de código de arriba.

Tenga en cuenta que la id con un tipo number en el bloque de código anterior se especificó explícitamente como un número entero para evitar ambigüedad entre los sistemas de tipos TypeScript y GraphQL.

Finalmente, inyecte su Entidad en su aplicación para crear una capa de acceso entre su aplicación y su base de datos.

Para lograr esto, navegue hasta su archivo blog.module.ts, importe TypeOrmModule desde @nestjs/typeorm e importe su entidad Blog.

Luego, agregue el siguiente bloque de código a su decorador de módulos:

imports: [TypeOrmModule.forFeature([Blog])],

El bloque de código anterior crea un repositorio que actuará como una capa de acceso entre su aplicación y la base de datos.

Creando un servicio

Navegue hasta su archivo de servicio generado previamente, blog.service.ts, e importe InjectRepository desde @nestjs/typeorm y su tipo de objeto.

De esta forma:

import { InjectRepository } from '@nestjs/typeorm';
import { Blog } from './blog.entity';

A continuación, agregue el siguiente bloque de código a su clase BlogService para inyectar su repositorio:

constructor(
    @InjectRepository(Blog)
    private blogRepository: Repository<Blog>,
  ) {}

A continuación, agregue el siguiente bloque de código a su clase de servicio para implementar la lógica para recuperar todos los blogs en la base de datos:

async getAll(): Promise<Blog[]> {
    return await this.blogRepository.find();
  }

A continuación, deberá implementar la lógica para agregar un nuevo blog a su base de datos, pero primero deberá crear un tipo de entrada.

Crear un tipo de entrada

Para crear un tipo de entrada para sus mutaciones que toman objetos complejos, cree una carpeta dtos en la carpeta de su blog. Luego, en su carpeta dtos, cree un nuevo archivo, create-blog.input.ts.

En su archivo create-blog.input.ts, importe los decoradores Field y InputType. De esta forma:

import { Field, InputType } from '@nestjs/graphql';

El decorador InputType marca una clase como tipo de entrada GraphQL.

A continuación, cree y exporte una clase, BlogInput. Luego, anote la clase con el decorador InputType. De esta forma:

@InputType()
export class BlogInput {}

Finalmente, agregue todas las propiedades del blog requeridas y anótelas con el decorador Field.

De esta forma:

//create-blog.input.ts
import { Field, InputType } from '@nestjs/graphql';

@InputType()
export class BlogInput {
  @Field()
  id: number;

  @Field()
  title: string;

  @Field()
  body: string;

  @Field()
  author: string;

  @Field()
  date: string;
}

Su tipo de entrada terminado debería verse como el bloque de código de arriba.

A continuación, importe su tipo de entrada a su archivo de servicio y agregue el siguiente bloque de código a su clase de servicio para implementar la lógica para agregar un blog a la base de datos:

async createBlog(blogInput: BlogInput): Promise<Blog> {
    const newBlog = this.blogRepository.create(blogInput);

    return this.blogRepository.save(newBlog);
  }

El tipo de entrada se aplica de la misma manera que se aplica un objeto de transferencia de datos Nest.js normal, como mecanismo de validación para los datos entrantes.

Al igual que dtos, también puede realizar una validación adicional de los datos utilizando el paquete class-validator.

Creando un solucionador

Navegue hasta su archivo Solucionador generado previamente, blog.resolver.ts, e importe su entidad, tipo de entrada y servicio. De esta forma:

import { Blog } from './blog.entity';
import { BlogService } from './blog.service';
import { BlogInput } from './dto/create-blog.input';

A continuación, agregue el siguiente bloque de código a su clase Solucionador para inyectar su servicio en su solucionador:

constructor(private blogService: BlogService) {}

A continuación, importe los decoradores Args, Mutation, y Query de @nestjs/graphql.

De esta forma:

import { Args, Mutation, Query } from '@nestjs/graphql';

El decorador Args extrae el objeto de argumentos de la plataforma subyacente y completa el parámetro decorado con el valor de todos los argumentos o de un único argumento especificado.

El decorador Mutation especifica una ruta como mutación.

El decorador Query especifica una ruta como consulta.

A continuación, pase la siguiente función al decorador Resolver para especificar el tipo de objeto:

(of) => Blog

Luego, agregue el siguiente bloque de código a su clase Solucionador para resolver el método getAll.

 @Query((type) => [Blog])
  async getAllBlogs() {
    return this.blogService.getAll();
  }

Finalmente, agregue el siguiente bloque de código a su clase Solucionador para resolver el método createBlog.

 @Mutation((returns) => Blog)
  createBlog(@Args('blogInput') blogInput: BlogInput): Promise<Blog> {
    return this.blogService.createBlog(blogInput);
  }

Nota: asegúrese de especificar la función de tipo de retorno para cada decorador.

Su clase Solucionador terminada debería verse como el siguiente bloque de código:

@Resolver((of) => Blog)
export class BlogResolver {
  constructor(private blogService: BlogService) {}

  @Query((type) => [Blog])
  async getAllBlogs() {
    return this.blogService.getAll();
  }

  @Mutation((returns) => Blog)
  createBlog(@Args('blogInput') blogInput: BlogInput): Promise<Blog> {
    return this.blogService.createBlog(blogInput);
  }
}

Probando su API usando GraphQL Playground

Para probar su aplicación utilizando el área de juegos GraphQL, disponible de forma predeterminada, ejecute el siguiente comando y navegue hasta http://localhost:3000/graphql.

npm start

A continuación, debería ver un área de juegos donde puede probar sus consultas y mutaciones, como se muestra en la imagen a continuación.

GraphQL playground

Para recuperar todos los blogs de la base de datos, ejecute la siguiente consulta:

query getAll{
  getAllBlogs {
    id,
    title,
    body,
    author,
    date
  }
}

Un ejemplo se muestra en la imagen a continuación:

Para agregar un blog a su base de datos, ejecute la siguiente mutación:

mutation createBlog {
  createBlog (blogInput: {
    id: 1, 
    title: "GraphQL is great", 
    body: "GraphQL APIs are faster than REST APIs", 
    author: "David Ekete",
    date: "01-02-03"
  }) {
    id,
    title,
    body,
    author,
    date,
  }
}

Un ejemplo se muestra en la imagen a continuación:

GraphQL playground

Back4app y GraphQL

Debido a las muchas ventajas que GraphQL tiene sobre REST, como el control de recuperación de datos, la plataforma Back4app proporciona una API GraphQL integrada en las aplicaciones de análisis que se ejecutan en la plataforma.

Esta integración le permite verificar el estado del backend de su servidor, realizar operaciones de creación, lectura, actualización, eliminación (CRUD) en su base de datos y mucho más escribiendo consultas y mutaciones GraphQL simples.

Por ejemplo, para comprobar el estado de su servidor backend utilizando la API GraphQL, ejecute la siguiente consulta:

query Health {
  health
}

La consulta anterior devolverá:

{
  "data": {
    "health": true
  }
}

Para comenzar con la función GraphQL, navegue hasta su panel de Parse y seleccione y haga clic en su aplicación.

En la interfaz de usuario de su aplicación, seleccione y haga clic en Núcleo, luego seleccione y haga clic en Consola API y elija la consola GraphQL.

Si sigue las instrucciones anteriores, se abrirá una consola GraphQL donde podrá ejecutar sus consultas y mutaciones de manera similar al área de juegos GraphQL que utilizó en el desarrollo.

Puede obtener más información sobre la función GraphQL de Back4app en la documentación de Back4app.

Conclusión

Este artículo cubrió cómo se puede crear una API GraphQL en Nest.js y probar la API con el área de juegos GraphQL.

Las API GraphQL tienen varias ventajas sobre las API REST. Sin embargo, no son ideales para todas las aplicaciones.

Asegúrese de considerar los pros y los contras de GraphQL y REST antes de elegir cuál sería adecuado para su aplicación.

Independientemente de su elección, GraphQL o REST, aún deberá implementar su aplicación. Back4app es un BaaS de código bajo basado en tecnologías de código abierto que simplifica su proceso de creación de backend al eliminar tareas repetitivas utilizando plantillas reutilizables y administrando la mayor parte de su infraestructura de backend.

Algunas de las características de Back4app incluyen una base de datos en tiempo real, funciones en la nube, API y SDK nativos, un sistema completo de gestión de usuarios y mucho más.

Esta plataforma también cuenta con un nivel gratuito que le permite explorar la plataforma sin pagar una tarifa.

¡Suerte en sus creaciones!


Leave a reply

Your email address will not be published.