Chiến lược phân phiên bản cho các trường và chỉ thị
Vui lòng đọc trước hướng dẫn Phát triển schema thông qua việc phân phiên bản trường, giải thích tính năng "field versioning" trong Gato GraphQL.
Gato GraphQL cho phép các trường và chỉ thị nhận đối số versionConstraint, để chọn phiên bản cụ thể nào (tức là phần triển khai) của trường/chỉ thị sẽ được sử dụng:
query GetPosts {
posts(versionConstraint: "^1.0") {
id
title(versionConstraint: ">=2.1")
excerpt @strUpperCase(versionConstraint: "~1.5.3")
}
}Điều gì sẽ xảy ra khi chúng ta không chỉ định đối số versionConstraint? Chẳng hạn, trường surname trong query dưới đây sẽ giải quyết đến phiên bản nào?
query GetSurname {
account(id: 1) {
# Phiên bản nào sẽ được dùng? 1.0.0? 2.0.0?
surname
}
}Chúng ta có hai mối quan tâm ở đây:
- Quyết định phiên bản mặc định nào sẽ được dùng khi không có phiên bản nào được cung cấp
- Thông báo cho client rằng có nhiều phiên bản để lựa chọn
Trước khi giải quyết những mối quan tâm này, chúng ta cần tìm hiểu xem GraphQL cung cấp phản hồi theo ngữ cảnh tốt đến mức nào khi chạy một query.
Cung cấp phản hồi theo ngữ cảnh khi chạy các queries
Chúng ta cần chỉ ra một hoàn cảnh chưa lý tưởng của GraphQL hiện nay: nó không cung cấp thông tin ngữ cảnh tốt khi chạy các queries. Điều này thể hiện rõ ràng liên quan đến các deprecation, khi dữ liệu deprecation chỉ được hiển thị thông qua introspection bằng cách truy vấn các trường isDeprecated và deprecationReason trên kiểu Field và Enum:
{
__type(name: "Account") {
name
fields {
name
isDeprecated
deprecationReason
}
}
}Phản hồi sẽ là:
{
"data": {
"__type": {
"name": "Account",
"fields": [
{
"name": "id",
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "name",
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "surname",
"isDeprecated": true,
"deprecationReason": "Use `personSurname`"
},
{
"name": "personSurname",
"isDeprecated": false,
"deprecationReason": null
}
]
}
}
}Tuy nhiên, khi chạy một query liên quan đến một trường đã bị deprecated…
query GetSurname {
account(id: 1) {
surname
}
}...thông tin deprecation sẽ không xuất hiện trong phản hồi:
{
"data": {
"account": {
"surname": "Owens"
}
}
}Điều này có nghĩa là developer đang thực thi query phải chủ động thực thi các introspection queries để tìm hiểu xem schema có được nâng cấp không và liệu có trường nào bị deprecated không. Điều đó có thể xảy ra… thỉnh thoảng? Có lẽ là không bao giờ?
Sẽ là một cải tiến lớn trong việc xem xét lại các queries lỗi thời nếu GraphQL API cung cấp thông tin deprecation khi thực thi các queries liên quan đến các trường đã bị deprecated. Lý tưởng nhất, thông tin này có thể được đưa ra dưới một mục cấp cao nhất mới deprecations, xuất hiện sau errors và trước data (theo đề xuất của spec về định dạng phản hồi).
Vì một mục cấp cao nhất deprecations không phải là một phần của spec, tính năng "Proactive Feedback" của Gato GraphQL thêm hỗ trợ cho phản hồi tốt hơn trong phản hồi của query bằng cách sử dụng mục cấp cao nhất dạng wildcard extensions, cho phép mở rộng giao thức khi cần:

