@stackra/versioning
v2.0.0
Published
API versioning for the Stackra frontend — advertise a version per outgoing HTTP request, honour backend Deprecation/Sunset signals, fan out to logger + monitoring. DI-first companion to the backend `stackra/versioning` wrapper.
Maintainers
Readme
@stackra/versioning
Frontend companion to the backend
stackra/versioning
wrapper. Advertises an API version on every outgoing HTTP request, honours the
backend's Deprecation / Sunset / Link: successor-version response
signals, and fans them out to the workspace logger and monitoring reporters
through the shared event bus.
Every runtime edge wires through DI — no new HttpClient(), no manual
interceptor registration at the call site. Consumers import
VersioningModule.forRoot(...) in their app module and the framework does the
rest.
Contract summary
| Concern | Where |
| -------------------- | ---------------------------------------------------------------------------------------------- |
| Backend pair | stackra/versioning (thin wrapper on shahghasiadil/laravel-api-versioning) |
| Config trio | config/versioning.config.ts — registerAs<IVersioningModuleOptions>(VERSIONING_CONFIG, ...) |
| Module | VersioningModule.forRoot(options) |
| Universal coverage | Request/response interceptor attached to every @stackra/http connection at boot |
| Per-request override | IHttpRequestConfig.meta.apiVersion — mirrors backend #[MapToApiVersion(...)] |
| Opt-out | meta.apiVersion: false — mirrors backend #[ApiVersionNeutral] |
| Response signals | Deprecation, Sunset, Link: rel="successor-version", X-API-Deprecation-* |
| Event fan-out | versioning.deprecated.hit, .sunset.approaching, .version.rejected |
| Architecture record | ADR-0068 — API versioning: frontend companion |
Installation
Already wired into the vite template. For a new app:
pnpm add @stackra/versioningPeer dependencies (every one already in a workspace app that ships the MUST + NICE tier):
@stackra/config— the ADR-0063 config factory@stackra/container— DI@stackra/contracts— token + interface vocabulary@stackra/logger— receives the deprecation-hit log line@stackra/support— shared helpersreflect-metadata— decorator metadata
@stackra/testing and react are optional peers — install when you consume
the ./testing or ./react subpath.
Wiring — app.module.ts
import { VersioningModule } from "@stackra/versioning";
import { versioningConfig } from "@/config";
@Module({
imports: [
ConfigModule.forRoot({ load: [httpConfig, versioningConfig /* … */] }),
WebHttpModule.forRoot(sync(httpConfig())),
// Versioning MUST come after HttpModule so the registrar finds
// every named connection at OnApplicationBootstrap.
VersioningModule.forRoot(sync(versioningConfig())),
],
})
export class AppModule {}Config template — src/config/versioning.config.ts
import { env, registerAs } from "@stackra/config";
import {
VERSIONING_CONFIG,
type IVersioningModuleOptions,
} from "@stackra/contracts";
export const versioningConfig = registerAs<IVersioningModuleOptions>(
VERSIONING_CONFIG,
() => ({
default: env("API_VERSION_DEFAULT", "1.0"),
strategy: env("API_VERSION_STRATEGY", "header"),
headerName: env("API_VERSION_HEADER_NAME", "X-API-Version"),
queryKey: env("API_VERSION_QUERY_KEY", "api-version"),
pathPrefix: env("API_VERSION_PATH_PREFIX", "api/v"),
// Different backends can speak different defaults.
connections: {
api: { default: "2.0" },
sdui: { default: "1.0" },
},
// Log every deprecated hit at "warn" level (default).
deprecationLog: { enabled: true, threshold: "warn" },
// Fire versioning.sunset.approaching 30 days before sunset.
sunsetWarningDays: env.number("API_VERSION_SUNSET_WARNING_DAYS", 30),
}),
);Per-request override — mirrors backend #[MapToApiVersion(...)]
The backend picks a per-method version via #[MapToApiVersion(['2.0'])]. The
frontend picks a per-request version via meta.apiVersion:
const http = useInject<IHttpClient>(HTTP_CLIENT);
// Uses the connection's default version (from versioningConfig).
const invoices = await http.get("/invoices");
// Overrides — this GET stamps X-API-Version: 2.0 regardless of default.
const invoicesV2 = await http.get("/invoices", { meta: { apiVersion: "2.0" } });
// Opt out — mirrors backend #[ApiVersionNeutral]. No header, no query, no
// path rewrite. Useful for /health, /.well-known/*, static asset endpoints.
const health = await http.get("/health", { meta: { apiVersion: false } });Response signals — deprecation fans out through the three-lane rule
When a response advertises Deprecation: true + Sunset: 2027-01-01 +
Link: <2.0>; rel="successor-version", the response interceptor:
- Lane 1 (DI) — records the hit on
DeprecationTracker.record(endpoint, signal). - Lane 3 (events) — emits
VERSIONING_EVENTS.DEPRECATED_HITon the shared bus. - Fan-out — logger writes a
warnline; monitoring reports to Sentry with adeprecationtag.
The React surface exposes the tracker via useApiVersion():
import { useApiVersion } from "@stackra/versioning/react";
function DeprecatedApiBanner(): ReactElement | null {
const { deprecatedHits } = useApiVersion();
if (deprecatedHits.length === 0) return null;
return (
<Alert status="warning">
<Alert.Content>
<Alert.Title>Deprecated endpoints</Alert.Title>
<Alert.Description>
{deprecatedHits.length} deprecated{" "}
{deprecatedHits.length === 1 ? "endpoint" : "endpoints"} hit this
session.
</Alert.Description>
</Alert.Content>
</Alert>
);
}Testing
@stackra/versioning/testing ships TestVersioningService — an in-memory
implementation of IVersioningService you can inject in unit / component
tests:
import { TestVersioningService } from "@stackra/versioning/testing";
import { VERSIONING_SERVICE } from "@stackra/contracts";
const testing = new TestVersioningService({
defaults: { api: "2.0", sdui: "1.0" },
});
// Simulate a deprecation hit:
testing.recordHit({
endpoint: "/invoices/legacy",
connection: "api",
signal: { message: "Use /invoices with v2.0", sunsetDate: "2027-01-01" },
});Cross-references
- Backend package —
stackra/versioning— header vocabulary + attribute surface this frontend package mirrors. - ADR-0068 — API versioning: frontend companion.
.kiro/steering/communication-patterns.md— the three-lane rule the deprecation fan-out follows..kiro/steering/package-conventions.md— the module + config trio + registrar-class pattern.
License
MIT — Copyright © 2026 Figentra L.L.C.
