How tanmaysheoran/fastapi-audit works
1. Repository purpose
- This is a Python package, fastapi-audit, that adds audit logging to FastAPI/Starlette applications.
- Its architecture is centered on capturing activity at two boundaries: the HTTP request lifecycle and SQLAlchemy ORM/session changes.
2. Public integration surface
- The main entry points are AuditMiddleware, AuditConfig, create_tables, and audit_log.
- Applications typically install the middleware into FastAPI and point it at a separate audit database via control_db_url.
3. HTTP request/response subsystem
- middleware.py is the primary runtime interceptor.
- It captures request metadata such as method, path, query params, IP, user agent, and timing, then records response status and latency after the downstream app returns.
- It also supports optional request/response body capture and path-based exclusions.
4. Actor and identity extraction
- jwt_parser.py handles extraction of actor identity from the Authorization header JWT.
- The package expects claims like sub, actor_type, and email, and supports alias mapping for organization-specific actor types.
- This is the main identity boundary between application authentication and audit logging.
5. Tenant context boundary
- The middleware can read tenant metadata from request.state.tenant.
- Tenant information is not owned by the package; the app provides it, and the audit layer simply propagates tenant_id and tenant_slug into audit records.
6. ORM diff tracking subsystem
- orm_listener.py integrates with SQLAlchemy async sessions to detect INSERT, UPDATE, and DELETE activity.
- The repository uses session/event-level listeners plus context isolation so diffs remain associated with the right request or task.
- This is the non-HTTP path into the audit system and complements middleware-based request logging.
7. Sanitization and redaction
- sanitizer.py applies sensitive-field redaction before persistence.
- Default redaction covers fields like passwords, tokens, and secrets, and config allows custom redaction fields.
- This layer sits between data capture and storage, ensuring audit logs do not retain raw secrets.
8. Persistence model and writer
- models.py defines the audit record shape stored in the audit database.
- writer.py is responsible for writing records asynchronously, matching the package’s “fire-and-forget” design so audit I/O does not block the request path.
- The storage target is a single audit_logs table, with PostgreSQL/asyncpg as the supported backend.
9. Schema provisioning
- create_tables() provisions the audit schema, including the audit_logs table and its related enum/type.
- This is a deployment/setup boundary rather than a runtime feature; it ensures the audit database is prepared before logging begins.
10. Configuration layer
- config.py defines the package’s operational settings, likely using Pydantic-style config and AUDIT_ environment variables.
- Key knobs include the audit DB URL, body capture flags, ORM diff capture, anonymous logging behavior, excluded paths, and actor alias mappings.
11. Data flow summary
- A request enters FastAPI → middleware extracts actor/request context → optional ORM listeners collect diffs during the request → data is sanitized/redacted → writer persists an audit row to the separate audit database.
- Manual calls to audit_log() bypass the HTTP path but reuse the same persistence and sanitization pipeline.
12. Package and test structure
- The codebase is organized as a small, modular library with focused modules rather than a large application.
- Tests mirror the architecture: config, JWT parsing, middleware, ORM listening, sanitization, helpers, table creation, and export/import behavior.
- That suggests the core design goal is stable integration points rather than a standalone service.
Main entry points: middleware.py Middleware · writer.py Writer · __init__.py API Surface · config.py Config
Components 15
Public API
Entry points used by applications and deployment setup
- API Surface · Public imports and helper entry points for application integration
fastapi_audit/__init__.py - Create Tables · Provisions audit schema and supporting database types
- Audit Log · Manual logging entry point that bypasses the HTTP boundary
HTTP Audit
Request/response interception and actor extraction
- Middleware · Captures request context, response status, latency, and optional bodies
fastapi_audit/middleware.py - JWT Parser · Extracts actor identity from Authorization JWT claims and aliases
fastapi_audit/jwt_parser.py - Tenant State · Tenant metadata provided by the host app via request.state
- Request Context · Method, path, query, IP, user agent, timing, and bodies
ORM Audit
Session-level change capture for SQLAlchemy
- ORM Listener · Detects INSERT, UPDATE, and DELETE activity from SQLAlchemy sessions
fastapi_audit/orm_listener.py
Storage
Audit record shape, sanitization, and async persistence
- Sanitizer · Redacts secrets and sensitive fields before persistence
fastapi_audit/sanitizer.py - Audit Model · Defines the audit log record stored in the audit database
fastapi_audit/models.py - Writer · Persists audit records without blocking the request path
fastapi_audit/writer.py - audit_logs · Single audit storage table in the separate control database
- Audit Type · Related database enum/type used by the audit schema
Connections 22
- API Surface → Middleware: exports · Application installs the middleware from the package surface
- API Surface → Config: exports · Configuration is part of the public integration surface
- API Surface → Create Tables: exports · Deployment/setup entry point
- API Surface → Audit Log: exports · Manual logging entry point
- Middleware → Request Context: captures · Builds request/response audit context
- Middleware → JWT Parser: parses actor · Extracts actor identity from Authorization JWT
- Middleware → Tenant State: reads tenant · Propagates tenant metadata supplied by the host app
- Middleware → Sanitizer: redacts · Sanitizes captured request data before persistence
- Middleware → Writer: writes · Submits audit row asynchronously after the response
- ORM Listener → Sanitizer: redacts · Sanitizes ORM diffs before they are persisted
- ORM Listener → Writer: writes · Emits ORM change events into the same persistence path
- Audit Log → Sanitizer: redacts · Manual logging reuses the same redaction pipeline
- Audit Log → Writer: writes · Manual logging persists through the async writer
- Writer → Audit Model: uses · Serializes data into the audit record shape
- Writer → audit_logs: persists · Targets the audit_logs table in the audit database
- Create Tables → audit_logs: creates · Provisioning step creates the audit_logs table
- Create Tables → Audit Type: creates · Provisioning step creates related enum/type definitions
- Config → Middleware: configures · Controls body capture, exclusions, and anonymous logging
- Config → ORM Listener: configures · Controls ORM diff capture behavior
- Config → Sanitizer: configures · Provides custom redaction fields
- Helpers → JWT Parser: supports · Shared utility functions used by parsing and integration code
- Helpers → Writer: supports · Shared utility functions used by persistence code