Công bố các phiên bản thông qua cảnh báo
Chúng ta vừa biết rằng server GraphQL có thể sử dụng mục cấp cao nhất extensions để cung cấp các deprecation. Chúng ta có thể sử dụng phương pháp tương tự để thêm một mục warnings, trong đó chúng ta thông báo cho developer rằng một trường đã được phân phiên bản. Chúng ta không cung cấp thông tin này mọi lúc; chỉ khi query liên quan đến một trường đã được phân phiên bản và đối số versionConstraint vắng mặt.
Xác định phiên bản mặc định cho một trường
Có nhiều cách tiếp cận chúng ta có thể áp dụng, bao gồm:
- Làm cho
versionConstrainttrở thành bắt buộc - Sử dụng phiên bản cũ theo mặc định cho đến một ngày nhất định, khi đó phiên bản mới sẽ trở thành mặc định
- Sử dụng phiên bản mới nhất theo mặc định và khuyến khích các developer viết queries chỉ định rõ phiên bản nào sẽ được sử dụng
Hãy khám phá từng chiến lược này và xem phản hồi của chúng khi chạy query này:
query GetSurname {
account(id: 1) {
surname
}
}1. Làm cho versionConstraint trở thành bắt buộc
Đây là cách rõ ràng nhất: ngăn client không chỉ định ràng buộc phiên bản bằng cách làm cho đối số của trường trở thành bắt buộc. Khi đó, bất cứ khi nào không được cung cấp, query sẽ trả về lỗi.
Chạy query sẽ phản hồi với:
{
"errors": [
{
"message": "Argument 'versionConstraint' in field 'surname' cannot be empty"
}
],
"data": {
"account": {
"surname": null
}
}
}2. Sử dụng phiên bản cũ theo mặc định cho đến một ngày nhất định khi phiên bản mới trở thành mặc định
Tiếp tục sử dụng phiên bản cũ cho đến một ngày nhất định, khi đó phiên bản mới sẽ trở thành mặc định. Trong giai đoạn chuyển đổi này, yêu cầu các developer viết queries chủ động thêm ràng buộc phiên bản vào phiên bản cũ trước ngày đó thông qua mục extensions.warnings mới trong query.
Chạy query có thể phản hồi với:
{
"extensions": {
"warnings": [
{
"message": "Field 'surname' has a new version: '2.0.0'. This version will become the default one on January 1st. We advise you to use this new version already and test that it works fine; if you find any problem, please report the issue in https://github.com/mycompany/myproject/issues. To do the switch, please add the 'versionConstraint' field argument to your query (using Composer's semver constraint rules; see https://getcomposer.org/doc/articles/versions.md#writing-version-constraints): surname(versionConstraint:\"^2.0\"). If you are unable to switch to the new version, please make sure to explicitly point to the current version '1.0.0' before January 1st: surname(versionConstraint:\"^1.0\"). In case of doubt, please contact us at name@company.com.",
]
},
"data": {
"account": {
"surname": "Owens"
}
}
}3. Sử dụng phiên bản mới nhất và khuyến khích người dùng chỉ định rõ phiên bản nào sẽ được sử dụng
Sử dụng phiên bản mới nhất của trường bất cứ khi nào versionConstraint không được đặt, và khuyến khích các developer viết queries xác định rõ phiên bản nào phải được sử dụng, hiển thị danh sách tất cả các phiên bản có sẵn cho trường đó thông qua một mục extensions.warnings mới:
Chạy query có thể phản hồi với:
{
"extensions": {
"warnings": [
{
"message": "Field 'surname' has more than 1 version. Please add the 'versionConstraint' field argument to your query to indicate which version to use (using Composer's semver constraint rules; see https://getcomposer.org/doc/articles/versions.md#writing-version-constraints). To use the latest version, use: surname(versionConstraint:\"^2.0\"). Available versions: '2.0.0', '1.0.0'.",
]
},
"data": {
"account": {
"surname": "Owens"
}
}
}Phân phiên bản các chỉ thị
Chúng ta có thể sử dụng các chiến lược tương tự để phân phiên bản các chỉ thị. Chẳng hạn, khi chạy query mà không cung cấp ràng buộc phiên bản:
query {
post(by: { id: 1 }) {
title @strTitleCase
}
}Nó có thể giả định một phiên bản mặc định để sử dụng và tạo ra một thông báo cảnh báo để developer xem xét lại query:
