Concepten, Ideeën, Strategieën
Concepten, Ideeën, StrategieënStrategieën voor versiebeheer van velden en directives

Strategieën voor versiebeheer van velden en directives

Lees eerst de gids Het schema uitbreiden via versiebeheer van velden, die de functie "field versioning" in Gato GraphQL uitlegt.

Gato GraphQL stelt velden en directives in staat het argument versionConstraint te ontvangen, om te kiezen welke specifieke versie (d.w.z. implementatie) van het veld/de directive gebruikt wordt:

query GetPosts {
  posts(versionConstraint: "^1.0") {
    id
    title(versionConstraint: ">=2.1")
    excerpt @strUpperCase(versionConstraint: "~1.5.3")
  }
}

Wat moet er gebeuren wanneer we het argument versionConstraint niet opgeven? Tot welke versie moet het veld surname in de onderstaande query bijvoorbeeld worden omgezet?

query GetSurname {
  account(id: 1) {
    # Welke versie moet worden gebruikt? 1.0.0? 2.0.0?
    surname
  }
}

We hebben hier twee aandachtspunten:

  1. Bepalen welke de standaardversie is wanneer er geen is opgegeven
  2. De client informeren dat er meerdere versies zijn om uit te kiezen

Voordat we deze aandachtspunten aanpakken, moeten we nagaan hoe goed GraphQL contextuele feedback biedt bij het uitvoeren van queries.

Contextuele feedback bieden bij het uitvoeren van queries

We moeten wijzen op een minder dan ideale situatie in GraphQL op dit moment: het biedt geen goede contextuele informatie bij het uitvoeren van queries. Dit is duidelijk zichtbaar bij deprecations, waarbij deprecation-gegevens alleen via introspectie worden getoond door de velden isDeprecated en deprecationReason op de types Field en Enum te bevragen:

{
  __type(name: "Account") {
    name
    fields {
      name
      isDeprecated
      deprecationReason
    }
  }
}

Het antwoord zal zijn:

{
  "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
        }
      ]
    }
  }
}

Maar bij het uitvoeren van een query met een verouderd veld…

query GetSurname {
  account(id: 1) {
    surname
  }
}

...verschijnt de deprecation-informatie niet in het antwoord:

{
  "data": {
    "account": {
      "surname": "Owens"
    }
  }
}

Dit betekent dat de ontwikkelaar die de query uitvoert actief introspectie-queries moet uitvoeren om erachter te komen of het schema is bijgewerkt en of er een veld als verouderd is gemarkeerd. Dat kan gebeuren… een keer in de zoveel tijd? Waarschijnlijk nooit?

Het zou een grote verbetering zijn voor het herzien van verouderde queries als de GraphQL API deprecation-informatie zou bieden bij het uitvoeren van queries met verouderde velden. Deze informatie wordt idealiter gegeven onder een nieuw toplevel-item deprecations, dat na errors en voor data verschijnt (overeenkomstig de suggestie van de specificatie voor het responsformaat).

Omdat een toplevel-item deprecations geen deel uitmaakt van de specificatie, voegt de "Proactive Feedback"-functie van Gato GraphQL ondersteuning toe voor betere feedback in het antwoord op de query door gebruik te maken van het generieke toplevel-item extensions, dat het uitbreiden van het protocol naar behoefte mogelijk maakt:

Deprecation-informatie in het query-antwoord

Versies bekendmaken via waarschuwingen

We hebben zojuist geleerd dat de GraphQL-server het toplevel-item extensions kan gebruiken om deprecations te verstrekken. We kunnen dezelfde methode gebruiken om een warnings-item toe te voegen, waarin we de ontwikkelaar informeren dat een veld voorzien is van versiebeheer. We bieden deze informatie niet altijd; alleen wanneer de query een veld bevat waarop versiebeheer is toegepast en het argument versionConstraint ontbreekt.

De standaardversie voor een veld definiëren

Er zijn verschillende benaderingen die we kunnen toepassen, waaronder:

  1. versionConstraint verplicht maken
  2. Standaard de oude versie gebruiken tot een bepaalde datum, waarop de nieuwe versie de standaard wordt
  3. Standaard de nieuwste versie gebruiken en query-ontwikkelaars aanmoedigen om expliciet aan te geven welke versie ze willen gebruiken

Laten we elk van deze strategieën verkennen en bekijken wat de antwoorden zijn bij het uitvoeren van deze query:

query GetSurname {
  account(id: 1) {
    surname
  }
}

1. versionConstraint verplicht maken

Dit is de meest voor de hand liggende aanpak: verbied de client om de versiebeperking niet op te geven door het veldargument verplicht te maken. Als het dan niet is opgegeven, retourneert de query een foutmelding.

Het uitvoeren van de query zal het volgende antwoord geven:

{
  "errors": [
    {
      "message": "Argument 'versionConstraint' in field 'surname' cannot be empty"
    }
  ],
  "data": {
    "account": {
      "surname": null
    }
  }
}

2. Standaard de oude versie gebruiken tot een bepaalde datum waarop de nieuwe versie de standaard wordt

Blijf de oude versie gebruiken tot een bepaalde datum, wanneer de nieuwe versie de standaard wordt. Vraag query-ontwikkelaars tijdens deze overgangsperiode om via het nieuwe extensions.warnings-item in de query expliciet een versiebeperking voor de oude versie toe te voegen vóór die datum.

Het uitvoeren van de query kan het volgende antwoord geven:

{
  "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. De nieuwste versie gebruiken en gebruikers aanmoedigen om expliciet aan te geven welke versie ze willen gebruiken

Gebruik de nieuwste versie van het veld wanneer versionConstraint niet is ingesteld, en moedig query-ontwikkelaars aan om expliciet te definiëren welke versie moet worden gebruikt, door de lijst met alle beschikbare versies voor dat veld te tonen via een nieuw extensions.warnings-item:

Het uitvoeren van de query kan het volgende antwoord geven:

{
  "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"
    }
  }
}

Versiebeheer van directives

We kunnen dezelfde strategieën gebruiken voor het versieren van directives. Bijvoorbeeld, bij het uitvoeren van de query zonder een versiebeperking op te geven:

query {
  post(by: { id: 1 }) {
    title @strTitleCase
  }
}

Het kan een standaardversie aannemen en een waarschuwingsbericht voor de ontwikkelaar genereren om de query te herzien:

Een versioned directive bevragen zonder versiebeperkingen