GraphQL 思考如何做一个好的 API Design

2019-03-1520:17:07WEB前端开发Comments3,262 views字数 2383阅读模式

对所有的资源返回 id

在 graphql 中,scalar 类型 ID 用来表示资源的全局唯一性。在 apollo-client 中也建议客户端每次请求都把 id 带上。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/9987.html

在响应中带上 id 至少有两个好处文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/9987.html

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

按需加载资源的字段

query TODO {
  todo (id: 10) {
    id 
    name
    status
  }
}
复制代码

如客户端只需要显示某个 TODO 的状态以及名称,则只需要返回 name 以及 status 字段,大大减少了网络的流量。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/9987.html

另外, graphql server 需要在数据库层面也对字段做按需加载。否则,graphql server 与 database 之间也会造成无用的数据 IO 与流量浪费。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/9987.html

获取 graphql query 所请求的字段,需要手动解析 GraphQLFieldResolveFn 函数的第四个字段 info,并在每一个 field 上自定义一个 directive 标注 Graphql Filed 与 Database Field 的关系文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/9987.html

在 Rest API 中可以使用额外字段做按需加载。 如使用 fields 标记返回需要的字段,若无此字段,默认返回资源的全部字段,在中间件中对 fields 做结构化处理文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/9987.html

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

// 请求 Todo:10 全部资源
'/api/todos/10'
复制代码

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

这个请求表示一个用户列表,每个用户需要展示最后一个 Todo 的名称。Todo 需要使用嵌套对象来表示。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/9987.html

query USERS {
  users {
    id
    name
    lastTodo {
      id
      name
    }
  }
}
复制代码

在 Rest API 设计中经常见到所有数据进行了展开,不仅无法定位资源,也不好扩展数据。嵌套数据可以很灵活的扩展数据,另外也可以对嵌套数据进行按需加载文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/9987.html

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 来表示时间。关于时间的格式文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/9987.html

参考 StackOverflow 上的问题 the-right-json-date-format文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/9987.html

const date = new Date()

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

这样返回的格式不仅符合规范,而且可读性也比较好。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/9987.html

我见过API中返回的时间戳表示为 unix timestamp,js timestamp, iso8601 三种格式,较为混乱。统一的数据格式有利于前后端的联调,不过这也得益于 graphql 的强类型 schema。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/9987.html

结构化的错误信息

在 graphql 中会返回 { data, errors } 的数据结构,可以在最后结构化错误信息为文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/9987.html

{
  "code": "InvalidToken",
  "message": "Token 失效",
  "httpStatus": 401
}
复制代码

message 为可读性的错误信息,可以由前端直接显示,code 为调试用,httpStatus 由下一步的中间件捕捉,设置状态码。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/9987.html

在结构化错误信息后,可以顺带把错误信息发送到报警系统 (如 Sentry)。不过需要分清 WARN 与 ERROR,如 401,403 应当做 WARN 处理。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/9987.html

符合标准的 http status

恩,好吧。graphql 这条有缺陷。graphql 的 QueryMutation 都是使用 POST 请求。对不同的执行成功的 Mutation 返回不同的 200,201,202 还是比较麻烦。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/9987.html

不过对于错误返回不同的状态码, 打开 devtool 一眼可以看到红色的 4XX 信息,也对快速定位错误请求有帮助,稍微减少了些烦躁心。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/9987.html

介绍几种常见的4xx状态码文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/9987.html

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

关于400参考 400 BAD request HTTP error code meaning? 这里有一篇文章,关于4xx状态码的选择,取一张图出来文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/9987.html

GraphQL 思考如何做一个好的 API Design

请求及响应数据校验

由于 graphql 的强类型 schema,也省了数据输入输出的校验。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/9987.html

对于 Rest API,可以使用 JSON Schema 来校验数据格式。node 也可以使用 joi 做数据校验。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/9987.html

这里放一份 JSON Schema 的文档:json-schema.org/文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/9987.html

注释文档化

得益于 graphql 的 introspection 与强类型的 schema。graphql 可以根据源码以及注释自动生成文档,直接使用 graphiql 或者 graphql playground 上查看。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/9987.html

如果你使用 node.js 来写服务器应用,可以使用 apiDoc文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/9987.html

另外,注意不要把文档暴露到生产环境,graphql 需要在生产环境中关掉 introspection。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/9987.html

作者:shanyue
链接:https://juejin.im/post/5c8afebfe51d4512445f227a
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/9987.html

文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/9987.html
  • 本站内容整理自互联网,仅提供信息存储空间服务,以方便学习之用。如对文章、图片、字体等版权有疑问,请在下方留言,管理员看到后,将第一时间进行处理。
  • 转载请务必保留本文链接:https://www.cainiaoxueyuan.com/gcs/9987.html

Comment

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定