Files
2026-05-27 23:29:53 +01:00

3.0 KiB

tenant

(no submodules)

Purpose

Provides buildTenantPluginMiddleware, the Hono middleware factory the plugin installs on ctx.app during onInit. The factory composes createTenantMiddleware from @suaveplan/api-tenant (the canonical resolver + status-gate implementation) with an outer wrapper that records OTel resolution timing and outcome for every request. The result is a single middleware that handles per-request tenant resolution, fail-closed behaviour, and observability.

Features

  • Delegates the core resolution + status-gate (deleted / suspended / pending) to the well-tested @suaveplan/api-tenant middleware.
  • Wraps every invocation with performance.now() timing so the OTel histogram captures real latency, including the inner middleware's work.
  • Records tenant_plugin.resolutions_total{outcome, strategy} with outcomes resolved, missing, error so dashboards can break down failures by strategy.
  • Returns the inner middleware's response verbatim — no shape changes — so error handlers see the same TenantError they would see if they called createTenantMiddleware directly.

Usage: Basic Example

import { buildTenantPluginMiddleware } from "@suaveplan/api-plugin-tenant";
import { headerResolver } from "@suaveplan/api-tenant";
import { Hono } from "hono";

const resolver = headerResolver("x-tenant-id", lookupFn);
const app = new Hono();
app.use("*", buildTenantPluginMiddleware({
    resolver,
    required: false,
    allowSuspended: false,
}));

Usage: Advanced Example

import { buildTenantPluginMiddleware } from "@suaveplan/api-plugin-tenant";
import { chainResolvers, headerResolver, subdomainResolver, TenantCache } from "@suaveplan/api-tenant";

const resolver = TenantCache.wrap(
    chainResolvers(
        headerResolver("x-tenant-id", store.byId),
        subdomainResolver("app.example.com", store.bySlug)
    ),
    { ttlMs: 60_000, negativeTtlMs: 5_000, maxEntries: 10_000 }
);

app.use("*", buildTenantPluginMiddleware({
    resolver,
    required: true,
    allowSuspended: false,
    excludePaths: ["/health", "/ready", "/openapi.json", "/public"],
}));

API Reference

  • interface BuildTenantPluginMiddlewareOptions — { resolver, required, allowSuspended, excludePaths? }.
  • function buildTenantPluginMiddleware(options): MiddlewareHandler — returns a Hono MiddlewareHandler ready for app.use("*", ...).

Implementation Notes

Timing is recorded for every code path including the rethrow path so the histogram captures slow failures (network errors in the store, slow JWT verifications, etc.). The outer wrapper does not call next() directly — that responsibility stays inside createTenantMiddleware, which also owns the short-circuit for excludePaths. Splitting responsibilities this way means a future upgrade of @suaveplan/api-tenant (e.g. adding sampling or distributed tracing) lands automatically without changes to the plugin.