在NestJS中使用GraphQL
- 1554字
- 8分鐘
- 2024-07-12
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 時,我們需要創建一些特定的文件和目錄來組織代碼:
1src/2├── app.module.ts3├── main.ts4├── user/5│ ├── user.model.ts6│ ├── user.input.ts7│ ├── user.resolver.ts8│ ├── user.service.ts
安裝和配置
- 安裝必要的包:
1npm install @nestjs/graphql @nestjs/apollo graphql apollo-server-express @nestjs/typeorm typeorm mysql2
- 配置 GraphQL 模組和資料庫連接:
在 src/app.module.ts
中配置 GraphQL 模組和 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"), // 自動生成 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})28export class AppModule {}
代碼優先與模式優先
代碼優先(Code-First)
代碼優先是通過裝飾器定義 Schema,在編譯時自動生成 GraphQL Schema 文件。這種方法使得開發者可以通過代碼直接定義和管理 Schema。
示例:
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 文件,然後通過解析 Schema 文件生成類型和解析器。這種方法使得開發者可以直接編輯和管理 GraphQL Schema 文件。
示例:
首先編寫 Schema 文件:
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}
然後配置 NestJS 使用這個 Schema 文件:
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 {}
定義 Schema
在代碼優先模式下,我們已經通過裝飾器定義了 Schema。
創建和使用 Resolver
在 src/user/user.resolver.ts
中創建 Resolver 來處理查詢和變更:
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}
實現複雜查詢
在 src/user/user.resolver.ts
中實現複雜查詢:
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}
與前端結合
配置前端環境
在前端專案中,通常使用 Apollo Client 來與 GraphQL API 進行互動。首先,我們需要安裝 Apollo Client 相關的依賴:
1npm install @apollo/client graphql
發起 GraphQL 請求
在前端專案中配置 Apollo Client:
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;
處理查詢和變更
使用 Apollo Client 發起查詢和變更請求:
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;
處理變更請求:
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;
在前端應用中使用這些組件:
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;
總結
本文介紹了 GraphQL 的基本概念,以及如何在 NestJS 專案中集成和使用 GraphQL。通過配置 GraphQL 模組,定義 Schema,創建和實現查詢與變更,以及實現複雜查詢,開發者可以充分利用 GraphQL 的強大功能來構建高效靈活的 API。最後,我們還展示了如何在前端專案中使用 Apollo Client 與 GraphQL API 進行互動,發起查詢和變更請求,並處理返回的資料。通過這些示例,我們可以在 NestJS 中創建自己的 GraphQL API,並與前端專案無縫集成,滿足不同的業務需求。