Middleware
UniPulse uses a layered middleware architecture with 10 middleware modules that handle authentication, authorization, validation, rate limiting, and observability.
Middleware Stack (Execution Order)
Complete Middleware Reference
1. auth.ts -- JWT Authentication
Validates the JWT access token from the Authorization: Bearer <token> header using Passport.js. On success, attaches the User object to req.user.
// Usage in routes
router.get('/posts', authenticate, postController.getAll);
| Behavior | Detail |
|---|---|
| Valid token | req.user populated, request continues |
| Missing token | Returns 401 UNAUTHORIZED |
| Expired token | Returns 401 UNAUTHORIZED (client should refresh) |
| Invalid token | Returns 401 UNAUTHORIZED |
2. requireWorkspace.ts -- Workspace Context + RBAC
Loads the workspace from the request (header or param), verifies the user is a member, and checks their role against the minimum required role.
// Require at least EDITOR role
router.post('/posts', authenticate, requireWorkspace('EDITOR'), postController.create);
// Require at least ADMIN role
router.delete('/members/:id', authenticate, requireWorkspace('ADMIN'), workspaceController.removeMember);
| Role Hierarchy | Level |
|---|---|
OWNER | 5 (highest) |
ADMIN | 4 |
EDITOR | 3 |
VIEWER | 2 |
CLIENT | 1 (lowest) |
Populates: req.workspace, req.membership (role, permissions).
3. requireFeature.ts -- Feature Flag Gating
Checks if the workspace's current subscription plan includes the requested feature. Features are defined in the Feature model and linked to plans via PlanFeature.
router.get('/ai/suggestions', authenticate, requireWorkspace('VIEWER'), requireFeature('ai_suggestions'), aiController.getSuggestions);
| Behavior | Detail |
|---|---|
| Feature enabled | Request continues |
| Feature disabled | Returns 403 FORBIDDEN with upgrade message |
4. requireQuota.ts -- Usage Quota Enforcement
Checks the WorkspaceUsage model to ensure the workspace hasn't exceeded their plan's monthly quota for the requested resource.
router.post('/ai/caption/generate', authenticate, requireWorkspace('EDITOR'), requireQuota('ai_generations'), aiController.generateCaption);
| Behavior | Detail |
|---|---|
| Under quota | Request continues, usage incremented after success |
| Over quota | Returns 429 with quota exceeded message |
5. requireSuperAdmin.ts -- Super Admin Guard
Checks the isSuperAdmin flag on the user. Used for platform-level admin routes.
router.get('/admin/users', authenticate, requireSuperAdmin, adminController.listUsers);
6. validate.ts -- Zod Schema Validation
Validates the request body (and optionally query params) against a Zod schema from @unipulse/shared.
import { createPostSchema } from '@unipulse/shared';
router.post('/posts',
authenticate,
requireWorkspace('EDITOR'),
validate(createPostSchema), // Validates req.body
postController.create
);
On validation failure, returns 400 VALIDATION_ERROR:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Validation failed",
"details": [
{ "field": "caption", "message": "Required" },
{ "field": "platforms", "message": "Array must contain at least 1 element(s)" }
]
}
}
7. rateLimiter.ts -- Rate Limiting
Provides multiple rate limiters for different route categories:
| Limiter | Target Routes | Strategy |
|---|---|---|
authLimiter | /api/v1/auth/login, /api/v1/auth/register, /api/v1/auth/otp/* | Strict per-IP limiting |
aiLimiter | /api/v1/ai/* | Per-workspace, plan-based limits |
| Global limiter | All other routes | Liberal per-IP limiting |
Returns 429 RATE_LIMITED when exceeded.
8. errorHandler.ts -- Global Error Handler
Catches all unhandled errors and returns a structured JSON response. Handles known AppError types and Prisma errors.
// Registered as the last middleware
app.use(errorHandler);
| Error Type | Handling |
|---|---|
AppError | Maps to appropriate HTTP status and error code |
Prisma P2002 (unique constraint) | Returns 409 CONFLICT |
Prisma P2025 (not found) | Returns 404 NOT_FOUND |
Zod ZodError | Returns 400 VALIDATION_ERROR |
| Unknown errors | Returns 500 INTERNAL_ERROR, logs full stack trace |
9. upload.ts -- File Upload
Handles multipart file uploads using multer. Configures file size limits, allowed MIME types, and temporary storage.
router.post('/media', authenticate, upload.single('file'), mediaController.upload);
router.post('/media/batch', authenticate, upload.array('files', 10), mediaController.uploadBatch);
10. responseTimeTracker.ts -- Response Time Tracking
Measures and logs response times for all requests. Data is used for performance monitoring in the admin dashboard.
Applying Middleware to Routes
Per-Route (Most Common)
router.post('/posts',
authenticate,
requireWorkspace('EDITOR'),
requireFeature('publishing'),
requireQuota('posts'),
validate(createPostSchema),
postController.create
);
Per-Router (Group of Routes)
const postRouter = express.Router();
postRouter.use(authenticate);
postRouter.use(requireWorkspace('VIEWER'));
postRouter.get('/', postController.getAll);
postRouter.get('/:id', postController.getById);
postRouter.post('/', requireWorkspace('EDITOR'), validate(createPostSchema), postController.create);
Global (All Routes)
// Applied in index.ts
app.use(cors());
app.use(express.json());
app.use(responseTimeTracker);
app.use(errorHandler); // Must be last
- Auth Flow -- JWT and RBAC details
- API Reference -- route middleware assignments
- Error Handling -- error patterns
- Shared Validators -- Zod schemas used by validate.ts
- Environment Variables -- JWT_SECRET, ENCRYPTION_KEY