GraphQL y NestJS forman una excelente asociación, ya que le brindan una base sólida para sus API y un marco fácil de usar para crear aplicaciones web escalables. La combinación es perfecta para crear aplicaciones listas para producción y ambas son herramientas muy relevantes en el ecosistema tecnológico actual.
Obtenga más información sobre cómo puede crear una API con ambos productos.
¿Qué es GraphQL?
GraphQL es un lenguaje de consulta y manipulación de datos que puede usar para crear API de una manera más precisa y concisa. GraphQL proporciona una descripción completa y adecuada de los datos existentes en una API y le da poder al cliente para obtener los datos exactos que necesita.
GraphQL proporciona muchas funciones de las que carecen las API REST, que van desde consultas de datos precisas hasta mejores herramientas para desarrolladores, como el editor graphiql . También le permite consultar múltiples recursos a través de una sola solicitud.
¿Qué es NestJS?
NestJS es un marco progresivo de Node.js que puede usar para crear aplicaciones del lado del servidor escalables y eficientes. NestJS proporciona muchos complementos, junto con herramientas para un desarrollo rápido y sencillo, incluida la compatibilidad con GraphQL, GRPC, WebSockets, etc.
NestJS es bien conocido en el ecosistema por su estructura de proyecto optimizada que utiliza módulos, controladores, servicios y esquemas. Su CLI integrada le permite crear una arquitectura de API estructurada. Puede usar los principios de inyección de dependencia para controlar cómo las partes de una aplicación se comunican entre sí.
Implementando GraphQL con NestJS y MongoDB
Antes de crear una API con NestJS y GraphQL, deberá tener disponibles las dependencias adecuadas. Debe instalar Node.js y NestJS, que puede instalar ejecutando npm i -g @nestjs/cli .
El ejemplo que sigue es una aplicación simple que almacena información sobre libros. Ejecute el siguiente comando en su terminal para crear una nueva aplicación NestJS:
nest new <app-name>
Navegue al directorio de la aplicación generada ( <app-name> ) e instale sus dependencias con el siguiente comando:
npm install --save @nestjs/config @nestjs/graphql graphql-tools graphql \
@nestjs/apollo apollo-server-express @nestjs/mongoose @types/graphql
Existen dos enfoques principales para crear API de GraphQL, a saber:
- Enfoque de esquema primero: donde describe la API en archivos de definición de esquema o SDL, y NestJS genera definiciones de TypeScript basadas en ellos.
- Enfoque de código primero: donde define consultas, mutaciones y otras funcionalidades de GraphQL utilizando clases y decoradores de TypeScript, y NestJS genera archivos SDL basados en ellos.
El siguiente ejemplo describe cómo usar un enfoque de código primero.
Primero, debe inicializar GraphQL en su AppModule y conectarlo a una base de datos MongoDB :
// app.module.ts
import { Module } from '@nestjs/common';
import { GraphQLModule as NestGraphQLModule } from '@nestjs/graphql';
import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
import { join } from 'path';
import { MongooseModule } from '@nestjs/mongoose';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { ConfigModule, ConfigService } from '@nestjs/config';
import mongodbConfig from './config/mongodb.config';
({
imports: [
ConfigModule.forRoot({
load: [mongodbConfig],
isGlobal: true
}),
NestGraphQLModule.forRootAsync<ApolloDriverConfig>({
driver: ApolloDriver,
inject: [ConfigService],
useFactory: async (configService: ConfigService) => ({
autoSchemaFile: join(process.cwd(), 'src/schema.gql'),
installSubscriptionHandlers: true,
sortSchema: true,
playground: true,
debug: configService.get<boolean>("DEBUG"),
uploads: false,
}),
}),
MongooseModule.forRootAsync({
inject: [ConfigService],
useFactory: async (configService: ConfigService) => ({
uri: configService.get('MONGO_URI')
})
}),
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
Este módulo importa GraphQLModule de @nestjs/graphql y MongooseModule de @nestjs/mongoose que ayuda a conectarse a MongoDB. La propiedad autoSchemaFile especifica la ubicación del archivo de esquema generado y la propiedad sortSchema garantiza que ordena los campos alfabéticamente.
Así es como debería verse su archivo de configuración de MongoDB:
import { registerAs } from '@nestjs/config';
/**
* Mongo database connection config
*/
export default registerAs('mongodb', () => {
const {
MONGO_URI
} = process.env;
return {
uri: `${MONGO_URI}`,
};
});
Definición del esquema GraphQL
Una vez configuradas las conexiones de GraphQL y MongoDB, debe definir consultas y mutaciones de GraphQL para generar un archivo de esquema ( schema.gql ).
Escribir consultas
En el enfoque de código primero , crea un modelo utilizando el decorador ObjectType . Más adelante transformará este modelo en un tipo GraphQL.
Por ejemplo:
// book.model.ts
import { Field, ObjectType } from '@nestjs/graphql';
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { Document } from 'mongoose';
export type BookDocument = Book & Document;
()
()
export class Book {
()
title: string;
()
author: string;
()
publishedDate: boolean;
}
export const BookSchema = SchemaFactory.createForClass(Book);
GraphQL, de forma predeterminada, no puede usar los esquemas creados. Para hacerlos funcionales, necesita un servicio de resolución que contenga las funciones para ejecutar los tipos de GraphQL. Puede hacerlo con el decorador Resolver .
// books.resolver.ts
import { Resolver, Query, Mutation, Args, ID } from '@nestjs/graphql';
import { Book } from './book.model';
import { BookService } from './books.service';
(() => Book)
export class BookResolver {
constructor(private readonly bookService: BookService) { }
(() => [Book])
async books(): Promise<Book[]> {
return this.bookService.findAll();
}
(() => Book)
async book(('id', { type: () => ID }) id: string): Promise<Book> {
return this.bookService.findOne(id);
}
}
Puede implementar BookService , importado arriba, de la siguiente manera:
// books.service.ts
import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { Book, BookDocument } from './book.model';
()
export class BookService {
constructor(private bookModel: Model<BookDocument> (Book.name) ) { }
async findAll(): Promise<Book[]> {
return this.bookModel.find().exec();
}
async findOne(id: string): Promise<Book> {
return this.bookModel.findById(id).exec();
}
}
También debe agregar BookResolver a la lista de proveedores en books.module.ts
import { Module } from "@nestjs/common";
import { MongooseModule } from "@nestjs/mongoose";
import { BookService } from './books.service';
import { BookResolver } from './books.resolver';
import { Book, BookSchema } from './book.model';
({
providers: [
BookService,
BookResolver
],
imports: [MongooseModule.forFeature([
{
name: Book.name,
schema: BookSchema,
},
]),
],
})
export class BooksModule {}
Trabajar con mutaciones
Mientras usa una consulta para recuperar datos en GraphQL, las mutaciones crean o actualizan datos en la base de datos. Para crear mutaciones, debe aceptar datos de los usuarios. El decorador InputType , que convierte una clase en un tipo de entrada de GraphQL, resulta útil aquí.
// book.input.ts
import { InputType, Field } from '@nestjs/graphql';
()
export class BookInput {
()
title: string;
()
author: string;
()
publishedDate: boolean
}
import { Book } from './book.model';
import { BookService } from './books.service';
import { BookInput } from './book.input';
(() => Book)
export class BookResolver {
constructor(private readonly bookService: BookService) { }
(() => Book)
async createBook(('input') input: BookInput): Promise<Book> {
return this.bookService.create(input);
}
(() => Book)
async updateBook(
('id', { type: () => ID }) id: string,
('input') input: BookInput,
): Promise<Book> {
return this.bookService.update(id, input);
}
(() => Book)
async deleteBook(('id', { type: () => ID }) id: string): Promise<Book> {
return this.bookService.delete(id);
}
}
Y books.service.ts así:
import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { Book, BookDocument } from './book.model';
()
export class BookService {
constructor(private bookModel: Model<BookDocument> (Book.name) ) { }
async create(book: Book): Promise<Book> {
const newBook = new this.bookModel(book);
return newBook.save();
}
async update(id: string, book: Book): Promise<Book> {
return this.bookModel.findByIdAndUpdate(id, book, { new: true }).exec();
}
async delete(id: string): Promise<Book> {
return this.bookModel.findByIdAndDelete(id).exec();
}
}
El decorador @Mutation marca una función como un tipo de mutación y el decorador @Args toma cualquier entrada pasada a la función.
Finalmente, debe importar BooksModule a AppModule para que sea funcional. También debe pasar BooksModule a forRootAsync como se ve a continuación.
Y aquí hay un ejemplo de una mutación:
Cree API eficientes con NestJS y GraphQL
La creación de una API de GraphQL en NestJS con MongoDB usando Mongoose implica definir un esquema para la API de GraphQL, un esquema para el modelo de Mongoose, un servicio para interactuar con la base de datos y un solucionador para asignar las operaciones de GraphQL a los métodos de servicio.
NestJS tiene una funcionalidad integrada para crear API, incluidos decoradores para definir rutas, guardias para protegerlos y middlewares para manejar solicitudes y respuestas. También admite otras bases de datos como PostgreSQL, MySQL y SQLite, así como otras bibliotecas de GraphQL como Apollo y TypeGraphQL.