诸位!我喜欢GraphQL!(意译:我个人对GraphQL的好处)
概括
你好。我是从朱迪卡中央的大洞穴中逃出来的人,在考虑用钻头挖地而自杀的时候,竟然与鲁西弗大人变得很好,并且几乎成为了朋友。
由于各种原因,我平时使用的电脑大约一个月不能使用,所以这次我想写一下GraphQL的优缺点。
由于GraphQL的魅力诸如静态类型等基础话题已被广泛探讨,为了简洁起见,在本文中将省略这些内容。
GraphQL 是一种用于API开发的开源查询语言。
GraphQL是由Facebook开发的一种查询语言,用于Web前端与服务器后端之间的通信。
怎样使用
例如,当我们想要从/graphql地址获取“国家名称和Alpha-3代码列表”的时候…
{
countries: getCountries(
startsWith: "A",
num: 2
) { name, alpha3 }
}
进行这样的查询时,
{
"data": {
"countries": [
{
"name": "Afghanistan",
"alpha3": "AFG"
},
{
"name": "Aland Islands",
"alpha3": "ALA"
},
]
}
}
可以得到类似这样的回应。
为什么有一种语言需要输入这么长的查询才能得出结果,却被称为”优秀的”?
是的,就像我对Java和PHP稍微有点反感一样,读者们可能也会想:“为什么这个失业的中年宅男会觉得使用这么冗长的查询语言是‘很棒’的?他是白痴还是疯了?”然而,虽然这些语言单独来看可能无用,但它们在组合使用时才能发挥真正的价值…
优点1:可以通过一次请求获取多个信息
上述介绍的查询实际上是一个缩写形式,实际上也可以使用下面这样的”长查询”进行编写。
query Get() {
countries: getCountries(
startsWith: "A",
num: 2
) { name, alpha3 }
}
在这种情况下,将查询操作称为Get,并在其中指示查询的内容。重要的是,在Get()操作中,查询的内容可以不仅仅是单个查询。
换句话说,可以进行多个询问。就像这样…
query Get() {
countries: getCountries(
startsWith: "A",
num: 2
) { name, alpha3 }
# getCountryで単一の国情報を取得する。
JCountry: getCountry(alpha2: "JP") { name, alpha3 }
}
这样做的话,就会得到这样的反应。
{
"data": {
"countries": [
{
"name": "Afghanistan",
"alpha3": "AFG"
},
{
"name": "Aland Islands",
"alpha3": "ALA"
},
],
"JCountry": {
"name": "Japan",
"alpha3": "JPN"
}
}
}
这个有什么优点呢?对于熟练的Web工程师来说,可能会注意到它在处理SPA时具有非常强大的特点,即可以通过一次访问获取所需的资源。
例如,
-
- 国のリスト (REST: /countries, GraphQL: countries)
-
- 価格のリスト (REST: /prices, GraphQL: prices)
-
- スポンサーのリスト (REST: /sponsors, GraphQL: sponsors)
- 取引先のリスト (REST: /partners GraphQL: partners)
如果在应用程序初始化的时候必须获取全部数据的话,使用REST框架可能需要发送GET请求到大约四个资源。
然而,这样做的话,在服务器端每次访问页面时至少需要处理5次HTTP请求(1次页面加载和4次资源获取)。当然,也可以准备一个名为/init的资源,在初始化时提供所有需要的信息,但这样的话就需要适当地管理/init提供的信息。
好的,就GraphQL而言,它会将特定的URL(例如/graphql)用作目的地
{
countries {name, code}
prices {planName, amount, features}
sponsors {name, desc}
partners {name, desc}
}
只需通过GET或POST请求一次发送此查询,就可以获取到所需的全部信息。而且,通过GraphQL的模式定义,可以轻松地管理所需信息。就像这样…
type Country {
name: String!
code: String!
alpha3: String!
}
type Price {
planName: String!
amount: Float!
features: [String!]
}
type Partner {
name: String!
desc: String!
}
type Query {
countries: [Country!]
prices: [Price!]
sponsors: [Partner!]
partners: [Partner!]
}
优点2:有望减少测试工作量。
GraphQL的规范文档中并没有明确规定“端点必须只有一个”。
只需一种选择:
我想要表达的是,例如,
[GET] /businesses: ユーザーのログイン必須。ユーザーが保有している企業の情報を返す。
[POST] /businesses: ユーザーのログイン必須。企業情報を登録する。
[GET] /transactions: ユーザーのログイン必須。ユーザーと企業との取引を参照する。
[POST] /transactions: ユーザーのログイン必須。企業取引を登録する。
[GET] /me: ユーザーのログイン必須。ログインしているユーザーの情報を参照する。
[POST] /me: ログインする
[DELETE] /me: ユーザーのログイン必須。ログアウトする
假设存在诸如REST资源之类的情况,下面的测试应该针对每个资源进行实施(即便是无业游手好闲的人也会这么做,难道Qiita的各位没有做吗?)。
-
- ユーザーがログインしている場合、必要な処理を行う (正常処理A)
-
- ユーザーがログインしていて特定の条件下の場合、エラーとなるようにする(異常処理A)
- ユーザーがログインしていない場合、認証エラーとなるようにする(異常処理B)
通常情况下,异常处理A的测试案例数量往往较多,但在本次假设中只有一个。针对每个资源,至少需要编写三个测试案例。也就是说,总共要编写20个测试案例,即6个资源乘以3再加2(其中2表示不需要登录的[POST] /me测试案例)。对于仅有6个资源,就需要编写20个测试案例。假设需要登录的资源数量增加到20个、30个等庞大的数量,那么同样需要实现以上所述的测试案例。在IT行业,注重速度的情况下,这将导致测试实现和测试本身花费过多时间。
所以,现在该上场的是GraphQL。通常,对于许多GraphQL,我们会为一个端点定义查询和突变:
directive @authRequired on FIELD_DEFINITION
type Mutation {
regBiz(biz: Business!): Business @authRequired
setTr(bizId: UUID!): Transaction @authRequired
login(username: String!, password: String!): User
}
type Query {
getBiz(bizId: UUID!): Business @authRequired
getTr(bizId: UUID!): Transaction @authRequired
me: User @authRequired
}
在上述情况下,我们定义了一个称为@authRequired的指令,用于指示需要进行认证,并将其应用于需要登录的处理。然而,使用这种方法,尽管测试的目标从资源变为查询,但所需的测试工作量并没有改变。
然而,正如我之前提到的,GraphQL只是定义了一种查询语言的规范,并没有明确要求端点一定只能有一个。
换句话说,可以将它分成两个端点,一个需要认证,一个不需要认证。就像这样:
type Mutation {
login(username: String!, password: String!): User
}
type Mutation {
regBiz(biz: Business!): Business
setTr(bizId: UUID!): Transaction
}
type Query {
getBiz(bizId: UUID!): Business
getTr(bizId: UUID!): Transaction
me: User @authRequired
}
通过这样做,例如,针对/prv,
-
- 确认/prv被分配给私人查询和变更
-
- 如果用户已登录,则进行处理(或返回HTTP状态码200)
- 如果用户未登录,则拒绝处理(或返回HTTP状态码401)
准备两个测试用例,然后对每个查询和突变执行正常和异常情况的测试就可以了。在这里,重要的是可以省略上述测试中的异常情况测试,因为我们已经在上面完成了。
根据这种方法的效能
我因为不愿花钱去使用云会计软件这种卑鄙的欲望,目前正在开发会计应用程序。使用Django编写REST资源,编写测试…在这种方法下,测试用例数量在(应用程序全体的百分之几的阶段)大约有1,200个,但通过这种方法,目前成功将测试用例数量减少到约550个左右。由于还有很多地方是REST资源,所以预计测试用例数量将进一步减少。厉害吧?
最后
说实话,虽然我在做应用程序,但我自己几乎一文钱都没有。所以我必须工作。如果有人愿意利用我,请务必与我联系,我会非常高兴。
好的。