Guides & concepts

Important defaults

The behaviours that surprise people. Read this before you get bitten.

hapi makes a handful of choices that are defensible but not obvious. They are gathered here rather than scattered through the guides, because a default you discover during an incident is a bad default.

Aborts are not failures

An aborted request rejects with an abort-tagged error and does not fire onError. A user navigating away is a normal end of life for a request, not something your telemetry should record as a failure. Use err.timedOut to separate a timeout from a cancellation.

Mutation cancellation is off

Mutations are not cancelled on unmount unless you ask with withOptions({ cancelOnUnmount: true }). Cancelling a mutation is not an undo — the server may already have committed the write — so on by default it would silently drop saves whenever someone navigated mid-edit.

Every hook runs, even after one decides

When several onHttpError hooks are registered, all of them run even after one has supplied a fallback. This is what lets a logging hook fire regardless of what an earlier hook decided. Two consequences: the last fallback wins, and rethrow from any hook beats a fallback from any other.

Request validation is always synchronous

A Standard Schema whose validate returns a promise raises a configuration error rather than being awaited. Awaiting it would make the whole pipeline async and change hook ordering for every user of the library.

Headers are not in the cache key

If a header provider injects a per-user token, two users will share a cache entry. Put the identity in queryKeyPrefix, or use one QueryClient per session.

Response validation throws by default

If the server drifts from your schema the query errors, rather than passing the raw body through. An onResponseValidationError hook returning { suppress: true } reverses that for a specific endpoint.