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:

1
src/
2
├── app.module.ts
3
├── main.ts
4
├── user/
5
│ ├── user.model.ts
6
│ ├── user.input.ts
7
│ ├── user.resolver.ts
8
│ ├── user.service.ts

Instalación y configuración

  1. Instalar los paquetes necesarios:
Terminal window
1
npm install @nestjs/graphql @nestjs/apollo graphql apollo-server-express @nestjs/typeorm typeorm mysql2
  1. 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:

1
import { Module } from "@nestjs/common";
2
import { GraphQLModule } from "@nestjs/graphql";
3
import { ApolloDriver, ApolloDriverConfig } from "@nestjs/apollo";
4
import { TypeOrmModule } from "@nestjs/typeorm";
5
import { join } from "path";
6
import { UserModule } from "./user/user.module";
7
import { 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áticamente
14
}),
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
})
28
export 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:

src/user/user.model.ts
1
import { ObjectType, Field, Int } from "@nestjs/graphql";
2
import { Entity, Column, PrimaryGeneratedColumn } from "typeorm";
3
4
@Entity()
5
@ObjectType()
6
export 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:

src/user/user.schema.graphql
1
type User {
2
id: Int!
3
name: String!
4
email: String!
5
}
6
7
type Query {
8
getUsers: [User]
9
}
10
11
type Mutation {
12
createUser(name: String!, email: String!): User
13
}

Luego, configurar NestJS para usar este archivo de esquema:

src/app.module.ts
1
import { Module } from "@nestjs/common";
2
import { GraphQLModule } from "@nestjs/graphql";
3
import { ApolloDriver, ApolloDriverConfig } from "@nestjs/apollo";
4
import { join } from "path";
5
import { 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
})
16
export 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:

1
import { Resolver, Query, Mutation, Args } from "@nestjs/graphql";
2
import { User } from "./user.model";
3
import { CreateUserInput } from "./user.input";
4
import { UserService } from "./user.service";
5
6
@Resolver(() => User)
7
export 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:

1
import { Resolver, Query, Args, Int } from "@nestjs/graphql";
2
import { User } from "./user.model";
3
import { UserService } from "./user.service";
4
5
@Resolver(() => User)
6
export 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.ts
26
import { Injectable } from "@nestjs/common";
27
import { InjectRepository } from "@nestjs/typeorm";
28
import { Repository } from "typeorm";
29
import { User } from "./user.model";
30
import { CreateUserInput } from "./user.input";
31
32
@Injectable()
33
export 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.usersRepository
58
.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:

Terminal window
1
npm install @apollo/client graphql

Realizar solicitudes GraphQL

Configurar Apollo Client en el proyecto frontend:

src/apollo-client.js
1
import { ApolloClient, InMemoryCache } from "@apollo/client";
2
3
const client = new ApolloClient({
4
uri: "http://localhost:3000/graphql",
5
cache: new InMemoryCache(),
6
});
7
8
export default client;

Manejo de consultas y mutaciones

Usar Apollo Client para realizar solicitudes de consulta y mutación:

src/components/Users.js
1
import React from "react";
2
import { useQuery, gql } from "@apollo/client";
3
4
const GET_USERS = gql`
5
query GetUsers {
6
getUsers {
7
id
8
name
9
email
10
}
11
}
12
`;
13
14
function 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
29
export default Users;

Manejo de solicitudes de mutación:

src/components/AddUser.js
1
import React, { useState } from "react";
2
import { useMutation, gql } from "@apollo/client";
3
4
const CREATE_USER = gql`
5
mutation CreateUser($name: String!, $email: String!) {
6
createUser(input: { name: $name, email: $email }) {
7
id
8
name
9
email
10
}
11
}
12
`;
13
14
function 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
<input
30
value={name}
31
onChange={(e) => setName(e.target.value)}
32
placeholder="Name"
33
/>
34
<input
35
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
48
export default AddUser;

Usar estos componentes en la aplicación frontend:

src/App.js
1
import React from "react";
2
import { ApolloProvider } from "@apollo/client";
3
import client from "./apollo-client";
4
import Users from "./components/Users";
5
import AddUser from "./components/AddUser";
6
7
function 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
19
export 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.