Graphql的使用

Graphql 是一种查询语言,用于 API 的设计和查询。它的主要特点是提供了一种灵活、高效、类型安全的数据查询方式,可以减少 API 的请求次数,提高数据传输的效率。

它与 Restful API 对比#

  1. 数据获取

RESTful API 是面向资源的,每个资源都有一个独立的 URL,通过 HTTP 方法(GET、POST、PUT、DELETE 等)对资源进行操作。GraphQL 是面向数据的,使用查询语言来描述需要获取的数据结构。

  1. 数据结构

RESTful API 返回的数据结构是固定的,通常是 JSON 或 XML 格式。GraphQL 允许客户端精确地指定需要返回的数据结构,避免了不必要的数据传输。

  1. 性能

RESTful API 的性能通常受到请求和响应的大小和数量的限制。GraphQL 通过在单个请求中获取多个数据点来提高性能,从而减少了请求次数和响应大小。

  1. 缓存

RESTful API 使用 HTTP 缓存来提高性能,因为每个资源都有一个独立的 URL。GraphQL 可以使用相同的查询来获取不同的数据,这意味着客户端可以缓存查询并重复使用它们。

  1. 安全性

RESTful API 使用 HTTP 方法来操作资源,因此需要对每个方法进行身份验证和授权。GraphQL 使用单个 HTTP 方法(POST)来处理所有请求,因此需要在查询语言中定义安全性。

js 搭建一个 Graphql 服务

import Koa from 'koa';
import mount from 'koa-mount';
import { createHandler } from 'graphql-http/lib/use/koa';
import { user_schema } from './lib/user_schema';

const app = new Koa();


// 定义数据源
const users = [
    { id: '1', name: 'Alice', age: 25, email: 'alice@example.com', posts: [{ id: '1', title: 'First Post', content: 'Hello world!' }] },
    { id: '2', name: 'Bob', age: 30, email: 'bob@example.com', posts: [{ id: '2', title: 'Second Post', content: 'What a wonderful day!' }] },
  ];
// 定义resolvers
const root = {
    hello: () => 'Hello, world!',
    user: ({ id }) => users.find(user => user.id === id),
  };

// 将GraphQL服务挂载到'/graphql'路径下
app.use(mount('/graphql', createHandler({
    schema: user_schema,
    rootValue: root,
    graphiql: true // 开启GraphQL的可视化界面
  })));

// 启动服务器
app.listen(3000, () => {
  console.log('Server started on http://localhost:3000');
});

user_schemal.js

import { buildSchema } from 'graphql';


const user_schema = buildSchema(`
  type Query {
    hello: String
    user(id: ID!): User
  }
  type User {
    id: ID
    name: String
    age: Int
    email: String
    posts: [Post]
  }
  type Post {
    id: ID
    title: String
    content: String
  }
`);
export default {user_schema}

通过 GraphQL 查询,这个需要支持 GraphQL 的客户端,发送查询语句

{
  hello,
  user(id:1){
      id,
      name,
      age,
      posts{
          id,
          title,
          content
      }
  }
}

返回的数据

{
    "data": {
        "hello": "Hello, world!",
        "user": {
            "id": "1",
            "name": "Alice",
            "age": 25,
            "posts": [
                {
                    "id": "1",
                    "title": "First Post",
                    "content": "Hello world!"
                }
            ]
        }
    }
}

创建 schema#

schema 文件定义了需要的数据结构,graphQL 有自己的类型系统
graphql.cn/learn/schema/#type-syst...

定义 resolvers#

对每个查询字段,需要生成对应的函数

本作品采用《CC 协议》,转载必须注明作者和本文链接
走出舒适区
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 7

我也有在用

2年前 评论

我已经把这个东西已经全部删除了,就在今天. 看起来很好用,实际逻辑稀碎

2年前 评论
Runtoweb3 (楼主) 2年前
sanders

graphql 已经不算新技术了,但这么长时间里,应用始终还是占少数,是有其原因的。

我提几点使用过程中的问题,看看大家有没有较好的解决方案:

1. 查询复杂度如何控制?

graphql 的查询复杂度是一个很大的问题,如果没有控制,很容易就会出现恶意查询,导致服务器崩溃。
举一个例子:

query {
  user(id: 1) {
      friends (first: 1000) {
          id
          friends (first: 1000) {
              id
              __typename
          }
      }
  }
}

这样的查询,会导致服务器查询 1000 * 1000 = 1000000 条数据,这是不可接受的。

2. 查询缓冲如何实现?

graphql 的查询是非常灵活的,如果没有缓冲,每次查询都会导致数据库查询,这是不可接受的。
举一个例子:

query {
    me {
        id
        name
        joinedTeams(first: 20) {
            id
            name
            owner {
                id
                name
            }
        }
    }
}

以上查询中当前用户和所加入的团队所有者中都有相同的用户 ID,如果没有缓冲,将导致多次查询。
更不用说我在使用中采用一些服务端指令,类似 @can 这类会在执行中进行数据库查询,而其中的结果难以在上下文中使用。
这种灵活的结构同时也让关系型数据库的 SQL 的组装变得非常困难,很难做到按需组装查询。

这么看,所有看起来是优势的特性都是有代价的。这让我想起我最近在使用 lighthouse 在介绍资源处置风险时提到的那句蜘蛛侠的台词 “With great power there must also come great responsibility”
这就变得矛盾,从服务端看:越是灵活的 API 维护成本就变的越高,我们越需要限制它的作用,直到达到某个平衡状态。

要说起我认为 graphql 真正的价值其实还是在沟通方面,无论前端还是后端工程师都基于其 schema 暴露出数据的结构来构建程序。 schema 即充当了程序沟通的标准,也成为了前后端工程师(只不过我同时为两者,比较精分)沟通的桥梁。

2年前 评论
小李世界 2年前
sanders

上面这帖子有部分是使用 github copilot 插件在 markdown 文档中写的,大家能看出来是哪部分吗?

2年前 评论
Runtoweb3 (楼主) 2年前