Skip to main content

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);
BehaviorDetail
Valid tokenreq.user populated, request continues
Missing tokenReturns 401 UNAUTHORIZED
Expired tokenReturns 401 UNAUTHORIZED (client should refresh)
Invalid tokenReturns 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 HierarchyLevel
OWNER5 (highest)
ADMIN4
EDITOR3
VIEWER2
CLIENT1 (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);
BehaviorDetail
Feature enabledRequest continues
Feature disabledReturns 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);
BehaviorDetail
Under quotaRequest continues, usage incremented after success
Over quotaReturns 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:

LimiterTarget RoutesStrategy
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 limiterAll other routesLiberal 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 TypeHandling
AppErrorMaps to appropriate HTTP status and error code
Prisma P2002 (unique constraint)Returns 409 CONFLICT
Prisma P2025 (not found)Returns 404 NOT_FOUND
Zod ZodErrorReturns 400 VALIDATION_ERROR
Unknown errorsReturns 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

Cross-Reference