api-v1/API_FLOW_DOCUMENTATION.md

9.6 KiB

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 <JWT_TOKEN>
    ↓
┌─────────────────────────────────────────────────────────────┐
│ 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: <JWT_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:

// 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

// 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