Express
One middleware for verification and limits, a second one after your body parser for idempotency.
Every block on this page is a region of a file that the docs' test suite runs against the real @keyring/express before the page is published.
Minimal
import express from 'express';
import { keyring } from '@keyring/express';
const liveOrders = [{ id: 'ord_live_1', total: 4200 }];
const testOrders = [{ id: 'ord_test_1', total: 100 }];
const app = express();
app.use(
keyring({ resources: { orders: { live: liveOrders, test: testOrders } } }),
);
app.get('/v1/orders', (req, res) =>
res.json({
tenant: req.keyring?.tenantId,
env: req.keyring?.env,
degraded: req.keyring?.degraded,
orders: req.keyring?.orders,
}),
);
app.listen(Number(process.env.PORT ?? 3000), '127.0.0.1', () => {
process.stdout.write('listening\n');
});keyring() reads KEYRING_SECRET_KEY, KEYRING_PROJECT_ID and KEYRING_BASE_URL from the environment. req.keyring is KeyringContext | null: null on a route configured skip: true, and absent entirely on anything mounted above the middleware, so read it with ?..
Where to mount it
- Before your routes and before any body parser. Verification needs only the
Authorizationheader, and an unauthenticated caller must not get your JSON parser to run on a 5 MB payload. - After
express.static, or the middleware verifies your favicon. - Beside, not instead of, your own authentication. Keyring answers "which tenant of yours is calling, in which environment", not "which of your staff is calling".
- Your error handler stays last. The middleware either calls
next()or writes its own401,403or503; it never throws into yours.
Per-route rules and idempotency
const mw = keyring({
resources: { orders: { live: liveOrders, test: testOrders } },
routes: {
'GET /healthz': { skip: true },
'POST /v1/payments': {
onUnavailable: 'closed',
scopes: ['write:payments'],
},
'/v1/internal/*': { onUnavailable: 'stale-then-closed' },
},
onError: (error) => console.error('keyring', error),
});app.use(mw);
app.use(express.json());
app.use(keyringIdempotency(mw.keyring));A rule is keyed by METHOD /path or by a path pattern with *. skip leaves req.keyring null and records no event; onUnavailable and scopes are the modes and the scope check. keyring.stats().unmatchedRoutes names a rule that never matched, which is what catches a typo that would leave a money route on the fail-open default.
Idempotency is a second mount after your body parser because the fingerprint hashes the body. The docs' test suite drives this exact file: a replayed POST /v1/payments with the same Idempotency-Key answers the stored 201 with idempotent-replayed: true, the same key with a different body answers 422, and a key without write:payments answers 403 with missing_scopes: ['write:payments'].
onError
The SDK's one operator channel. A failed poll, a failed disk persist, a project that outgrew the cache: each arrives here as an Error. Log it. On a healthy deployment it is quiet.