Skip to content

Retiring old mobile app versions

Goal: builds of your iOS app older than 4.2.0 call a field your API no longer serves well. Refuse them at the edge with a clear error, so the app can tell the user to update, and leave every newer build and every other client alone.

The rules for client profiles are on telling your clients apart; this recipe applies them to one case, a mobile app that already ships.

The running example is shop-api and its iOS app, built with Apollo iOS, which sends apollographql-client-name: ios-app and apollographql-client-version on every request. The build pipeline appends a build number to the version: build 118 of 4.2.0 sends 4.2.0-118.

  • A deployed service and its gpilot.toml.
  • An app that sends its name and version on every request. Apollo iOS sends the two apollographql-client-* headers; any other client can be set up to send them, or gp-client-name and gp-client-version.
gpilot.toml
[[client.profile]]
name = "ios-app"
when = "header:apollographql-client-name == 'ios-app'"
version = "split(header:apollographql-client-version, '-', 0)"
min_version = "4.2.0"

version takes the part before the first -. That is not cosmetic: versions are compared as semantic versions, where 4.2.0-118 is a prerelease of 4.2.0 and sorts below it. Without the split, every build of 4.2.0 itself would be refused. With it, 4.2.0-118 is read as 4.2.0 and clears the floor.

A request from the app below 4.2.0 is refused with 403 and CLIENT_VERSION_UNSUPPORTED, without reaching your origin, with the required version in the message. A request that matches no profile (your web app, a partner, a script) is not affected, because client.unknown stays at "allow".

Terminal window
curl -sS "https://$GRAPHPILOT_HOST/graphql" \
-H 'content-type: application/json' \
-H 'apollographql-client-name: ios-app' \
-H 'apollographql-client-version: 4.1.3-97' \
-d '{"query":"query ProductPage { product(id: \"p01\") { id name price } }"}'

The answer is 403, and the body’s extensions.code is CLIENT_VERSION_UNSUPPORTED. Send 4.2.0-118 instead and the request is served as before.

Handle the code in the app: when a response carries CLIENT_VERSION_UNSUPPORTED, show the update prompt instead of a generic error. The code is stable; the message text is for people.

Before you raise the floor, look at who it would refuse. In Insights, each request’s Client tab shows Recognized client ios-app and the version the profile read, so 4.2.0 for build 4.2.0-118. From Jet (Starter), the request list can show client name and version as columns, which is the quickest way to see which versions still call.

After the deployment, the refused requests are in the list with status 403. The Auth failures preset (under Errors) lists requests answered 401 or 403.

  • Three-part versions only. A version the profile cannot parse as a semantic version is refused, like one below the floor. An app that reports 4.2 is refused; make it report 4.2.0. A min_version that does not parse refuses nothing and logs CLIENT_MIN_VERSION_UNREADABLE.
  • Never write the floor with >=. header:apollographql-client-version >= '4.2.0' compares numbers, and a version is not one. Use min_version, or semver_gte in an expression; see versions.
  • A + build suffix needs no split. 4.2.0+118 is build metadata, and 4.2.0+118 is the same release as 4.2.0. Only a - suffix reads as a prerelease.
  • Raising the floor does not empty the cache. It refuses requests; the answers newer builds read stay cached as before.