API Middleware¶
The API uses both global middleware and route-level middleware for security, authentication, and authorization.
Global Middleware¶
Applied to all requests in api/src/index.ts:
Helmet¶
Adds security headers including:
- Content-Security-Policy
- X-Frame-Options
- X-Content-Type-Options
- Strict-Transport-Security
- And other security-related headers
CORS¶
- Development: Allows
localhostorigins automatically - Production: Configure via
ALLOWED_ORIGINSenv var (comma-separated) - Credentials enabled for cookie-based authentication
JSON Body Parser¶
Parses JSON request bodies.
Cookie Parser¶
Parses session cookies for Better-Auth session management.
Note
Better-Auth routes are registered before the JSON parser, as Better-Auth handles its own body parsing.
Static File Serving¶
Serves uploaded photos from the configured storage path.
Authentication Middleware¶
File: api/src/middleware/auth.ts
requireAuth()¶
Validates the current session and populates req.user.
Behavior:
- Reads session token from cookies or
Authorization: Bearerheader - Validates against Better-Auth session store
- Queries user's assigned properties from
property_cleanerstable - Sets
req.userwith:userId— User's IDrole— User's role (owner/admin/cleaner/guest)propertyIds— Array of property IDs the user is assigned to
Response on failure: 401 Unauthorized
requireRole(...roles: string[])¶
Enforces role-based access control.
Behavior:
- Reads
req.user.role(set byrequireAuth) - Checks if the user's role is in the allowed list
- Returns
403 Forbiddenif the role is not permitted
Usage examples:
// Only owners can create properties
router.post('/', requireAuth(), requireRole('owner'), createProperty);
// Owners and admins can manage users
router.get('/', requireAuth(), requireRole('owner', 'admin'), listUsers);
requirePropertyAccess(paramName: string)¶
Restricts cleaners to their assigned properties.
Behavior:
- If user is
owneroradmin— access granted (full access) - If user is
cleaner— checks if the property ID from the route parameter is inreq.user.propertyIds - Returns
403 Forbiddenif the cleaner is not assigned to the property
Usage example:
// Cleaners can only see rooms for their assigned properties
router.get('/:propertyId/rooms',
requireAuth(),
requirePropertyAccess('propertyId'),
listRooms
);
Rate Limiting¶
Guest-facing endpoints use a simple in-memory rate limiter:
- Limit: 10 requests per minute per IP address
- Applied to:
POST /guest/:propertySlug/issuesandPOST /guest/:propertySlug/messages - Response on limit:
429 Too Many Requests
This is implemented inline in the guest route handler rather than as separate middleware.
Middleware Application Order¶
The middleware is applied in this order in index.ts:
helmet()— Security headerscors()— CORS configuration- Better-Auth handler — Auth endpoint routes (
/api/auth/*) express.json()— JSON body parsingcookieParser()— Cookie parsing- Static file serving (
/photos/*) - Route handlers (with
requireAuth,requireRole,requirePropertyAccessas needed)