# API Request Flow Documentation ## GET `/users/:userId` Request Flow ### Complete Request Journey ``` Mobile App (Android/iOS) ↓ GET http://10.0.2.2:3200/users/:userId Headers: Authorization: Bearer ↓ ┌─────────────────────────────────────────────────────────────┐ │ Backend Server (Port 3200) │ │ │ │ 1. Express App Layer │ │ ├─ CORS middleware │ │ ├─ express.json() │ │ │ │ │ 2. Global Middleware Chain │ │ ├─ requestContext │ │ │ └─ Extracts: IP, User-Agent, Request-ID │ │ │ │ │ ├─ auditLoggerMiddleware │ │ │ └─ Attaches audit logger to req.auditLogger │ │ │ │ │ 3. Route Matching │ │ └─ /users → userRoutes │ │ │ │ 4. Route-Level Middleware (userRoutes.js) │ │ ├─ jwtAuthenticate │ │ │ ├─ Extracts token from Authorization header │ │ │ ├─ Calls Auth Service API: │ │ │ │ POST http://auth-service:3000/auth/validate-token │ │ │ Body: { token: } │ │ │ ├─ Auth Service validates: signature, expiry, etc. │ │ │ └─ Sets req.user = { userId, role, ... } │ │ │ │ │ ├─ rateLimiterRead │ │ │ └─ Checks rate limits (Redis-based) │ │ │ │ │ ├─ requireUserOrAdmin (coarseAuthorize) │ │ │ └─ Verifies role is USER or ADMIN │ │ │ │ │ └─ fineAuthorize (for GET /:id only) │ │ └─ Verifies user can read this specific user │ │ (user can read own profile OR admin can read any)│ │ │ │ 5. Route Handler │ │ └─ GET /:id handler │ │ ├─ Extracts userId from req.params.id │ │ ├─ Database Query (via queryHelper) │ │ │ SELECT * FROM users WHERE id = :userId │ │ └─ Returns user data │ └─────────────────────────────────────────────────────────────┘ ↓ Response: { id, name, phone_number, avatar_url, ... } ↓ Mobile App ``` ### Services & Dependencies 1. **Backend Server** (Port 3200) - Main API server - Handles business logic 2. **Auth Service** (Port 3000) - JWT token validation endpoint: `/auth/validate-token` - Called by `jwtAuthenticate` middleware - Returns user payload if token is valid 3. **Database** (PostgreSQL) - Stores user data - Queried via queryHelper 4. **Redis** (Optional) - Used for rate limiting - Falls back gracefully if unavailable --- ## Implementation Pattern for All APIs ### Standard Middleware Chain Pattern All API routes should follow this consistent pattern: ```javascript // routes/exampleRoutes.js import express from "express"; import jwtAuthenticate from "../middleware/jwtAuthenticate.js"; import { rateLimiterRead, rateLimiterWrite } from "../middleware/rateLimiter.js"; import createCoarseAuthorize from "../middleware/coarseAuthorize.js"; import { fineAuthorize } from "../middleware/fineAuthorize.js"; const router = express.Router(); // 1. Apply authentication to ALL routes router.use(jwtAuthenticate); // 2. Apply rate limiting (read for GET, write for POST/PUT/DELETE) router.use(rateLimiterRead); // Default: read rate limiter // 3. Apply coarse-grained authorization (role-based) const requireUserOrAdmin = createCoarseAuthorize(['USER', 'ADMIN']); router.use(requireUserOrAdmin); // 4. Define routes with fine-grained authorization where needed router.get("/", async (req, res) => { // No fine authorization - all authenticated users can list }); router.get("/:id", fineAuthorize({ action: 'read', resource: 'resource_type', // e.g., 'user', 'order', 'listing' getResourceOwnerId: (req) => req.params.id, }), async (req, res) => { // Handler logic } ); router.post("/", rateLimiterWrite, // Override default rate limiter for write operations async (req, res) => { // Handler logic } ); router.put("/:id", rateLimiterWrite, fineAuthorize({ action: 'write', resource: 'resource_type', getResourceOwnerId: (req) => req.params.id, }), async (req, res) => { // Handler logic } ); ``` ### Middleware Execution Order ``` Request ↓ [Global Middleware - server.js] 1. requestContext 2. auditLoggerMiddleware 3. CORS 4. express.json() ↓ [Route Matching] ↓ [Route-Level Middleware - routes/*.js] 1. jwtAuthenticate → Validates JWT, sets req.user 2. rateLimiterRead/Write → Rate limiting 3. coarseAuthorize → Role-based access (USER/ADMIN) 4. fineAuthorize (optional) → Resource-level permissions ↓ [Route Handler] ↓ Response ``` ### Quick Reference: Route Setup Checklist For each new API route file: - [ ] Import required middleware - [ ] Apply `jwtAuthenticate` to all routes (router.use) - [ ] Apply appropriate rate limiter (router.use or per-route) - [ ] Apply coarse authorization (router.use) - [ ] Add fine authorization for resource-specific routes - [ ] Add audit logging in handlers (req.auditLogger) - [ ] Handle errors appropriately ### Example: New Resource Route ```javascript // routes/productsRoutes.js import express from "express"; import jwtAuthenticate from "../middleware/jwtAuthenticate.js"; import { rateLimiterRead, rateLimiterWrite } from "../middleware/rateLimiter.js"; import createCoarseAuthorize from "../middleware/coarseAuthorize.js"; import { fineAuthorize } from "../middleware/fineAuthorize.js"; import { select, insert, update } from "../db/queryHelper/index.js"; const router = express.Router(); // Authentication & Authorization router.use(jwtAuthenticate); router.use(rateLimiterRead); router.use(createCoarseAuthorize(['USER', 'ADMIN'])); // GET /products - List all (public listings, no fine auth needed) router.get("/", async (req, res) => { try { const products = await select({ table: 'products', where: { deleted: false } }); res.json(products); } catch (err) { res.status(500).json({ error: "Internal server error" }); } }); // GET /products/:id - Get specific product router.get("/:id", fineAuthorize({ action: 'read', resource: 'product', getResourceOwnerId: (req) => req.params.id, }), async (req, res) => { try { const product = await select({ table: 'products', where: { id: req.params.id, deleted: false }, limit: 1, }); if (product.length === 0) return res.status(404).json({ error: "Not found" }); res.json(product[0]); } catch (err) { res.status(500).json({ error: "Internal server error" }); } } ); // POST /products - Create product (requires write rate limiter) router.post("/", rateLimiterWrite, async (req, res) => { try { const product = await insert({ table: 'products', data: req.body, returning: '*', }); res.status(201).json(product); } catch (err) { res.status(500).json({ error: "Internal server error" }); } } ); // PUT /products/:id - Update product router.put("/:id", rateLimiterWrite, fineAuthorize({ action: 'write', resource: 'product', getResourceOwnerId: (req) => req.params.id, }), async (req, res) => { try { const updated = await update({ table: 'products', data: req.body, where: { id: req.params.id, deleted: false }, returning: '*', }); if (updated.length === 0) return res.status(404).json({ error: "Not found" }); res.json(updated[0]); } catch (err) { res.status(500).json({ error: "Internal server error" }); } } ); export default router; ``` --- ## Summary **All API requests follow this pattern:** 1. Mobile App sends request with JWT token 2. Backend receives request → Global middleware → Route middleware → Handler 3. JWT validated via Auth Service 4. Rate limiting applied 5. Authorization checks (coarse + fine) 6. Business logic executes 7. Response returned to app **Key Principles:** - JWT authentication is mandatory for all protected routes - Rate limiting prevents abuse - Coarse authorization checks roles (USER/ADMIN) - Fine authorization checks resource-level permissions - Audit logging tracks all operations