RestQL:现代化的 API 开发方式
参考:https://tech.meituan.com/koa-restql.html
在现代的业务系统中,后端开发工作基本上可以被拆分为三项:
- 接口鉴权。例如判断是不是当前系统的用户,以及该用户是否有权限访问接口。
- 与其他系统的交互。例如调用第三方的服务,或内部搭建的其他服务。
- 数据操作。基本上所有需要持久化存储的系统都会在这项工作上耗费大量时间。
本文将介绍如何利用 RestQL 来非常有效地减少「数据操作」相关的工作量。
现状与挑战
我们先来做个假设。
- 假设系统中有 60 张表,每张表对应的接口都要有四种 CRUD 的 API。那么就需要后端工程师写
60 * 4 = 240
个API。 - 假设上述 60 张表中,40 张表存的是资源类的数据,其余 20 张表为关系类的数据,也就是说每张表和 20 张表都要进行关联,每个关联也需要四种 CRUD 操作,那么又要增加
40 * 20 * 4 = 3200
个API。
所以在上述假设场景中,后端工程师要编写 3200 + 240 = 3440 个 API。而且这还不是全部,假如后端代码需要 100% 的测试覆盖,那么工程师们就要写至少 3440 个测试!
60 张表 = 3440 个 API + 3440 个单元测试
众所周知,数据操作 API 的实现过程基本上是重复的,有的同学甚至认为这是低端的,体现不出工程师价值的工作,纯粹的「体力活」。但是却没有一个能真正解放生产力的方案。
解决思路
尽管我们把数据库抽象成了「关系型」数据库,把操作数据的命令抽象成了 SQL ,同时我们也有了 MySQL 客户端,甚至是 sequelize 这种非常方便的库,也有「RESTful」API 命名规则,但是接口的实现从来都是需要工程师们自己用手敲出来的。
如果说我看得比别人远,那是因为我站在巨人的肩膀上。
所以我们在现有的技术基础上再抽象,把已有的东西重新组合起来,拼装成一个新的工具,帮助工程师从「体力活」中解脱出来,解放生产力。
什么样的工具
最开始的时候,我们最先需要明确的问题就是:「我们需要什么样的工具?」或者说「这种工具要帮我们解决什么问题?」。
实际上我们从刚才的假设中,已经可以得出结论:我们希望有一个工具可以让工程师免于编写数据操作 API,把数据库操作直接映射到 HTTP RESTful API 上。
调用方式
如何请求
为了解释「如何请求」,我们先从一些公认的规则出发,举一个例子,然后再从例子中抽象出一些规则。
注意:为了更便于理解,我们把所有的命名从客户端一直穿透到数据库,所以请不要纠结于我们在定义一个 API 时名词单复数的问题。
基本用例
几乎所有的系统都会有一个用户表(user)。根据 RESTful 规则的约定,我们应该把访问 user 表的 API 路径定义为 /user
,并把 CRUD 的访问方法映射到 HTTP 协议中的四种方法:GET
、POST
、PUT
、DELETE
。
比如:
GET /user
:获取用户列表,应该返回一个数组。GET /user/:id
:获取指定的用户,应该返回一个对象。POST /user
:创建一个用户,应该返回被存储的对象,状态码应该为 201(Created)。PUT /user
:修改一个用户的信息,应该返回修改后的对象。DELETE /user/:id
:删除一个用户,状态码应该为 204(No Content)。
如果 user 表有一个关系表 feed,那么我们的路径就会再复杂一点:
GET /user/:id/feed
或GET /feed?user_id=:id
:获取某个用户的帖子,应该返回一个数组。GET /user/:id/feed/:feed_id
或GET /feed/:id
:获取指定的帖子,应该返回一个对象。
上述的例子中还会衍生出其他的数据操作,不仅仅只有 GET
,这里不一一列举了。
抽象出规则
上一节中,列举了要提供一个表的数据访问 API,大概要实现哪些路由。从这些枚举中,可以找出其中的规律,总结出一套规则。最终我们在「把能实现的路由,全部实现」的原则基础上,开发了 RestQL 的 koa 版本。
支持的 HTTP 方法:
HTTP verb | CRUD |
---|---|
GET | Read |
POST | Create |
PUT | Create/Update |
DELETE | Delete |
支持的带有 body 的 HTTP 方法:
HTTP verb | List | Single |
---|---|---|
POST | Array/Object | × |
PUT | Array/Object | Object |
说明:
List
路径为返回值为数组的路径,包括:/resource
/resource/:id/association
, association 为1:n
关系/resource/:id/association
, association 为n:m
关系
Single
路径为返回值为单个对象的路径,包括:/resource/:id
/resource/:id/association
, association 为1:1
关系/resource/:id/association/:id
, association 为1:n
关系/resource/:id/association/:id
, association 为n:m
关系
如何使用
我们已经开源了 koa-restql,koa 应用开发者可以通过 npm 安装它:
npm install koa-restql
然后在 koa 应用的代码中引用 RestQL:
const koa = require('koa')
const RestQL = require('koa-restql')
let app = koa()
// Build APIs from `sequelize.models`
let restql = new RestQL(sequelize.models)
app.use(restql.routes())
常见问题
修改参数
用户可以通过querystring
来修改参数。强烈建议使用qs
对 querystring 进行解析,例如:
qs.stringify({a: 1, b:2}) // => a=1&b=2
RestQL 中的querystring
仅有 3 条规则:
所有不以
_
开头的键,都会被放进sequelize#query()
的where
参数中。例如:// query
{
name: "Li Xin"
}
// option for sequelize
{
where: {
name: "Li Xin"
}
}
所有以
_
开头的键,都会被放进sequelize#query()
的参数中,和where
保持平级。例如:// query
{
_limit: 10
}
// option for sequelize
{
limit: 10
}
当需要使用关系时,可以用关系名称的字符串代替关系对象传入。例如需要使用
include
时:// query
{
_include: ['friends']
}
// option for sequelize
{
include: [
models.user.association.friends
]
}
访问控制
通常来说,我们有两种方法实现访问控制:
通过中间件
在 koa 应用挂载 RestQL 的 router 之前,可以实现一个鉴权中间件,控制用户的访问权限:
app.use(authorizeMiddleware)
app.use(restql.routes())
通过 restql 参数
在使用sequelize
定义关联时,我们可以设定restql
参数,实现访问控制。例如:
禁止通过
restql
访问关联:models.user.hasOne(
models.privacy,
{
restql: {
ignore: true
}
}
)
禁止通过
restql
使用指定的 HTTP 方法访问关联models.user.hasOne(
models.privacy,
{
restql: {
ignore: ['get']
}
}
)
RestQL:现代化的 API 开发方式的更多相关文章
- 【转】RestQL:现代化的 API 开发方式
原文:http://tech.meituan.com/koa-restql.html RestQL:现代化的 API 开发方式 李鑫 ·2016-08-12 11:26 koa-restql 已经在 ...
- graphql 新API 开发方式
我们知道 GraphQL 使用 Schema 来描述数据,并通过制定和实现 GraphQL 规范 定义了支持 Schema 查询的 DSQL (Domain Specific Query Langua ...
- 一篇文章帮你梳理清楚API设计时需要考虑的几个关键点
本文作者是Enchant的架构师,他最近研究了Netflix.SoundCloud.谷歌.亚马逊.Spotify等公司的微服务实践,并根据自己的理解总结出了一套适用于现代Web和云技术的微服务实战经验 ...
- 【Azure API 管理】在APIM中使用客户端证书验证API的请求,但是一直提示错误"No client certificate received."
API 管理 (APIM) 是一种为现有后端服务创建一致且现代化的 API 网关的方法. 问题描述 在设置了APIM客户端证书,用户保护后端API,让请求更安全. 但是,最近发现使用客户端证书的API ...
- 将声音文件加入VC
概述 VC++是微软公司开发的一个IDE(集成开发环境),换句话说,就是使用c++的一个开发平台.有些软件就是这个编出来的...另外还有VB,VF.只是使用不同语言...但是,VC++是Windows ...
- 【转】33 个 2017 年必须了解的 iOS 开源库
1.IGListKit,作者是Instagram Engineering Instagram 程序员做的,IGListKit 是数据驱动的 UICollectionView 框架,为了构建快速和可扩展 ...
- 33 个 2017 年必须了解的 iOS 开源库
本文翻译自Medium,原作者为Pawe? Bia?ecki 照片版权:(Unsplash/Markus Pe) 你好,iOS 开发者们!我的名字叫 Pawe?,我是一个独立 iOS 开发者,并且是 ...
- Oracle WebLogic Server 12c 新特性
美国时间2011年 12月9日,Oracle公司正式发布WebLogic 12c版本,c是cloud的缩写.截止当前(2013年8月)最新版本为Oracle WebLogic Server 12c ( ...
- 33 个 2017 年必须了解的 iOS/swift 开源库第三方库
本文翻译自Medium,原作者为 Paweł Białecki<img src="https://pic3.zhimg.com/v2-c786777447261347b0d97 ...
随机推荐
- 给trac的ticket添加提交时字段验证
我们在项目管理中使用了trac系统,并且对于ticket添加了以下自定义字段并且对它们的格式都有一定要求: svn版本号:格式为 r1234.多个版本号之间使用半角逗号隔开.如:r1234,r5678 ...
- 包不包含__declspec(dllimport)的判定
按照MSDN说明,当链接dll的导出函数时,只需要包含头文件和lib,__declspec(dllimport)修饰符不是必须的,但加上该修饰能使导出函数的调用效率更高.那么,究竟原因是什么? 不使用 ...
- WEB安全 魔术引号及注入类型
一.魔术引号 1. magic_quotes_gpc 变量 什么是魔术引号 Warning本特性已自 PHP 5.3.0 起废弃并将自 PHP 5.4.0 起移除.当打开时,所有的 '(单引号),&q ...
- My97datepicker使用方法
My97DatePicker是一款非常灵活好用的日期控件.使用非常简单. 1.下载My97DatePicker组件包 2.在页面中引入该组件js文件: <script type=&quo ...
- python3之安装mysql问题
python3是不能通过pip install mysql或pipinstall mysqldb这样的形式来安装mysql. 只能 pip install PyMySQL 至于如何在文件中引用? 答曰 ...
- linux下搭建LAMP
PHP命令找不到: export PATH=$PATH:/usr/local/php/bin https://www.centos.bz/forum/thread-69-1-1.html 步骤: w ...
- PAT——1032. 挖掘机技术哪家强
为了用事实说明挖掘机技术到底哪家强,PAT组织了一场挖掘机技能大赛.现请你根据比赛结果统计出技术最强的那个学校. 输入格式: 输入在第1行给出不超过105的正整数N,即参赛人数.随后N行,每行给出一位 ...
- Android的JNI调用(一)
Android提供NDK开发包来提供Android平台的C++开发,用来扩展Android SDK的功能.主要包括Android NDK构建系统和JNI实现与原生代码通信两部分. 一.Android ...
- 如何用GDI+画个验证码
如何使用GDI+来制作一个随机的验证码 绘制验证码之前先要引用 using System.Drawing; using System.Drawing.Drawing2D; 首先,先写一个方法来取得验证 ...
- Bootstrap03
一.表单(以下示例 * 代表class) 注意:a.使用表单的关键字form b.所有的提示词使用label包裹 c.所写内容按div分类,使得层次分明 1.基本表单+表单组合+内联表单 *=form ...