热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

由GraphQL来思考如何做一个好的APIDesign

目前我已经写了一年多graphql,也时常思考和RestAPI的不同,以及对APIDesign的启发。他山之石可以攻玉。qraphql一些天然的设计或者

目前我已经写了一年多 graphql,也时常思考和 Rest API 的不同,以及对 API Design 的启发。

他山之石可以攻玉。qraphql 一些天然的设计或者思想对写 Rest API 有很大的借鉴或参考意义。

这里总结下一些受启发的 API 设计规范。

如果你对 graphql 不熟悉,可以先参考 graphql 中文文档

本文链接 shanyue.tech/post/api-de…

对所有的资源返回 id

在 graphql 中,scalar 类型 ID 用来表示资源的全局唯一性。在 apollo-client 中也建议客户端每次请求都把 id 带上。

在响应中带上 id 至少有两个好处

  1. 客户端对资源的缓存
  2. 在数据上游至客户端的整个链路中有利于数据的溯源

按需加载资源的字段

query TODO {todo (id: 10) {id namestatus}
}

如客户端只需要显示某个 TODO 的状态以及名称,则只需要返回 name 以及 status 字段,大大减少了网络的流量。

另外, graphql server 需要在数据库层面也对字段做按需加载。否则,graphql server 与 database 之间也会造成无用的数据 IO 与流量浪费。

获取 graphql query 所请求的字段,需要手动解析 GraphQLFieldResolveFn 函数的第四个字段 info,并在每一个 field 上自定义一个 directive 标注 Graphql Filed 与 Database Field 的关系

在 Rest API 中可以使用额外字段做按需加载。 如使用 fields 标记返回需要的字段,若无此字段,默认返回资源的全部字段,在中间件中对 fields 做结构化处理

// 请求 Todo:10,并且只需要 id,name,status 三个字符安
'/api/todos/10?fields=id,name,status'// 请求 Todo:10 全部资源
'/api/todos/10'

关联资源使用嵌套对象表示

这个请求表示一个用户列表,每个用户需要展示最后一个 Todo 的名称。Todo 需要使用嵌套对象来表示。

query USERS {users {idnamelastTodo {idname}}
}

在 Rest API 设计中经常见到所有数据进行了展开,不仅无法定位资源,也不好扩展数据。嵌套数据可以很灵活的扩展数据,另外也可以对嵌套数据进行按需加载

const res0 = {users: [{id: 1,name: "山月",todoName: "学习"}]
}// 修改后
const todoFields = {}
const res = {users: [{id: 1,name: "山月",todo: {id: 1,name: "学习",...fields}}]
}// 可以这样设计 API
const api = '/api/users?fields=id,name,todo.id,todo.name'

使用 ISOString 表示时间戳

在 graphql 中,虽没有一个 scalar 类型来表示时间戳,不过可以自定义 scalar DateTime 来表示时间。关于时间的格式

参考 StackOverflow 上的问题 the-right-json-date-format

const date = new Date()// 从 toJSON 的输出就知道前后端交互需要使用什么格式了
date.toJSON()
// 2019-03-14T07:41:08.500Z
date.toISOString()
// 2019-03-14T07:41:08.500Z

这样返回的格式不仅符合规范,而且可读性也比较好。

我见过API中返回的时间戳表示为 unix timestamp,js timestamp, iso8601 三种格式,较为混乱。统一的数据格式有利于前后端的联调,不过这也得益于 graphql 的强类型 schema。

结构化的错误信息

在 graphql 中会返回 { data, errors } 的数据结构,可以在最后结构化错误信息为

{"code": "InvalidToken","message": "Token 失效","httpStatus": 401
}

message 为可读性的错误信息,可以由前端直接显示,code 为调试用,httpStatus 由下一步的中间件捕捉,设置状态码。

在结构化错误信息后,可以顺带把错误信息发送到报警系统 (如 Sentry)。不过需要分清 WARN 与 ERROR,如 401,403 应当做 WARN 处理。

符合标准的 http status

恩,好吧。graphql 这条有缺陷。graphql 的 QueryMutation 都是使用 POST 请求。对不同的执行成功的 Mutation 返回不同的 200,201,202 还是比较麻烦。

不过对于错误返回不同的状态码, 打开 devtool 一眼可以看到红色的 4XX 信息,也对快速定位错误请求有帮助,稍微减少了些烦躁心。

介绍几种常见的4xx状态码

  • 401 Unauthorized: 用户未登录请求需要登录才能请求的资源
  • 403 Forbidden: 用户A登录了,但他却想请求 B 的资源
  • 400 Bad Request: 恩,我把所有找不到状态码的错误都放到了 400

