Strategie wersjonowania pól i dyrektyw
Przeczytaj najpierw przewodnik Rozwijanie schematu poprzez wersjonowanie pól, który wyjaśnia funkcję "field versioning" w Gato GraphQL.
Gato GraphQL pozwala polom i dyrektywom przyjmować argument versionConstraint, aby wybrać konkretną wersję (czyli implementację) pola/dyrektywy do użycia:
query GetPosts {
posts(versionConstraint: "^1.0") {
id
title(versionConstraint: ">=2.1")
excerpt @strUpperCase(versionConstraint: "~1.5.3")
}
}Co powinno się stać, gdy nie podamy argumentu versionConstraint? Na przykład, do której wersji powinno zostać rozwiązane pole surname w poniższej query?
query GetSurname {
account(id: 1) {
# Która wersja powinna być użyta? 1.0.0? 2.0.0?
surname
}
}Mamy tutaj dwie kwestie do rozważenia:
- Ustalenie, która wersja jest domyślna, gdy żadna nie została podana
- Poinformowanie klienta, że istnieje kilka wersji do wyboru
Zanim zajmiemy się tymi kwestiami, musimy sprawdzić, jak dobrze GraphQL zapewnia kontekstowe informacje zwrotne podczas wykonywania queries.
Dostarczanie kontekstowych informacji zwrotnych podczas wykonywania queries
Musimy wskazać pewną mniej niż idealną sytuację w GraphQL: nie oferuje on dobrych informacji kontekstowych podczas wykonywania queries. Jest to wyraźnie widoczne w odniesieniu do przestarzałości (deprecations), gdzie dane o przestarzałości są wyświetlane tylko poprzez introspekcję przez odpytywanie pól isDeprecated i deprecationReason na typach Field i Enum:
{
__type(name: "Account") {
name
fields {
name
isDeprecated
deprecationReason
}
}
}Odpowiedź będzie wyglądać następująco:
{
"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
}
]
}
}
}Jednak podczas wykonywania query obejmującej przestarzałe pole…
query GetSurname {
account(id: 1) {
surname
}
}...informacje o przestarzałości nie pojawią się w odpowiedzi:
{
"data": {
"account": {
"surname": "Owens"
}
}
}Oznacza to, że programista wykonujący query musi aktywnie uruchamiać queries introspekcji, aby dowiedzieć się, czy schemat został zaktualizowany i czy jakieś pole zostało uznane za przestarzałe. Może to się zdarzyć… raz na jakiś czas? Najprawdopodobniej nigdy?
Znaczącą poprawą w zakresie aktualizowania przestarzałych queries byłoby, gdyby API GraphQL dostarczało informacje o przestarzałości podczas wykonywania queries obejmujących przestarzałe pola. Idealnie, informacje te byłyby podawane w nowym wpisie najwyższego poziomu deprecations, pojawiającym się po errors i przed data (zgodnie z sugestią specyfikacji dotyczącą formatu odpowiedzi).
Ponieważ wpis najwyższego poziomu deprecations nie jest częścią specyfikacji, funkcja "Proactive Feedback" w Gato GraphQL dodaje obsługę lepszych informacji zwrotnych w odpowiedzi na query, używając ogólnego wpisu najwyższego poziomu extensions, który pozwala na rozszerzanie protokołu w razie potrzeby:

Ogłaszanie wersji poprzez ostrzeżenia
Właśnie dowiedzieliśmy się, że serwer GraphQL może używać wpisu najwyższego poziomu extensions do dostarczania informacji o przestarzałości. Możemy użyć tej samej metodologii, aby dodać wpis warnings, w którym informujemy programistę, że pole zostało poddane wersjonowaniu. Nie dostarczamy tych informacji zawsze; tylko wtedy, gdy query obejmuje pole, które zostało poddane wersjonowaniu, a argument versionConstraint jest nieobecny.
Definiowanie domyślnej wersji dla pola
Istnieje kilka podejść, które możemy zastosować, w tym:
- Uczynienie
versionConstraintobowiązkowym - Używanie starej wersji domyślnie do określonej daty, po której nowa wersja staje się domyślna
- Używanie najnowszej wersji domyślnie i zachęcanie programistów queries do jawnego wskazania, której wersji użyć
Przeanalizujmy każdą z tych strategii i zobaczmy ich odpowiedzi podczas wykonywania tej query:
query GetSurname {
account(id: 1) {
surname
}
}1. Uczynienie versionConstraint obowiązkowym
Jest to najbardziej oczywiste rozwiązanie: zabronić klientowi niepodawania ograniczenia wersji poprzez uczynienie argumentu pola obowiązkowym. Wtedy, gdy nie zostanie podany, query zwróci błąd.
Wykonanie query odpowie następująco:
{
"errors": [
{
"message": "Argument 'versionConstraint' in field 'surname' cannot be empty"
}
],
"data": {
"account": {
"surname": null
}
}
}2. Używanie starej wersji domyślnie do określonej daty, po której nowa wersja staje się domyślna
Kontynuowanie używania starej wersji do określonej daty, kiedy nowa wersja stanie się domyślna. W tym okresie przejściowym należy prosić programistów queries, aby jawnie dodali ograniczenie wersji dla starej wersji przed tą datą, poprzez nowy wpis extensions.warnings w query.
Wykonanie query mogłoby odpowiedzieć następująco:
{
"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. Używanie najnowszej wersji i zachęcanie użytkowników do jawnego wskazania, której wersji użyć
Używanie najnowszej wersji pola, gdy versionConstraint nie jest ustawiony, i zachęcanie programistów queries do jawnego zdefiniowania, której wersji należy użyć, wyświetlając listę wszystkich dostępnych wersji dla danego pola poprzez nowy wpis extensions.warnings:
Wykonanie query mogłoby odpowiedzieć następująco:
{
"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"
}
}
}Wersjonowanie dyrektyw
Możemy używać tych samych strategii do wersjonowania dyrektyw. Na przykład, wykonując query bez podania ograniczenia wersji:
query {
post(by: { id: 1 }) {
title @strTitleCase
}
}Może ona przyjąć domyślną wersję do użycia i wygenerować komunikat ostrzegawczy dla programisty, aby przejrzał query:
