2.5 KiB
context/audit-context
Purpose
Builds the per-request AuditContext handle the audit middleware attaches to c.set("audit", context) before invoking next(). Handlers call c.get("audit").log({...}) to fire explicit, semantic audit events alongside the implicit per-request audit that the middleware writes. This is the public extension point for application code — instead of importing auditService and threading it through every route, handlers reach into the Hono context.
The two-method surface (log + getLogger) is intentionally small so handler code stays focused on the event payload rather than the plumbing. The same underlying AuditLogger instance is reused across every request — there is one logger per app, not one per request — so chain-integrity links hold across handler-level and middleware-level events.
Features
- Stable two-method surface (
log,getLogger). - Hono
ContextVariableMapaugmentation declared in./context-augmentation.tssoc.get("audit")types asAuditContext(notunknown). - Zero per-request allocation beyond the lightweight wrapper object (no closures over the request, so the object can be cached if needed).
Usage: Basic
// In a route handler:
app.post("/users/:id/delete", async (c) => {
const audit = c.get("audit");
await audit.log({
type: "user.deleted",
severity: "warning",
actor: { userId: c.get("user")?.id },
target: { type: "user", id: c.req.param("id") },
});
return c.json({ deleted: true });
});
Usage: Advanced
// Reach the underlying logger for advanced ops:
app.get("/admin/audit-chain/verify", async (c) => {
const logger = c.get("audit").getLogger();
const events = await logger.query({ limit: 1000, order: "asc" });
const verdict = await logger.verifyChainIntegrity(events);
return c.json(verdict);
});
API Reference
createAuditContext(deps: { logger: AuditLogger }): AuditContextlog(event)→Promise<AuditEvent>getLogger()→AuditLogger
Implementation Notes
AuditContext does not carry the request, the principal, or the tenant. Handlers fill actor.userId, actor.sessionId, tenantId, and so on from c.get("user") / c.get("tenant") themselves. The middleware's per-request event populates those fields automatically; handler-level events stay explicit because the handler knows the semantic of the event (which user is the actor versus the target) better than the middleware does.