思路还是 GraphQL 那一套,那我更宁愿去试试 GraphQL/Falcor。而且它的简单基于一个假设:JSON resource 结构跟数据库的表结构一致。现实中稍微复杂点的模型都不是这样的。所以 -- 不怎么看好。只说说我还算了解的 JSON API 和 GraphQL。
JSON API 适合粗粒度场景,是 REST 思路的延伸,各方面都有所考虑,对处理 relationship 有自己的一套解法。如果自己定义 API 规范有些想不清楚的地方,这个规范值得参考,但不见得值得使用。不过如果非要在同类规范(HAL, Siren 等)中选一个,我更愿意选择它。最后,如果使用 Ember + Ember Data 的话,这个规范还是推荐的。
GraphQL 没实际写过,感觉适合细粒度场景,适合前端频繁变化的应用,配合 Relay 和 React 可以有效的整合各个组件中的请求。我个人更喜欢这种方式的数据层,屏蔽了发起哪些查询和何时发起查询等细节。
最后,写 API 本来就是个体力活,这部分本该自动化一点。
你好,感谢你的关注。
查询:
GraphQL-半自动化,只有过滤字段实现了自动化,且字段需要手动 resolve。
APIJSON-完全自动化,任意结构任意内容,只要数据库里有。
兼容性:
GraphQL-GraphQL 特定格式,只能用 GraphQL Server,Client,Web 全套。
APIJSON-标准 JSON 格式,能用 JSON 的地方就能用 APIJSON。
为了公平起见,以下 GraphQL 的所有示例都出自官方文档及代码。
1.查询条件
GraphQL:
table(key0:value0,key1:value1,...) {
}
{
user(id: 4) {
id
name
}
}
APIJSON:
Table: {
key0:value0,
key1:value1,
...
}
{
"User":{
"id":38710
}
}
2.返回字段
GraphQL:
只返回已声明字段
{
key0
key1
...
}
{
user(id: 4) {
id
name
}
}
APIJSON:
默认返回全部,可加上以下条件限制
"@column":"key0,key1,..."
{
"User":{
"id":38710,
"@column":"id,name"
}
}
3.查询数组:
GraphQL:
需要后端在 schema 中手动 resolve
query noFragments {
user(id: 4) {
friends(first: 10) {
id
name
}
}
}
APIJSON:
"[]":{} 自动解析
{
"[]":{
"count":10,
"User":{
}
}
}
4.关联查询:
需要后端在 schema 中手动 resolve 关联
GraphQL:
query inlineFragmentTyping {
profiles(handles: ["zuck", "cocacola"]) {
handle
... on User {
friends {
count
}
}
... on Page {
likers {
count
}
}
}
}
APIJSON:
"key@":"key0/key1/.../targetKey" 自动解析
{
"[]": {
"User": {
"name$": ["Strong", "Mike"]
},
"Moment": {
"userId@": "[]/User/id"
}
}
}
5.后端定义结构
GraphQL:
读、写操作都需要,代码中,且字段要手动 resolve。
Node.js
var schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: 'RootQueryType',
fields: {
hello: {
type: GraphQLString,
resolve() {
return 'world';
}
}
}
})
});
Java
GraphQLObjectType queryType = newObject()
.name("helloWorldQuery")
.field(newFieldDefinition()
.type(GraphQLString)
.name("hello")
.staticValue("world"))
.build();
GraphQLSchema schema = GraphQLSchema.newSchema()
.query(queryType)
.build();
APIJSON:
只有写操作才需要,自动解析自动校验,而且是在数据库 Request 表中定义,完全可视化。
{
"Moment":{
"DISALLOW":"id",
"NECESSARY":"userId,pictureList"
}
}
6.前端查询灵活性
GraphQL:
1.所有结构都要后端先定义,前端/客户端才能按照已定义结构查询。
2.多层嵌套需要后端在结构外声明内部结构,不够直观,Request 和 Response 不能完全对应 (Request 少了内层结构)。
后端定义:
const UserType = new GraphQLType({
name:'User",
fields:{
id:{
type:GraphQLInt
},
name:{
type:GraphQLString
}
}
})
const CommentType = new GraphQLType({
name:'Comment",
fields:{
id:{
type:GraphQLInt
},
toId:{
type:GraphQLInt
},
userId:{
type:GraphQLInt
},
content:{
type:GraphQLString
}
}
})
const MomentType = new GraphQLType({
name:'Moment",
fields:{
id:{
type:GraphQLInt
},
userId:{
type:GraphQLInt
},
title:{
type:GraphQLString
},
content:{
type:GraphQLString
},
user:{
type:new GraphQLObject(UserType)
resolve:(moment) => getUserById(userId)
},
comments:{
type:new GraphQLList(CommentType),
resolve:(moment) => moment.commentIds.map(id => getCommentById(id))
}
}
})
function getUserById(id) {
//查询User...
return new UserType(id) //这里只是模拟,实际代码一般会比较多
}
function getCommentById(id) {
//查询Comment...
return new CommentType(id) //这里只是模拟,实际代码一般会比较多
}
前端请求:
query momentFragmentTyping {
moments(first: 10, text: "a") {
id
userId
title
content
user {
id
name
}
comments(first: 2) {
id
toId
userId
content
}
}
})
注:实在找不到官方的,群里问出了这个。
APIJSON:
1) 不需要后端定义,只要 类声明 + 权限注解 + 权限注册 3 行代码配置 Model 允许的操作及对应的角色。
3) 可任意组合、任意嵌套,Request 和 Response 完全对应。
后端定义:
//注册表并添加权限,以下都用默认
@MethodAccess
public class User {
}
@MethodAccess
public class Comment {
}
@MethodAccess
public class Moment {
}
//AccessVerifier内添加权限
accessMap.put(User.class.getSimpleName(), getAccessMap(User.class.getAnnotation(MethodAccess.class)));
accessMap.put(Moment.class.getSimpleName(), getAccessMap(Moment.class.getAnnotation(MethodAccess.class)));
accessMap.put(Comment.class.getSimpleName(), getAccessMap(Comment.class.getAnnotation(MethodAccess.class)));
前端请求:
{
"[]":{ //请求一个数组
"page":0, //数组条件
"count":2,
"Moment":{ //请求一个名为Moment的对象
"content$":"%a%" //对象条件,搜索content中包含a的动态
},
"User":{
"id@":"/Moment/userId", //缺省依赖路径,从所处容器的父容器路径开始
"@column":"id,name,head" //指定返回字段
},
"Comment[]":{ //请求一个名为Comment的数组,并去除Comment包装
"count":2,
"Comment":{
"momentId@":"[]/Moment/id" //完整依赖路径
}
}
}
}
7.前端写操作请求
GraphQL:
代码中定义 mutation schema。
mutation {
likeStory(storyID: 12345) {
story {
likeCount
}
}
}
APIJSON:
操作单条记录必传"id":Long,操作多条记录必传"id{}":[Long],还有自动化权限校验,保证安全性。
{
"Moment":{
"id":12,
"praiseUserIdList+":[
38710
]
}
}
等我有时间再发篇博文,做一个详细的比较。
APIJSON 目前有 Java Server,Android,JavaScript 3 种实现,如果要和 GraphQL 比,这几种语言随便挑。
APIJSON 在线测试
http://39.108.143.172
APIJSON 项目主页
https://github.com/TommyLemon/APIJSON
欢迎使用、建议、吐槽、反馈 ^_^
确实很复杂,概念就很多很绕了,schema,mutation,Subscription,Source Stream,Notation Conventions... 而且 Request 和 Schema 并不是严格对应,尤其是多层嵌套的时候,Schema 内层对象得通过外部函数调用来获取。 字段关联居然要手动 resolve,前端发的每个 Request 都必须在后端先有对应的 Schema 声明。 GraphQL 并没有替代 REST,而是在 Web/Client 到 REST Sever 中间用 GraphQL Server 做了个中间层,最后还是调 REST API.
除了目标一致,其它完全不一样,不管是思路还是实现。 GraphQL 是查询语言,APIJSON 是 JSON 传输结构协议 (也包括内容)。 用 GraphQL 就要学习一堆概念和语法,前端要后端要写一大堆 schema 才能用对应 schema 结构的特定格式来请求,正因为 GraphQL 格式特殊,整个后端都要重写。 而 APIJSON 只是一个简单的 JSON 对应协议,采用标准且通用的的 JSON 格式,只有少数几个简单的概念,没有所谓的语法,Request 和 Response 的结构严格对应,支持任意组合任意嵌套任意内容,并且还能和原来的 RESTful API 无缝兼容,几乎无迁移成本。 可以试试 https://github.com/TommyLemon/APIJSON
完爆 Facebook/GraphQL,APIJSON 全方位对比解析 (一)-基础功能 https://juejin.im/post/5ae80edd51882567277433cf
完爆 Facebook/GraphQL,APIJSON 全方位对比解析 (二)-权限控制 https://juejin.im/post/5b17518c6fb9a01e75463096
完爆 Facebook/GraphQL,APIJSON 全方位对比解析 (三)-表关联查询 https://juejin.im/entry/5b4ff88f6fb9a04f914a8df5
https://www.oschina.net/news/120011
-news