Uso de GraphQL en NestJS
- 1232Palabras
- 6Minutos
- 12 Jul, 2024
GraphQL es un lenguaje de consulta para APIs y una especificación de tiempo de ejecución en el lado del servidor que permite a los clientes solicitar datos precisos. Este artículo presentará los conceptos básicos de GraphQL y explicará cómo integrar y usar GraphQL en NestJS, incluyendo la definición de esquemas, consultas complejas y mutaciones.
Conceptos básicos de GraphQL
GraphQL es un lenguaje de consulta para APIs desarrollado por Facebook que ofrece una alternativa flexible y eficiente a las APIs REST. GraphQL permite a los clientes solicitar exactamente los datos que necesitan, ni más ni menos, y obtener datos de múltiples recursos en una sola solicitud.
Los conceptos principales incluyen:
- Schema: Define todos los tipos de datos que se pueden consultar en la API y las relaciones entre ellos.
- Query: Solicitud para leer datos.
- Mutation: Solicitud para modificar datos.
- Resolver: Funciones que manejan las solicitudes de consultas y mutaciones.
Módulo GraphQL en NestJS
NestJS proporciona un potente módulo GraphQL que soporta la definición de esquemas GraphQL mediante los métodos de “code-first” (código primero) y “schema-first” (esquema primero).
Uso de GraphQL en NestJS
Estructura del proyecto
Al usar GraphQL, necesitamos crear algunos archivos y directorios específicos para organizar el código:
1src/2├── app.module.ts3├── main.ts4├── user/5│ ├── user.model.ts6│ ├── user.input.ts7│ ├── user.resolver.ts8│ ├── user.service.ts
Instalación y configuración
- Instalar los paquetes necesarios:
1npm install @nestjs/graphql @nestjs/apollo graphql apollo-server-express @nestjs/typeorm typeorm mysql2
- Configurar el módulo GraphQL y la conexión a la base de datos:
En src/app.module.ts
, configurar el módulo GraphQL y el módulo TypeORM:
1import { Module } from "@nestjs/common";2import { GraphQLModule } from "@nestjs/graphql";3import { ApolloDriver, ApolloDriverConfig } from "@nestjs/apollo";4import { TypeOrmModule } from "@nestjs/typeorm";5import { join } from "path";6import { UserModule } from "./user/user.module";7import { User } from "./user/user.model";8
9@Module({10 imports: [11 GraphQLModule.forRoot<ApolloDriverConfig>({12 driver: ApolloDriver,13 autoSchemaFile: join(process.cwd(), "src/schema.gql"), // Archivo schema.gql generado automáticamente14 }),15 TypeOrmModule.forRoot({16 type: "mysql",17 host: "localhost",18 port: 3306,19 username: "root",20 password: "password",21 database: "test",22 entities: [User],23 synchronize: true,24 }),25 UserModule,26 ],27})28export class AppModule {}
Code-First vs. Schema-First
Code-First
Code-first define el esquema mediante decoradores y genera automáticamente el archivo del esquema GraphQL durante la compilación. Este método permite a los desarrolladores definir y gestionar el esquema directamente mediante el código.
Ejemplo:
1import { ObjectType, Field, Int } from "@nestjs/graphql";2import { Entity, Column, PrimaryGeneratedColumn } from "typeorm";3
4@Entity()5@ObjectType()6export class User {7 @PrimaryGeneratedColumn()8 @Field(() => Int)9 id: number;10
11 @Column()12 @Field()13 name: string;14
15 @Column()16 @Field()17 email: string;18}
Schema-First
Schema-first consiste en escribir manualmente el archivo del esquema y luego generar los tipos y resolvers a partir de él. Este método permite a los desarrolladores editar y gestionar el archivo del esquema GraphQL directamente.
Ejemplo:
Primero, escribir el archivo del esquema:
1type User {2 id: Int!3 name: String!4 email: String!5}6
7type Query {8 getUsers: [User]9}10
11type Mutation {12 createUser(name: String!, email: String!): User13}
Luego, configurar NestJS para usar este archivo de esquema:
1import { Module } from "@nestjs/common";2import { GraphQLModule } from "@nestjs/graphql";3import { ApolloDriver, ApolloDriverConfig } from "@nestjs/apollo";4import { join } from "path";5import { UserModule } from "./user/user.module";6
7@Module({8 imports: [9 GraphQLModule.forRoot<ApolloDriverConfig>({10 driver: ApolloDriver,11 typePaths: ["./**/*.graphql"],12 }),13 UserModule,14 ],15})16export class AppModule {}
Definir el Schema
En el modo code-first, ya hemos definido el esquema mediante decoradores.
Crear y usar Resolver
En src/user/user.resolver.ts
, crear el resolver para manejar las consultas y mutaciones:
1import { Resolver, Query, Mutation, Args } from "@nestjs/graphql";2import { User } from "./user.model";3import { CreateUserInput } from "./user.input";4import { UserService } from "./user.service";5
6@Resolver(() => User)7export class UserResolver {8 constructor(private readonly userService: UserService) {}9
10 @Query(() => [User])11 async getUsers(): Promise<User[]> {12 return this.userService.getUsers();13 }14
15 @Mutation(() => User)16 async createUser(@Args("input") input: CreateUserInput): Promise<User> {17 return this.userService.createUser(input);18 }19}
Implementar consultas complejas
En src/user/user.resolver.ts
, implementar consultas complejas:
1import { Resolver, Query, Args, Int } from "@nestjs/graphql";2import { User } from "./user.model";3import { UserService } from "./user.service";4
5@Resolver(() => User)6export class UserResolver {7 constructor(private readonly userService: UserService) {}8
9 @Query(() => [User])10 async getUsers(11 @Args("page", { type: () => Int, nullable: true }) page: number = 1,12 @Args("limit", { type: () => Int, nullable: true }) limit: number = 10,13 ): Promise<User[]> {14 return this.userService.getUsersWithPagination(page, limit);15 }16
17 @Query(() => [User])18 async searchUsers(19 @Args("keyword", { type: () => String }) keyword: string,20 ): Promise<User[]> {21 return this.userService.searchUsers(keyword);22 }23}24
25// src/user/user.service.ts26import { Injectable } from "@nestjs/common";27import { InjectRepository } from "@nestjs/typeorm";28import { Repository } from "typeorm";29import { User } from "./user.model";30import { CreateUserInput } from "./user.input";31
32@Injectable()33export class UserService {34 constructor(35 @InjectRepository(User)36 private usersRepository: Repository<User>,37 ) {}38
39 async getUsers(): Promise<User[]> {40 return this.usersRepository.find();41 }42
43 async createUser(input: CreateUserInput): Promise<User> {44 const user = this.usersRepository.create(input);45 return this.usersRepository.save(user);46 }47
48 async getUsersWithPagination(page: number, limit: number): Promise<User[]> {49 const [result] = await this.usersRepository.findAndCount({50 skip: (page - 1) * limit,51 take: limit,52 });53 return result;54 }55
56 async searchUsers(keyword: string): Promise<User[]> {57 return this.usersRepository58 .createQueryBuilder("user")59 .where("user.name LIKE :keyword", { keyword: `%${keyword}%` })60 .orWhere("user.email LIKE :keyword", { keyword: `%${keyword}%` })61 .getMany();62 }63}
Integración con el frontend
Configuración del entorno frontend
En un proyecto frontend, normalmente se usa Apollo Client para interactuar con la API GraphQL. Primero, debemos instalar las dependencias relacionadas con Apollo Client:
1npm install @apollo/client graphql
Realizar solicitudes GraphQL
Configurar Apollo Client en el proyecto frontend:
1import { ApolloClient, InMemoryCache } from "@apollo/client";2
3const client = new ApolloClient({4 uri: "http://localhost:3000/graphql",5 cache: new InMemoryCache(),6});7
8export default client;
Manejo de consultas y mutaciones
Usar Apollo Client para realizar solicitudes de consulta y mutación:
1import React from "react";2import { useQuery, gql } from "@apollo/client";3
4const GET_USERS = gql`5 query GetUsers {6 getUsers {7 id8 name9 email10 }11 }12`;13
14function Users() {15 const { loading, error, data } = useQuery(GET_USERS);16
17 if (loading) return <p>Loading...</p>;18 if (error) return <p>Error :(</p>;19
20 return data.getUsers.map(({ id, name, email }) => (21 <div key={id}>22 <p>23 {name}: {email}24 </p>25 </div>26 ));27}28
29export default Users;
Manejo de solicitudes de mutación:
1import React, { useState } from "react";2import { useMutation, gql } from "@apollo/client";3
4const CREATE_USER = gql`5 mutation CreateUser($name: String!, $email: String!) {6 createUser(input: { name: $name, email: $email }) {7 id8 name9 email10 }11 }12`;13
14function AddUser() {15 const [name, setName] = useState("");16 const [email, setEmail] = useState("");17 const [createUser, { data, loading, error }] = useMutation(CREATE_USER);18
19 const handleSubmit = (e) => {20 e.preventDefault();21 createUser({ variables: { name, email } });22 setName("");23 setEmail("");24 };25
26 return (27 <div>28 <form onSubmit={handleSubmit}>29 <input30 value={name}31 onChange={(e) => setName(e.target.value)}32 placeholder="Name"33 />34 <input35 value={email}36 onChange={(e) => setEmail(e.target.value)}37 placeholder="Email"38 />39 <button type="submit">Add User</button>40 </form>41 {loading && <p>Loading...</p>}42 {error && <p>Error :(</p>}43 {data && <p>User {data.createUser.name} created!</p>}44 </div>45 );46}47
48export default AddUser;
Usar estos componentes en la aplicación frontend:
1import React from "react";2import { ApolloProvider } from "@apollo/client";3import client from "./apollo-client";4import Users from "./components/Users";5import AddUser from "./components/AddUser";6
7function App() {8 return (9 <ApolloProvider client={client}>10 <div>11 <h2>My first Apollo app 🚀</h2>12 <AddUser />13 <Users />14 </div>15 </ApolloProvider>16 );17}18
19export default App;
Conclusión
Este artículo presentó los conceptos básicos de GraphQL y cómo integrarlo y usarlo en un proyecto NestJS. Al configurar el módulo GraphQL, definir esquemas, crear y manejar consultas y mutaciones, así como implementar consultas complejas, los desarrolladores pueden aprovechar al máximo las potentes funcionalidades de GraphQL para construir APIs eficientes y flexibles. Finalmente, mostramos cómo usar Apollo Client en un proyecto frontend para interactuar con la API GraphQL, realizar consultas y mutaciones, y manejar los datos devueltos. Con estos ejemplos, podemos crear nuestra propia API GraphQL en NestJS e integrarla sin problemas con un proyecto frontend para satisfacer diferentes necesidades comerciales.