GioJSdocs
On this page

Deployment helpers

getDeploymentId, isHardReloadResponse, handleHardReload and initDeploymentId: detect that a tab runs an older build than the server, and reload it.

ts
import {
  getDeploymentId,
  handleHardReload,
  initDeploymentId,
  isHardReloadResponse,
} from '@gio.js/react';

The client router already uses them: every navigation, prefetch, refresh and <GioForm> post sends the tab's deployment id, and a tab on an old build loads the new one in full. You need them only for your own fetch() calls.

Reference

How skew detection works

  1. Every server-rendered page carries an inline script setting window.__GIO_DEPLOYMENT_ID__ to the server's deployment id - a hash of the build, or GIO_DEPLOYMENT_ID when you pin one.
  2. A request from the tab sends it back as the x-deployment-id header.
  3. When the id differs from the server's, the server answers 409 with x-gio-action: hard-reload before any handler runs - for pages and route handlers alike. A request without the header is never refused.

[server] skew_protection = false makes the server ignore the header (no 409s); it logs a startup warning.

getDeploymentId

ts
getDeploymentId(): string | undefined

getDeploymentId() returns the id of the build that served this document, read from window.__GIO_DEPLOYMENT_ID__ on first use. Soft navigations never run the fetched page's scripts, so it keeps naming the build whose code runs in the tab. It is undefined on the server and on a page without the script (a static export).

isHardReloadResponse

ts
isHardReloadResponse(response: Response): boolean

isHardReloadResponse() is true when response.status is 409 and its x-gio-action header is hard-reload. A 409 your own handler returns does not match.

handleHardReload

ts
handleHardReload(): void

handleHardReload() reloads the page (window.location.reload()), so the tab fetches the new build's HTML and scripts. On the server, where there is no window, it does nothing, like the other helpers.

initDeploymentId

ts
initDeploymentId(): void

initDeploymentId() re-reads window.__GIO_DEPLOYMENT_ID__ into the value the getter above returns. Nothing needs to call it, since the getter reads the value on first use; it is kept for compatibility.

Examples

A fetch wrapper for your API

lib/api-client.ts
import { getDeploymentId, handleHardReload, isHardReloadResponse } from '@gio.js/react';

/** fetch() for this app's API that reloads the tab when a new build is live. */
export async function apiFetch(input: string, init: RequestInit = {}): Promise<Response> {
  const headers = new Headers(init.headers);
  const id = getDeploymentId();
  if (id !== undefined) headers.set('x-deployment-id', id);
  const res = await fetch(input, { ...init, headers });
  if (isHardReloadResponse(res)) handleHardReload();
  return res;
}

After a deploy, the next apiFetch from a tab on the old build gets the 409 before your handler runs - so a POST changed nothing - and the tab reloads onto the new build.

Good to know

  • Opt-in for your requests. Plain fetch() calls do not send x-deployment-id, so an old tab keeps talking to the new server - fine for an API that stays compatible across deploys.
  • Reloading loses unsaved state on the page (form input, scroll in nested views). For a request the user cannot afford to lose, save a draft before calling handleHardReload().
  • Pin the id across instances. Instances that run the same build compute the same id. If yours could differ (different build hosts), set the same GIO_DEPLOYMENT_ID on all of them and change it with every deploy; otherwise a load balancer alternating between them causes reloads.

Version history

VersionChanges
v0.1.0-beta.8The router sends the id on navigations, prefetches, refreshes and <GioForm> posts, and getDeploymentId() reads it on first use - initDeploymentId() is no longer needed. [server] skew_protection can turn the 409 off. handleHardReload() does nothing on the server instead of throwing.
v0.1.0-beta.1Introduced.