在Nest+GrqphQL+Prisma+React全栈开发这个系列中,我将会带领着大家从零到一,一步一步简单学会使用这些技术栈,并实际开发一个实战应用。
这篇是第一篇文章,GraphQL篇。
一、初识GraphQL
(一)、简介
官方定义:GraphQL 是一个用于 API 的查询语言,是一个使用基于类型系统来执行查询的服务端运行时(类型系统由你的数据定义)。
说简单点,就是另一种接口查询数据的技术,和我们熟知的REST是同类的技术。
(二)、与REST对比
既然已经有了REST,为什么还需要GraphQL?
REST,即Representational State Transfer,翻译过来就是表现层状态转化。
RESTful架构定义:
- 每一个URI代表一种资源
- 客户端和服务器之间,传递这种资源的某种表现层,也就是
conent-type的不同类型 - 客户端通过四个HTTP动词,对服务器端资源进行操作,实现"表现层状态转化",也就是
GET,POST这些
REST 的 API 配合JSON格式的数据交换,使得前后端分离、数据交互变得非常容易,因而也大受欢迎,发展迅猛。但是随之缺点也暴露了出来:
- 接口定义依赖文档,文档定义编写耗时,缺失则导致前端人员不知道该如何使用
- 设计不合理导致大量冗余重复相似度较高的接口出现
- 升级迭代困难,从前端到后台都需要全部修改一遍
- 一次性难以获得全部需要的数据,需要发起多个请求
基于以上这些问题,GraphQL则让前端自己描述需要的数据结构,后端返回对应的数据即可,是不是真香?
(三)、GraphQL的优缺点
1.优点
- 数据整体性强,REST所获取的数据都是离散的,如果要获取相关联的数据,则需要再发送一个新的接口,Graph只要在获取的时候直接定义就可以了
- 缓存强大,官方已经帮你实现好了,graphql.org/learn/cachi…
- 升级简单,不用修改不同的版本号接口
- 前后端沟通成本低
- 没有过度获取或者不足情况
- subscription 接受服务端推送,不必依靠轮询或者websocket
- 自省,可以查询 GraphQL 服务器支持的类型
2.缺点
- 复杂度,在获取接口的时候,如果不合理地嵌套太多字段,则容易引发服务端效率极差的查询
- 无法做查询频率限制
二、入门
(一)、基本概念
1. Operation
GraphQL的操作包括query、mutation、subscription。
一般的操作格式是这样的
operation name(args) {
//数据类型
}
例如:
query HeroNameAndFriends {
hero(id: "1000"){
name
friends {
name
}
}
}
2. Schema
GraphQL有自己的一套类型语言,定义了字段的类型、数据的结构,描述了接口数据请求的规则。
# 对象类型
type Character {
name: String!
appearsIn: [Episode!]!
}
# 参数
type Starship {
length(unit: LengthUnit = METER): Float
}
# 查询类型
type Query {
hero(episode: Episode): Character
droid(id: ID!): Droid
}
# 变更类型
type Mutation {
hero(episode: Episode): Character
droid(id: ID!): Droid
}
# 标量类型
Int、Float、String、Boolean、ID
# 枚举类型
enum Episode {
NEWHOPE
EMPIRE
JEDI
}
# 接口类型
interface Character {
id: ID!
name: String!
friends: [Character]
appearsIn: [Episode]!
}
# 联合类型
union SearchResult = Human | Droid | Starship
# 输入类型
input ReviewInput {
stars: Int!
commentary: String
}
3. Execution
当我们进行查询的时候,后端还需要定义解析函数Resolver来提供数据。
例如一个query的解析函数:
Query: {
human(obj, args, context, info) {
return context.db.loadHumanByID(args.id).then(
userData => new Human(userData)
)
}
}
解析器函数接收 4 个参数:
obj上一级对象,如果字段属于根节点查询类型通常不会被使用。args传入的参数。context会被提供给所有解析器,并且持有重要的上下文信息info一个保存与当前查询相关的字段特定信息以及 schema 详细信息的值
4. Introspection
内省就是通过GraphQL可以查出支持哪些查询:
# 查询可用的类型
{
__schema {
types {
name
}
}
}
# 检验特定类型
{
__type(name: "...") {
name
}
}
# 查询有哪些对象
{
__type(name: "...") {
name
fields {
name
type {
name
kind
}
}
}
}
5. HTTP请求
例如查询GET请求:
{
me {
name
}
}
# http请求为
http://myapi/graphql?query={me{name}}
POST请求为:
{
"query": "...",
"operationName": "...",
"variables": { "myVariable": "someValue", ... }
}
返回的结果都是JSON的:
{
"data": { ... },
"errors": [ ... ]
}
6. 分页
在GraphQL中一般建议使用游标进行分页查询。
{
hero {
name
friends(first:2, after:$friendId) {
totalCount
edges {
node {
name
}
cursor
}
pageInfo {
endCursor
hasNextPage
}
}
}
}
三、在Nest中使用GraphQL
首先需要安装相应的包
$ npm i --save @nestjs/graphql graphql-tools graphql apollo-server-express@2.x.x
Nest 提供了两种构建 GraphQL 应用程序的方式,模式优先和代码优先。
代码优先,就是使用装饰器和 TypeScript 类来生成相应的 GraphQL schema。
模式优先,就是自己手写 GraphQL SDL(模式定义语言)。
安装完成之后配置app.module.ts
import { Module } from '@nestjs/common';
import { GraphQLModule } from '@nestjs/graphql';
@Module({
imports: [
GraphQLModule.forRoot({}),
],
})
export class ApplicationModule {}
四、在React中使用GraphQL
造react中使用GraphQL一般使用applo/client
npm install @apollo/client graphql
初始化:
const client = new ApolloClient({
uri: 'https://48p1r2roz4.sse.codesandbox.io',
cache: new InMemoryCache()
});
连接Client到React
render(
<ApolloProvider client={client}>
<App />
</ApolloProvider>,
document.getElementById('root'),
获取数据:
const EXCHANGE_RATES = gql`
query GetExchangeRates {
...
}
`;
function ExchangeRates() {
const { loading, error, data } = useQuery(EXCHANGE_RATES);
if (loading) return <p>Loading...</p>;
if (error) return <p>Error :(</p>;
return ...
));
}