在NestJS中使用GraphQL

GraphQL 是一种用于 API 查询语言和服务器端运行时的规范,它能够满足客户端对数据的准确需求。本文将介绍 GraphQL 的基本概念,并讲解如何在 NestJS 中集成和使用 GraphQL,包括 Schema 的定义、复杂查询和变更的实现。

GraphQL 的基本概念

GraphQL 是由 Facebook 开发的一种用于 API 的查询语言,它提供了一个灵活和高效的替代方案来替代 REST API。GraphQL 允许客户端请求它们需要的确切数据,不多也不少,并且从多个资源中获取数据只需要一个请求。

主要概念包括:

  • Schema:定义 API 中可以查询的所有数据类型和它们之间的关系。
  • Query:读取数据的请求。
  • Mutation:修改数据的请求。
  • Resolver:处理查询和变更请求的函数。

NestJS 中的 GraphQL 模块

NestJS 提供了强大的 GraphQL 模块,支持使用代码优先(code-first)和模式优先(schema-first)两种方法来定义 GraphQL Schema。

在 NestJS 中使用 GraphQL

项目目录结构

在使用 GraphQL 时,我们需要创建一些特定的文件和目录来组织代码:

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

安装和配置

  1. 安装必要的包:
Terminal window
1
npm install @nestjs/graphql @nestjs/apollo graphql apollo-server-express @nestjs/typeorm typeorm mysql2
  1. 配置 GraphQL 模块和数据库连接:

src/app.module.ts 中配置 GraphQL 模块和 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"), // 自动生成 schema.gql 文件
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)

代码优先是通过装饰器定义 Schema,在编译时自动生成 GraphQL Schema 文件。这种方法使得开发者可以通过代码直接定义和管理 Schema。

示例:

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 文件,然后通过解析 Schema 文件生成类型和解析器。这种方法使得开发者可以直接编辑和管理 GraphQL Schema 文件。

示例:

首先编写 Schema 文件:

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
}

然后配置 NestJS 使用这个 Schema 文件:

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 {}

定义 Schema

在代码优先模式下,我们已经通过装饰器定义了 Schema。

创建和使用 Resolver

src/user/user.resolver.ts 中创建 Resolver 来处理查询和变更:

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
}

实现复杂查询

src/user/user.resolver.ts 中实现复杂查询:

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
}

与前端结合

配置前端环境

在前端项目中,通常使用 Apollo Client 来与 GraphQL API 进行交互。首先,我们需要安装 Apollo Client 相关的依赖:

Terminal window
1
npm install @apollo/client graphql

发起 GraphQL 请求

在前端项目中配置 Apollo Client:

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;

处理查询和变更

使用 Apollo Client 发起查询和变更请求:

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;

处理变更请求:

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;

在前端应用中使用这些组件:

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;

总结

本文介绍了 GraphQL 的基本概念,以及如何在 NestJS 项目中集成和使用 GraphQL。通过配置 GraphQL 模块,定义 Schema,创建和实现查询与变更,以及实现复杂查询,开发者可以充分利用 GraphQL 的强大功能来构建高效灵活的 API。最后,我们还展示了如何在前端项目中使用 Apollo Client 与 GraphQL API 进行交互,发起查询和变更请求,并处理返回的数据。通过这些示例,我们可以在 NestJS 中创建自己的 GraphQL API,并与前端项目无缝集成,满足不同的业务需求。

美团外卖红包 饿了么红包 支付宝红包