关于400参考 400 BAD request HTTP error code meaning? 这里有一篇文章,关于4xx状态码的选择,取一张图出来

请求及响应数据校验

由于 graphql 的强类型 schema,也省了数据输入输出的校验。

对于 Rest API,可以使用 JSON Schema 来校验数据格式。node 也可以使用 joi 做数据校验。

这里放一份 JSON Schema 的文档:json-schema.org/

注释文档化

得益于 graphql 的 introspection 与强类型的 schema。graphql 可以根据源码以及注释自动生成文档,直接使用 graphiql 或者 graphql playground 上查看。

如果你使用 node.js 来写服务器应用,可以使用 apiDoc

另外,注意不要把文档暴露到生产环境,graphql 需要在生产环境中关掉 introspection。

转:https://juejin.im/post/5c8afebfe51d4512445f227a



推荐阅读
  • 深入解析轻量级数据库 SQL Server Express LocalDB
    本文详细介绍了 SQL Server Express LocalDB,这是一种轻量级的本地 T-SQL 数据库解决方案,特别适合开发环境使用。文章还探讨了 LocalDB 与其他轻量级数据库的对比,并提供了安装和连接 LocalDB 的步骤。 ... [详细]
  • 本文探讨了Web API 2中特性的路由机制,特别是如何利用它来构建RESTful风格的URI。文章不仅介绍了基本的特性路由使用方法,还详细说明了如何通过特性路由进行API版本控制、HTTP方法的指定、路由前缀的应用以及路由约束的设置。 ... [详细]
  • 本文探讨了在使用Apache Flink向Kafka发送数据过程中遇到的事务频繁失败问题,并提供了详细的解决方案,包括必要的配置调整和最佳实践。 ... [详细]
  • 本文介绍了一种算法,用于在一个给定的二叉树中找到一个节点,该节点的子树包含最大数量的值小于该节点的节点。如果存在多个符合条件的节点,可以选择任意一个。 ... [详细]
  • 本文介绍了如何在Spring框架中配置和使用定时任务,包括初始化配置和动态启动定时器的方法。通过示例代码展示了如何利用Spring的TaskScheduler接口来创建和管理定时任务。 ... [详细]
  • 本文详细解析了Java中流的概念,特别是OutputStream和InputStream的区别,并通过实际案例介绍了如何实现Java对象的序列化。文章不仅解释了流的基本概念,还探讨了序列化的重要性和具体实现步骤。 ... [详细]
  • 本文详细介绍了如何在Vue项目中集成和配置XGPlayer视频插件,包括安装步骤、基本配置以及常见问题的解决方法。 ... [详细]
  • 本文介绍了一个基本的同步Socket程序,演示了如何实现客户端与服务器之间的简单消息传递。此外,文章还概述了Socket的基本工作流程,并计划在未来探讨同步与异步Socket的区别。 ... [详细]
  • 深入解析Nacos服务自动注册机制
    本文将探讨Nacos服务自动注册的具体实现方法,特别是如何通过Spring事件机制完成服务注册。通过对Nacos源码的详细分析,帮助读者理解其背后的原理。 ... [详细]
  • 本文探讨了在Qt框架下实现TCP多线程服务器端的方法,解决了一个常见的问题:服务器端仅能与最后一个连接的客户端通信。通过继承QThread类并利用socketDescriptor标识符,实现了多个客户端与服务器端的同时通信。 ... [详细]
  • 本文探讨了在Node.js环境中如何有效地捕获标准输出(stdout)的内容,并将其存储到变量中。通过具体的示例和解决方案,帮助开发者解决常见的输出捕获问题。 ... [详细]
  • HTTP中的Chunked编码与Content-Length的区别及应用场景
    本文探讨了在HTTP协议中,当使用Transfer-Encoding为chunked时为何无需设置Content-Length,以及这种编码方式的具体实现和优势。 ... [详细]
  • 本文详细介绍了如何将After Effects中的动画相机数据导入到Vizrt系统中,提供了一种有效的解决方案,适用于需要在广播级图形制作中使用AE动画的专业人士。 ... [详细]
  • 本文基于《Core Java Volume 2》的内容,深入探讨了网络编程中通过POST方法提交表单数据的技术细节,包括GET与POST方法的区别、POST提交的具体步骤及常见问题处理。 ... [详细]
  • 本文将作为我硕士论文的一部分,但鉴于其内容的独特性和趣味性,决定单独发布。文中将定义一些皮亚诺公理,并介绍如何使用这些公理进行等式替换,以证明定理。 ... [详细]
author-avatar
向日葵渴望
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有