深入探索 React、NestJS、GraphQL 和 Apollo
1. GraphQL 查询
GraphQL 的查询语言是其一大优势,它允许客户端定义要检索的数据以及数据的所需形状。与 REST 相比,GraphQL 非常强大且有标准化规范。RESTful API 没有统一标准,每个 API 实现包含/排除、分页、搜索和过滤等功能的方式可能不同,而 GraphQL 对许多概念都有明确的方法。
1.1 基本查询示例
以星球大战 GraphQL API 为例,若客户端想检索所有星球大战电影的标题,可使用如下查询:
{
allFilms {
films {
title
}
}
}
服务器返回的响应示例如下:
{
"data": {
"allFilms": {
"films": [
{
"title": "A New Hope"
},
{
"title": "The Empire Strikes Back"
},
{
"title": "Return of the Jedi"
},
...
]
}
}
}
可以看到,服务器仅提供了客户端请求的
title
属性,这一特性可显著提高应用的网络友好性。GraphQL 查询不是 JSON,但响应默认使用 JSON,便于解析。
1.2 带别名的查询示例
若客户端想检索星球大战电影中每个角色的 ID、姓名和物种名称,可使用如下查询:
{
characters: allPeople {
people {
id
name
species {
name
}
}
}
}
这里的
characters:
是别名,用于进一步自定义响应的形状。服务器可能的响应如下:
{
"data": {
"characters": {
"people": [
{
"id": "cGVvcGxlOjE=",
"name": "Luke Skywalker",
"species": {
"name": "Human"
}
},
{
"id": "cGVvcGxlOjI=",
"name": "C-3PO",
"species": {
"name": "Droid"
}
},
...
]
}
}
}
1.3 带参数的查询示例
若客户端想检索已知标识符的 Luke Skywalker 的姓名和出生年份,可使用如下查询:
{
person(id: "cGVvcGxlOjE=") {
name
birthYear
}
}
查询参数以键值对形式传递,服务器响应如下:
{
"data": {
"person": {
"name": "Luke Skywalker",
"birthYear": "19BBY"
}
}
}
GraphQL 还支持客户端使用参数请求特定或替代的数据格式、内容翻译等,可减轻客户端应用的负担。
1.4 GraphQL 请求特点
GraphQL 请求总是使用 GET 或 POST 方法发送到同一个端点 URL,这与 RESTful API 每个资源都有自己的基础 URL 形成鲜明对比。一些人认为这是 GraphQL 的主要弱点,更倾向于 REST 按资源特定 URL 的方式。
2. 服务器端的 Schema、验证和解析器
在服务器端,GraphQL 运行时使用 GraphQL 模式来确定请求的内容以及如何检索数据。模式描述了不同的对象、它们的类型、支持的查询和突变以及所需/可选的参数。
当请求到来时,会根据模式进行验证(例如,强制某些字段必须存在)。如果验证通过,查询将被分析,GraphQL 解析器负责获取请求的数据。响应也可以(可选)根据模式进行验证。模式还可用于生成对应的类、类型或接口,并且有成熟的工具生态系统来辅助这个过程。
3. Apollo
Apollo GraphQL 是一个非常流行的开源平台,用于开发 GraphQL API 和 API 客户端。其主要元素包括:
- Apollo GraphQL 服务器
- Apollo GraphQL 客户端
- Apollo GraphQL CLI
Apollo 提供了与许多流行的 Web 框架(如 Angular、Vue、React 等)以及移动平台(如 Android 和 iOS)的客户端集成,便于在不同类型的项目中使用 GraphQL。而且,Apollo 遵循 GraphQL 规范,没有供应商锁定问题,其主要构建块都是开源的,开发者社区可以贡献和改进。
4. NestJS GraphQL 插件
NestJS 支持插件,GraphQL 插件是官方支持的插件之一。该插件提供了一个名为
GraphQLModule
的 NestJS 模块,它是 Apollo 服务器的包装器,主要目的是便于在 NestJS 应用中集成 GraphQL。
NestJS 的 GraphQL 插件支持两种不同的方法:
-
模式优先方法
:先定义 GraphQL 模式,该模式成为应用的唯一事实来源。
-
代码优先方法
:以应用代码为事实来源,需要对代码进行装饰,以便 NestJS 插件能够动态生成 GraphQL 模式。
两种方法各有优缺点。
5. 相关技术学习资源
| 技术 | 资源链接 |
|---|---|
| React |
官方网站:https://reactjs.org
官方文档:https://reactjs.org/docs 官方博客:https://reactjs.org/blog 等 |
| NestJS |
官方网站:https://nestjs.com
源码:https://github.com/nestjs/nest 等 |
| GraphQL |
官方文档:https://graphql.org/learn
初学者指南:https://medium.freecodecamp.org/a-beginners-guide-to-graphql-60e43b0a41f5 等 |
| Apollo |
文档:https://www.apollographql.com/docs
源码:https://github.com/apollographql 等 |
| TypeScript |
对象解构:http://exploringjs.com/es6/ch_destructuring.html#_object-destructuring
数组解构:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Array_destructuring 等 |
6. 构建 LyricsFinder 应用
接下来将利用 React、NestJS、GraphQL 和 Apollo 来重新构建 LyricsFinder 应用。具体步骤如下:
graph LR
A[构建应用] --> B[React 前端开发]
A --> C[后端 GraphQL API 开发]
B --> B1[安装 React Bootstrap]
B --> B2[安装 React Router]
B --> B3[创建主页骨架]
B --> B4[创建搜索组件]
B --> B5[集成后端 API]
B --> B6[创建和使用歌曲列表组件]
B --> B7[加载歌词并跳转歌词页面]
B --> B8[实现歌词页面]
C --> C1[添加必要依赖]
C --> C2[实现第一个解析器的 GraphQL API]
C --> C3[导入和配置 GraphQL NestJS 模块]
C --> C4[使用 GraphQL 操场测试 API]
C --> C5[添加对艺术家和歌词的支持]
6.1 前端开发步骤
- 安装 React Bootstrap :为应用添加样式。
- 安装 React Router :实现路由功能。
- 创建主页骨架 :搭建基本的页面结构。
- 创建搜索组件 :用于用户搜索歌曲。
- 集成后端 API :使前端能够与后端进行数据交互。
- 创建和使用歌曲列表组件 :展示搜索结果。
- 加载歌词并跳转歌词页面 :实现歌词的加载和页面跳转。
- 实现歌词页面 :展示具体的歌词内容。
6.2 后端开发步骤
- 添加必要依赖 :安装构建 GraphQL API 所需的库。
- 实现第一个解析器的 GraphQL API :开始构建 API 的基本功能。
- 导入和配置 GraphQL NestJS 模块 :将 GraphQL 集成到 NestJS 应用中。
- 使用 GraphQL 操场测试 API :确保 API 正常工作。
- 添加对艺术家和歌词的支持 :完善 API 的功能。
通过这些步骤,我们可以创建一个功能强大的现代 Web 应用。
7. 重新构建 LyricsFinder 应用概述
之前我们构建了 LyricsFinder 的第一个版本,使用的是 Vue.js,并且直接利用了 MusixMatch API。而这次,我们将使用 React、Bootstrap 和 React Bootstrap 来重写 LyricsFinder,同时创建自己的后端 API。
LyricsFinder V2 基本功能保持不变,但用户界面和 API 层会有显著变化。我们将创建一个前端 React 单页应用(SPA),并借助 NestJS 框架和 Apollo 实现后端 GraphQL API。
8. 详细开发流程
8.1 前端开发
8.1.1 安装 React Bootstrap
React Bootstrap 可以为应用提供丰富的样式组件,让界面更加美观。安装命令如下(假设使用 npm):
npm install react-bootstrap bootstrap
安装完成后,需要在项目中引入 Bootstrap 的 CSS 文件,一般在入口文件(如
index.js
或
index.tsx
)中添加:
import 'bootstrap/dist/css/bootstrap.min.css';
8.1.2 安装 React Router
React Router 用于实现单页应用的路由功能,方便用户在不同页面之间导航。安装命令:
npm install react-router-dom
8.1.3 创建主页骨架
创建一个基本的主页结构,例如在
src
目录下创建
Home.jsx
或
Home.tsx
文件:
import React from 'react';
const Home = () => {
return (
<div className="container">
<h1>LyricsFinder</h1>
</div>
);
};
export default Home;
8.1.4 创建搜索组件
在
src
目录下创建
Search.jsx
或
Search.tsx
文件,实现搜索功能:
import React, { useState } from 'react';
const Search = () => {
const [searchTerm, setSearchTerm] = useState('');
const handleSearch = () => {
// 这里可以添加调用后端 API 的逻辑
console.log('Searching for:', searchTerm);
};
return (
<div>
<input
type="text"
placeholder="Search for songs"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
<button onClick={handleSearch}>Search</button>
</div>
);
};
export default Search;
8.1.5 集成后端 API
使用 Apollo Client 来集成后端的 GraphQL API。首先安装 Apollo Client 和相关依赖:
npm install @apollo/client graphql
然后在项目中配置 Apollo Client,例如在
src
目录下创建
apolloClient.js
或
apolloClient.ts
文件:
import { ApolloClient, InMemoryCache, HttpLink } from '@apollo/client';
const httpLink = new HttpLink({
uri: 'http://localhost:3000/graphql', // 后端 API 的地址
});
const client = new ApolloClient({
link: httpLink,
cache: new InMemoryCache(),
});
export default client;
在需要使用 API 的组件中使用 Apollo Client,例如在
Search
组件中:
import React, { useState } from 'react';
import { useQuery, gql } from '@apollo/client';
const SEARCH_SONGS = gql`
query SearchSongs($searchTerm: String!) {
searchSongs(searchTerm: $searchTerm) {
id
title
artist
}
}
`;
const Search = () => {
const [searchTerm, setSearchTerm] = useState('');
const { loading, error, data } = useQuery(SEARCH_SONGS, {
variables: { searchTerm },
skip: !searchTerm,
});
const handleSearch = () => {
// 触发查询
};
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<div>
<input
type="text"
placeholder="Search for songs"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
<button onClick={handleSearch}>Search</button>
{data && data.searchSongs.map((song) => (
<div key={song.id}>
<p>{song.title} - {song.artist}</p>
</div>
))}
</div>
);
};
export default Search;
8.1.6 创建和使用歌曲列表组件
创建
SongList.jsx
或
SongList.tsx
文件,用于展示搜索结果:
import React from 'react';
const SongList = ({ songs }) => {
return (
<div>
{songs.map((song) => (
<div key={song.id}>
<p>{song.title} - {song.artist}</p>
</div>
))}
</div>
);
};
export default SongList;
在
Search
组件中使用
SongList
组件:
import React, { useState } from 'react';
import { useQuery, gql } from '@apollo/client';
import SongList from './SongList';
const SEARCH_SONGS = gql`
query SearchSongs($searchTerm: String!) {
searchSongs(searchTerm: $searchTerm) {
id
title
artist
}
}
`;
const Search = () => {
const [searchTerm, setSearchTerm] = useState('');
const { loading, error, data } = useQuery(SEARCH_SONGS, {
variables: { searchTerm },
skip: !searchTerm,
});
const handleSearch = () => {
// 触发查询
};
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<div>
<input
type="text"
placeholder="Search for songs"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
<button onClick={handleSearch}>Search</button>
{data && <SongList songs={data.searchSongs} />}
</div>
);
};
export default Search;
8.1.7 加载歌词并跳转歌词页面
当用户点击歌曲时,跳转到歌词页面并加载歌词。可以使用 React Router 的
Link
组件实现跳转,同时在歌词页面使用 Apollo Client 获取歌词数据。
8.1.8 实现歌词页面
创建
LyricsPage.jsx
或
LyricsPage.tsx
文件,用于展示歌词:
import React from 'react';
import { useQuery, gql } from '@apollo/client';
const GET_LYRICS = gql`
query GetLyrics($songId: ID!) {
song(id: $songId) {
lyrics
}
}
`;
const LyricsPage = ({ songId }) => {
const { loading, error, data } = useQuery(GET_LYRICS, {
variables: { songId },
});
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<div>
<h2>Lyrics</h2>
<p>{data.song.lyrics}</p>
</div>
);
};
export default LyricsPage;
8.2 后端开发
8.2.1 添加必要依赖
在项目根目录下使用 npm 安装必要的依赖:
npm install @nestjs/graphql @nestjs/apollo graphql apollo-server-express
8.2.2 实现第一个解析器的 GraphQL API
创建一个简单的解析器,例如在
src
目录下创建
songs.resolver.ts
文件:
import { Resolver, Query } from '@nestjs/graphql';
@Resolver()
export class SongsResolver {
@Query(() => String)
hello() {
return 'Hello, GraphQL!';
}
}
8.2.3 导入和配置 GraphQL NestJS 模块
在
src/app.module.ts
文件中导入和配置
GraphQLModule
:
import { Module } from '@nestjs/common';
import { GraphQLModule } from '@nestjs/graphql';
import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
import { SongsResolver } from './songs.resolver';
@Module({
imports: [
GraphQLModule.forRoot<ApolloDriverConfig>({
driver: ApolloDriver,
autoSchemaFile: 'schema.gql',
}),
],
providers: [SongsResolver],
})
export class AppModule {}
8.2.4 使用 GraphQL 操场测试 API
启动 NestJS 应用,访问
http://localhost:3000/graphql
打开 GraphQL 操场,在操场中输入查询语句进行测试:
{
hello
}
8.2.5 添加对艺术家和歌词的支持
扩展解析器和模式,添加对艺术家和歌词的查询支持。例如在
songs.resolver.ts
文件中添加新的查询:
import { Resolver, Query, Args } from '@nestjs/graphql';
@Resolver()
export class SongsResolver {
@Query(() => String)
hello() {
return 'Hello, GraphQL!';
}
@Query(() => String)
songLyrics(@Args('songId') songId: string) {
// 这里可以添加获取歌词的逻辑
return 'Sample lyrics';
}
}
通过以上详细的前端和后端开发步骤,我们可以利用 React、NestJS、GraphQL 和 Apollo 构建出一个功能强大的 LyricsFinder 应用。这个应用不仅具有良好的用户界面,还能高效地处理数据查询和交互。
超级会员免费看
11

被折叠的 条评论
为什么被折叠?



