Flick Knowledge Base
Repository docs from .qoder/repowiki
Search, browse, and read the generated project wiki without leaving the repo.
Middleware System
Referenced Files in This Document
index.tspipelines.tscompose-middleware.tsauthenticate.middleware.tsinject-user.middleware.tsrequire-auth.middleware.tsrequire-roles.middleware.tsrequire-user.middleware.tsstop-banned-user.middleware.tscontext.middleware.tserror.middleware.tszod-error.tsrequest-logging.middleware.tsrate-limit.middleware.tsrate-limiter.create-middleware.tsrate-limiter.factory.tscors.tsapp.ts
Table of Contents
Introduction
This document explains the middleware architecture of the backend service. It covers the middleware pipeline design, the authentication middleware chain, error handling middleware, request logging, rate limiting, and CORS configuration. It also details the execution order, interactions between middleware types, and provides practical guidance for creating custom middleware, implementing authentication flows, propagating errors, optimizing performance, and debugging middleware behavior.
Project Structure
The middleware system resides under the server module and is composed of:
- Authentication middleware set for session retrieval, user injection, role checks, and permission enforcement
- Pipeline composition utilities to assemble reusable middleware sequences
- Request logging via Morgan with structured log forwarding
- Rate limiting backed by Redis
- Global error handling for HTTP and Zod errors
- CORS configuration applied at the security layer
graph TB
subgraph "Express App"
A["express() app"]
B["observeRequest (context)"]
C["registerRequestLogging (Morgan)"]
D["applySecurity (CORS, helmet, etc.)"]
E["registerRoutes()"]
F["errorHandlers.notFound"]
G["errorHandlers.general"]
end
subgraph "Pipelines"
P1["identity"]
P2["authenticated"]
P3["withRequiredUserContext"]
P4["withOptionalUserContext"]
P5["checkUserContext"]
P6["adminOnly"]
end
A --> B --> C --> D --> E --> F --> G
P1 --> P2 --> P3 --> P4 --> P5 --> P6Diagram sources
app.tspipelines.ts
Section sources
app.tsindex.ts
Core Components
- Middleware composition engine: a small recursive runner that executes middleware in sequence until completion or error.
- Authentication chain: optional session retrieval, followed by user injection and onboarded user checks.
- Pipelines: prebuilt compositions for common patterns (identity, authenticated, with user context, admin-only).
- Request logging: Morgan-based structured logging with filtering and forwarding to the internal logger.
- Rate limiting: IP-based limits with Redis-backed counters and standardized response headers.
- Error handling: centralized handler for Zod and HTTP errors, plus a not-found terminator.
- CORS: origin-controlled cross-origin policy with credentials and allowed methods/headers.
Section sources
compose-middleware.tspipelines.tsrequest-logging.middleware.tsrate-limit.middleware.tserror.middleware.tscors.ts
Architecture Overview
The Express app initializes middleware in a specific order to ensure context availability, logging, security, routing, and robust error handling. Pipelines encapsulate common authentication and authorization sequences, enabling route handlers to focus on business logic.
sequenceDiagram
participant Client as "Client"
participant App as "Express App"
participant Ctx as "observeRequest"
participant Log as "registerRequestLogging"
participant Sec as "applySecurity"
participant Routes as "Routes"
participant NotFound as "errorHandlers.notFound"
participant Err as "errorHandlers.general"
Client->>App : HTTP Request
App->>Ctx : Set request context
App->>Log : Log request (structured)
App->>Sec : Apply CORS and security headers
App->>Routes : Dispatch to matched route
alt Route not found
Routes-->>NotFound : Not found
NotFound-->>Err : Throw HTTP 404
else Route handled
Routes-->>Client : Response
end
App->>Err : Catch unhandled errors (HTTP/Zod/other)
Err-->>Client : Standardized error responseDiagram sources
app.tserror.middleware.ts
Detailed Component Analysis
Middleware Pipeline Design
The composition utility runs middleware sequentially, passing control to the next in the chain until completion or an error is thrown. This enables building reusable pipelines for common flows.
flowchart TD
Start(["Start"]) --> Init["Initialize index i=0"]
Init --> Fetch["fn = middlewares[i++];"]
Fetch --> HasFn{"fn exists?"}
HasFn --> |No| Done["Call next()"]
HasFn --> |Yes| Run["Invoke fn(req,res,run)"]
Run --> Fetch
Done --> End(["End"])Diagram sources
compose-middleware.ts
Section sources
compose-middleware.tspipelines.ts
Authentication Middleware Chain
The authentication chain performs:
- Optional session retrieval and attaches auth/session to the request
- Optional user injection and onboarding checks
- Required authentication enforcement
- Role-based restrictions
sequenceDiagram
participant M1 as "authenticate"
participant M2 as "injectUser"
participant M3 as "requireAuth"
participant M4 as "requireOnboardedUser"
participant M5 as "requireRole('admin')"
M1->>M1 : Retrieve session from headers
M1-->>M2 : Attach req.auth/req.session
M2->>M2 : Inject user profile if missing
M2-->>M3 : Attach req.user
M3->>M3 : Validate session presence
M3-->>M4 : Proceed if authenticated
M4->>M4 : Validate onboarded state
M4-->>M5 : Proceed if onboarded
M5->>M5 : Enforce admin roleKey behaviors:
- Optional authentication: session retrieval without failing if absent
- User injection: auto-create user profile if needed, handle constraint violations, and audit creation
- Onboarding requirement: reject requests from incomplete profiles
- Role enforcement: restrict endpoints to admins
Diagram sources
authenticate.middleware.tsinject-user.middleware.tsrequire-auth.middleware.tsrequire-roles.middleware.tsrequire-user.middleware.ts
Section sources
authenticate.middleware.tsinject-user.middleware.tsrequire-auth.middleware.tsrequire-roles.middleware.tsrequire-user.middleware.tspipelines.ts
Error Handling Middleware
The error handler:
- Detects Zod validation errors and converts them to HTTP errors
- Logs warnings for HTTP errors and logs unexpected exceptions
- Returns standardized JSON responses with operational messages in production
- Provides developer-friendly metadata in development
flowchart TD
A["Error received"] --> B{"Is ZodError?"}
B --> |Yes| C["Convert to HttpError via zod-error"]
B --> |No| D{"Is HttpError?"}
D --> |Yes| E["Log warning with statusCode/code"]
D --> |No| F["Log error and create UNHANDLED_ERROR"]
C --> G["Build response"]
E --> G
F --> G
G --> H{"Development?"}
H --> |Yes| I["Include stack and meta"]
H --> |No| J["Omit stack"]
I --> K["Send JSON response"]
J --> KDiagram sources
error.middleware.tszod-error.ts
Section sources
error.middleware.tszod-error.ts
Request Logging System
The logging middleware:
- Uses Morgan to produce structured JSON logs
- Skips noisy endpoints (health checks) and HEAD requests
- Forwards parsed logs to the internal logger
- Attaches request ID and remote address tokens
flowchart TD
Start(["Incoming request"]) --> Tokens["Resolve Morgan tokens"]
Tokens --> Skip{"Skip condition?<br/>HEAD or /api/* ending with /healthz|/readyz"}
Skip --> |Yes| Pass["Do not log"]
Skip --> |No| Build["Build JSON log payload"]
Build --> Stream["Write to stream"]
Stream --> Logger["Forward to internal logger.http()"]
Logger --> End(["Done"])
Pass --> EndDiagram sources
request-logging.middleware.ts
Section sources
request-logging.middleware.ts
Rate Limiting Implementation
The rate limiter:
- Consumes points per client IP using Redis-backed limiter
- Sets X-RateLimit-* headers when available
- Returns 429 with Retry-After header when limits are exceeded
- Propagates internal errors (e.g., Redis connectivity) to the general error handler
sequenceDiagram
participant Client as "Client"
participant Lim as "ensureRatelimit.{auth|api}"
participant MW as "createRateLimiterMiddleware"
participant RL as "RedisLimiter"
participant Resp as "Response"
Client->>Lim : Request
Lim->>MW : Invoke middleware
MW->>RL : consume(req.ip)
alt Within limit
RL-->>MW : OK
MW->>RL : get(req.ip)
RL-->>MW : remaining/reset info
MW->>Resp : Set X-RateLimit-* headers
MW-->>Client : Continue
else Exceeded
RL-->>MW : RateLimiterRes(msBeforeNext)
MW->>Resp : Set Retry-After
MW-->>Client : 429 JSON
endDiagram sources
rate-limit.middleware.tsrate-limiter.create-middleware.tsrate-limiter.factory.ts
Section sources
rate-limit.middleware.tsrate-limiter.create-middleware.tsrate-limiter.factory.ts
CORS Configuration
CORS is configured with:
- Dynamic origins from environment
- Credentials support
- Allowed methods and headers
- Preflight success status
flowchart TD
A["Incoming request with Origin"] --> B{"Origin allowed?"}
B --> |Yes| C["Set Access-Control-Allow-Origin: origin"]
B --> |No| D["Reject or omit allow-origin"]
C --> E["Set credentials/methods/headers"]
D --> F["Proceed or fail preflight"]
E --> G["Allow request"]
F --> GDiagram sources
cors.ts
Section sources
cors.ts
Middleware Execution Order
The app mounts middleware in this order:
- Context: attach request identifiers and metadata
- Logging: structured request logs
- Security: CORS and related headers
- Routes: application endpoints
- Not found: terminate unmatched routes with 404
- General error: catch-all error handler
sequenceDiagram
participant App as "Express App"
participant Ctx as "observeRequest"
participant Log as "registerRequestLogging"
participant Sec as "applySecurity"
participant R as "Routes"
participant NF as "notFound"
participant EH as "general error"
App->>Ctx : Mount
App->>Log : Mount
App->>Sec : Mount
App->>R : Mount
App->>NF : Mount
App->>EH : MountDiagram sources
app.ts
Section sources
app.ts
Practical Examples
- Custom middleware creation
- Use the composition utility to chain your own middleware functions. See
compose-middleware.ts. - Export your middleware from a dedicated file and integrate via pipelines or route-specific mounting.
- Authentication flow
- For optional context: use the identity pipeline to attach session data without enforcing auth.
- For required context: use withRequiredUserContext to enforce authentication and onboarded user state.
- For admin-only endpoints: use adminOnly to enforce role gating.
- See
pipelines.ts.
- Error propagation
- Throw HttpError instances to propagate controlled errors; they will be normalized by the general error handler.
- Zod errors are automatically converted to HTTP errors.
- See
error.middleware.ts.
- Rate limiting integration
- Wrap route handlers or groups with ensureRatelimit.auth or ensureRatelimit.api.
- Inspect X-RateLimit-* headers and Retry-After on 429 responses.
- See
rate-limit.middleware.ts.
- Request logging
- Structured logs are forwarded to the internal logger; skip conditions avoid noise for health endpoints.
- See
request-logging.middleware.ts.
Dependency Analysis
The middleware system exhibits low coupling and high cohesion:
- Pipelines depend on composition and individual middleware functions
- Authentication middleware depends on the auth adapter and user repository
- Error handling depends on HTTP error types and Zod error conversion
- Rate limiting depends on Redis and the rate limiter factory
- CORS is configured centrally and applied by the security layer
graph LR
CM["compose-middleware.ts"] --> PL["pipelines.ts"]
PL --> AM1["authenticate.middleware.ts"]
PL --> AM2["inject-user.middleware.ts"]
PL --> AM3["require-auth.middleware.ts"]
PL --> AM4["require-roles.middleware.ts"]
PL --> AM5["require-user.middleware.ts"]
EH["error.middleware.ts"] --> ZE["zod-error.ts"]
RL["rate-limit.middleware.ts"] --> RMW["rate-limiter.create-middleware.ts"]
RMW --> RF["rate-limiter.factory.ts"]
APP["app.ts"] --> LOG["request-logging.middleware.ts"]
APP --> ERR["error.middleware.ts"]
APP --> SEC["applySecurity (CORS)"]Diagram sources
compose-middleware.tspipelines.tserror.middleware.tsrate-limit.middleware.tsrate-limiter.create-middleware.tsrate-limiter.factory.tsrequest-logging.middleware.tsapp.ts
Section sources
index.tsapp.ts
Performance Considerations
- Prefer optional authentication where feasible to minimize overhead for public endpoints.
- Use pipelines to reuse validated contexts and avoid repeated checks.
- Enable rate limiting early in the pipeline to protect downstream services.
- Keep logging selective (skip health endpoints) to reduce I/O overhead.
- Ensure Redis connectivity for rate limiting to avoid unnecessary retries.
- Cache user lookups during injection to reduce database load.
Troubleshooting Guide
- Authentication failures
- Verify session retrieval and cookie handling in the authenticate middleware.
- Confirm user injection logic and constraint handling for profile creation.
- Check role and onboarded user requirements for admin endpoints.
- Error handling
- Review the general error handler for logged codes and messages.
- Ensure Zod errors are converted to HttpError consistently.
- Logging
- Confirm Morgan stream forwards logs to the internal logger.
- Adjust skip conditions for noisy endpoints if needed.
- Rate limiting
- Validate Redis connectivity and limiter configuration.
- Inspect X-RateLimit-* headers and Retry-After on 429 responses.
- CORS
- Verify allowed origins and credentials configuration.
- Check preflight behavior for cross-origin requests.
Section sources
inject-user.middleware.tserror.middleware.tsrequest-logging.middleware.tsrate-limiter.create-middleware.tscors.ts
Conclusion
The middleware system provides a clean, composable foundation for authentication, logging, rate limiting, and error handling. By leveraging pipelines and a consistent composition model, developers can implement secure, observable, and resilient APIs while maintaining predictable performance characteristics.
Appendices
- Middleware export surface: see
index.ts - Application bootstrap and middleware mounting: see
app.ts