Files
skills/docs/localization/i18n/i18n-backend-http/src/backend/backend.md
2026-05-29 08:20:56 +01:00

4.5 KiB

HttpBackend

(no submodules)

Purpose

HttpBackend is the canonical browser-tier translation source for the @suaveplan/i18n family. It fetches <loadPath> per (locale, namespace) via an injectable fetchFn (defaulting to globalThis.fetch) and parses the response body via an injectable parsePayload (defaulting to a JSON-object parser that rejects arrays, null, and primitive payloads). HTTP 404 maps to BACKEND_LOCALE_NOT_AVAILABLE — the chained-backend fallthrough signal — while HTTP 5xx, network failures, and parse failures map to BACKEND_READ_FAILED, the chain-stopping signal that prevents silent degradation when the transport itself is broken.

The backend is read-only by design (canSave: false, canTrackModification: false, canWatch: false). Consumers needing write-back wire MultiFileFsBackend or StorageBackend behind the catalog instead. Universal — runs identically in browsers, Bun, and Node.js >= 18; older Node runtimes get a clear BACKEND_READ_FAILED from defaultFetch() instructing them to inject fetchFn.

Features

  • 5-flag capability matrix { canRead: true, canReadMulti: false, canSave: false, canTrackModification: false, canWatch: false }.
  • URL templating via {locale} / {namespace} placeholders with encodeURIComponent per segment.
  • Pluggable parsePayload accepts non-JSON bodies (YAML, MessagePack, schema-validated JSON).
  • Pluggable customHeaders builder per request.
  • crossDomain: true shorthand for credentials: "include" + mode: "cors" (preserves explicit overrides in requestOptions).
  • Optional retry?: HttpBackendRetry (structurally compatible with @suaveplan/retry's RetryPolicy) wraps every fetch.
  • Optional signal?: AbortSignal — pre-aborted signals throw BACKEND_TIMEOUT without issuing a network request; mid-flight AbortError surfaces as BACKEND_TIMEOUT; the signal is threaded into the underlying RequestInit so fetch can cancel mid-flight.
  • Zod boundary validation throws INVALID_OPTIONS at construction.
  • Per-call OTel instrumentation under i18n.backend.http.*.

Usage: Basic Example

import { HttpBackend } from "@suaveplan/i18n-backend-http";

const backend = new HttpBackend({
    loadPath: "/i18n/{locale}/{namespace}.json",
});

const record = await backend.read("en", "auth");

Usage: Advanced Example

import { HttpBackend } from "@suaveplan/i18n-backend-http";

const backend = new HttpBackend({
    loadPath: "/i18n/{locale}/{namespace}.yaml",
    crossDomain: true,
    customHeaders: (locale) => ({ "x-locale": locale }),
    parsePayload: async (res) => parseYaml(await res.text()),
    retry: {
        run: async (op) => {
            for (let i = 0; i < 3; i++) {
                try {
                    return await op();
                } catch (err) {
                    if (i === 2) throw err;
                }
            }
            throw new Error("unreachable");
        },
    },
});

const ctrl = new AbortController();
setTimeout(() => ctrl.abort(), 2_000);
const record = await backend.read("en", "auth", ctrl.signal);

API Reference

class HttpBackend implements Backend

  • new HttpBackend(options: HttpBackendOptions) — Zod-validated; throws I18nError(I18N.INVALID_OPTIONS) on malformed input.
  • read(locale, namespace, signal?): Promise<TranslationRecord> — fetches and parses the URL. Throws BACKEND_LOCALE_NOT_AVAILABLE on 404; BACKEND_READ_FAILED on 5xx / network / parse failures; BACKEND_TIMEOUT on signal abort.

Helpers

  • buildUrl(loadPath, locale, namespace): string — substitutes {locale} and {namespace} with encodeURIComponent-applied values.
  • defaultParsePayload: ParsePayloadFn — JSON object parser. Rejects arrays, null, and primitives as BACKEND_READ_FAILED.
  • HttpBackendOptionsSchema: z.ZodObject — Zod boundary schema.

Implementation Notes

The class delegates to a private #fetchOnce helper that classifies network failures: AbortError / ABORT_ERR codes surface as BACKEND_TIMEOUT (so consumers can distinguish cancellation from transport faults), and I18nError(I18N.BACKEND_TIMEOUT) thrown from inside fetchFn (e.g. an injected fake) propagates unchanged. Everything else from the network surface — TypeError, refused connections, DNS failures, CORS preflights — maps to BACKEND_READ_FAILED. Capability flags stay false for write paths because the HTTP transport has no canonical PUT semantics across CDNs; consumers needing write-back wire a different backend.