13 KiB
title, tags, created
| title | tags | created | ||
|---|---|---|---|---|
| Backend Architecture |
|
2026-05-23 |
Backend Architecture
Module-level architecture of the Express 5 + TypeScript + Mongoose backend at /Users/mojtabaheidari/code/backend (development branch).
[!info] Repo:
git@git.manko.yoga:222/nick/backend.git· Branch:development· Version: 2.6.3-beta (package.json:4)
1. Folder tree
backend/src/
├── app.ts # Express bootstrap, middleware chain, route registration
├── config/ # Per-feature config (legacy — most moved to shared/config)
├── controllers/ # HTTP request handlers (slim — delegate to services)
├── infrastructure/
│ ├── database/ # Mongoose connection, retries, graceful shutdown
│ └── socket/socketService.ts # Socket.IO server, rooms, emit helpers
├── models/ # Mongoose models — see 02 - Data Models/
├── routes/ # Express Router definitions (mounted in app.ts)
├── scripts/ # CLI utilities (seed:users, seed:categories, ...)
├── seeds/ # Seed data fixtures
├── services/
│ ├── ai/ # OpenAI integration (descriptions, moderation)
│ ├── auth/ # JWT, OAuth, Passkey, password reset
│ ├── blockchain/ # Web3 read/verify helpers
│ ├── blog/ # Posts, categories, comments
│ ├── chat/ # Conversations, messages, attachments
│ ├── dispute/ # Dispute lifecycle, evidence, mediator
│ ├── file/ # Multer uploads, MIME validation
│ ├── marketplace/ # PurchaseRequest, SellerOffer, Template, Shop
│ ├── notification/ # Templates, delivery, mark-as-read
│ ├── payment/ # Payment orchestration + shkeeper/ subdir
│ │ └── shkeeper/ # SHKeeper API, webhook, payout
│ ├── points/ # Loyalty points, levels, redemption
│ ├── redis/ # Redis client, cache helpers
│ ├── user/ # Profile, preferences, addresses
│ ├── admin/ # Admin-only operations
│ └── email/ # Nodemailer transport + templates
├── shared/
│ ├── config/index.ts # Centralised env-var loader (typed)
│ ├── middleware/ # auth, errorHandler, validators
│ ├── types/ # Cross-cutting TypeScript types
│ └── utils/response-handler.ts # Standard success/error response envelope
└── utils/ # Pure utility fns (logger, currencyUtils, etc.)
Tip
Service folders are self-contained: each typically has
<feature>Service.ts,<feature>Controller.ts,<feature>Routes.ts,<feature>Validation.ts. This makes each service movable to a microservice later with minimal coupling.
2. Bootstrap — src/app.ts
The bootstrap is intentionally linear and easy to audit. Execution order:
- Imports & env load —
dotenv(if used), thenimport { config } from './shared/config'. - Express app construction —
const app = express(); - Trust proxy —
app.set('trust proxy', config.trustProxy)so X-Forwarded-For works behind Nginx. - Security headers —
app.use(helmet({ ... })). - CORS —
cors({ origin: config.frontendUrl, credentials: true, methods: [...] }). - Body parsers —
express.json({ limit: '10mb' }),express.urlencoded({ extended: true }). - Static uploads —
app.use('/uploads', express.static(uploadDir)). - Health endpoint —
GET /healthfor Docker healthcheck and external monitors. - Route mounting — every
/api/*route registered before the error handler. - 404 handler — catches unmatched
/api/*. - Error handler — central
errorHandlermiddleware formats responses viaresponse-handler.ts. - HTTP server creation —
const server = http.createServer(app). - Socket.IO attach —
initSocket(server, corsOptions)(see Real-time Layer). - DB connect —
await connectDatabase(). - Redis connect —
await connectRedis(). - Listen —
server.listen(config.port, ...). - Graceful shutdown — SIGTERM/SIGINT handlers close server, drain sockets, close Mongoose, close Redis.
- Optional dev seeding — when
NODE_ENV === 'development'andSEED_USERS !== 'false', the bootstrap calls the seed scripts to provision default test users.
3. Middleware chain
| Order | Middleware | Where | Purpose |
|---|---|---|---|
| 1 | helmet |
global | Sets security headers (CSP, X-Frame-Options, ...). |
| 2 | cors |
global | Origin allow-list = config.frontendUrl, credentials enabled. |
| 3 | express.json / express.urlencoded |
global | Body parsers (10MB limit). |
| 4 | morgan (dev only) |
global | HTTP request log to stdout. |
| 5 | requestId |
global | Adds X-Request-Id for log correlation. |
| 6 | authMiddleware |
per-route | Verifies JWT, attaches req.user. Mounted only on protected routes. |
| 7 | `roleGuard('admin' | 'seller' | ...)` |
| 8 | validate(schema) |
per-route | express-validator + zod inputs. |
| 9 | controllerFn |
per-route | Delegates to service layer. |
| 10 | notFound |
tail | Returns 404 envelope for unmatched routes. |
| 11 | errorHandler |
tail | Catches thrown errors, formats response. |
Warning
Rate-limit middleware is disabled by default for personal use (see
app.ts:227cited in the architecture review). Enable before any real public traffic —express-rate-limitis already a dependency.
4. Route registration
The full route table mounted by app.ts:
| Mount path | Module | Auth | Notes |
|---|---|---|---|
/api/auth |
services/auth/authRoutes.ts |
mixed | login, register, refresh, OAuth, passkey |
/api/user |
services/user/userRoutes.ts |
JWT | profile, preferences |
/api/address |
services/user/addressRoutes.ts |
JWT | CRUD addresses |
/api/marketplace/requests |
services/marketplace/controllerRoutes.ts |
JWT | PurchaseRequest CRUD |
/api/marketplace/offers |
services/marketplace/controllerRoutes.ts |
JWT (seller) | SellerOffer CRUD |
/api/marketplace/templates |
services/marketplace/controllerRoutes.ts |
JWT (seller) | RequestTemplate CRUD |
/api/marketplace/categories |
services/marketplace/controllerRoutes.ts |
public read | Category list |
/api/marketplace/shop-settings |
services/marketplace/shopSettingsController.ts |
JWT (seller) | Shop profile |
/api/payment |
services/payment/paymentRoutes.ts |
JWT | Payment intent, status |
/api/payment/shkeeper/webhook |
services/payment/shkeeper/shkeeperWebhook.ts |
HMAC | Inbound from gateway |
/api/payment/payout |
services/payment/shkeeper/shkeeperPayoutService.ts |
JWT (seller/admin) | Withdraw to wallet |
/api/chat |
services/chat/chatRoutes.ts |
JWT | Conversations, messages |
/api/notification |
services/notification/notificationRoutes.ts |
JWT | List, mark read |
/api/dispute |
services/dispute/disputeRoutes.ts |
JWT | Open, evidence, resolve |
/api/blog |
services/blog/blogRoutes.ts |
mixed | Public read, admin write |
/api/admin |
services/admin/adminRoutes.ts |
JWT (admin) | Mod operations |
/api/points |
services/points/pointsRoutes.ts |
JWT | Balance, redemption |
/api/ai |
services/ai/aiRoutes.ts |
JWT | OpenAI-backed helpers |
/api/file |
services/file/fileRoutes.ts |
JWT | Multipart upload |
Full per-endpoint details → 03 - API Reference/API Overview and the service-specific reference docs.
5. Service layer pattern
Every service module follows this contract:
// services/<feature>/<feature>Service.ts
export class FeatureService {
static async createX(input, ctx): Promise<X> { /* business logic */ }
static async getX(id, ctx): Promise<X | null> { /* ... */ }
static async listX(filter, ctx): Promise<X[]> { /* ... */ }
static async updateX(id, patch, ctx): Promise<X> { /* ... */ }
}
- Controllers are thin — they validate request shape, call the service, format the response.
- Services own business logic, side effects (DB writes, socket emits, email sends).
- Models are pure schema — only Mongoose definitions + virtuals/hooks.
Cross-service calls are direct imports — no event bus yet. When the system grows, the seam between services is a natural place to introduce a message queue.
6. Dependency map (simplified)
flowchart TB
auth[auth]
user[user]
market[marketplace]
pay[payment]
chat[chat]
notify[notification]
dispute[dispute]
points[points]
file[file]
email[email]
socket[socket]
auth --> user
auth --> notify
market --> notify
market --> chat
market --> file
pay --> market
pay --> notify
pay --> socket
dispute --> market
dispute --> chat
dispute --> notify
points --> notify
notify --> socket
notify --> email
Note
socketand
7. Error handling
All thrown errors are caught by the central error handler. The expected shape:
class AppError extends Error {
statusCode: number; // HTTP 4xx/5xx
code: string; // app-specific code, e.g. "PAYMENT_ALREADY_REFUNDED"
details?: unknown; // optional debug payload
}
Response envelope (success path is {success:true,data:...}):
{
"success": false,
"error": {
"code": "VALIDATION_FAILED",
"message": "email is required",
"details": [{ "path": "email", "msg": "required" }]
}
}
See backend/src/shared/utils/response-handler.ts and backend/src/shared/middleware/errorHandler.ts.
8. Configuration
Single source of truth for env vars: src/shared/config/index.ts. It exports a typed config object — anywhere you would write process.env.X, instead import config.x.
Full table in Environment Variables. Critical ones:
| Key | Default | Notes |
|---|---|---|
PORT |
5001 |
Listen port |
MONGODB_URI |
mongodb://localhost:27017/nickapp |
Includes db name |
REDIS_URI |
redis://localhost:6379 |
+ REDIS_PASSWORD |
JWT_SECRET |
required | ≥32 chars |
JWT_EXPIRES_IN |
7d |
|
REFRESH_TOKEN_EXPIRES_IN |
30d |
|
FRONTEND_URL |
http://localhost:3000 |
CORS origin |
SHKEEPER_API_URL |
https://pay.amn.gg |
|
SHKEEPER_API_KEY |
required | |
SHKEEPER_WEBHOOK_SECRET |
required | HMAC key |
SMTP_* |
required | Nodemailer |
OPENAI_API_KEY |
required |
9. Database & connection management
- Mongoose is the ODM. Connection in
src/infrastructure/database/. - Connection options enable retryable writes, exponential backoff on reconnect.
- Indexes are defined on each model and auto-created on connect (Mongoose
autoIndex: truein dev, recommendfalsein prod with explicit migration). - See Data Model Overview for the relational map and per-model docs.
Redis client (in src/services/redis/) provides:
- Session caching (login attempts, lockout counters)
- Rate-limit counters (when middleware is enabled)
- Hot-path caches (category list, level configs)
10. Background work
The codebase has no dedicated queue runner — scheduled / async work is triggered inline from request handlers and uses setTimeout / setInterval patterns where needed (e.g., delayed retries). Consider introducing Bull / BullMQ if you grow:
- Payment status reconciliation (polling SHKeeper for stragglers)
- Notification email digests
- Auto-release escrow timers
- Token / refresh-token cleanup
11. Testing
Jest test suites in backend/__tests__/:
| File | Covers |
|---|---|
models.test.ts |
Schema validation, virtuals, hooks |
payment-services.test.ts |
Payment orchestration logic |
complete-backend.test.ts |
Cross-service integration |
shkeeper-backend.test.ts |
SHKeeper service + webhook |
Run with npm run test:all. CI runs the same. Reach for npm run test:models, npm run test:payment, etc. when iterating on a slice.
12. Notable files for orientation
| File | Why it matters |
|---|---|
src/app.ts |
Bootstrap — read once to understand wiring |
src/shared/config/index.ts |
All env vars, typed |
src/shared/utils/response-handler.ts |
Standard response shape |
src/shared/middleware/auth.ts |
JWT verify + RBAC |
src/infrastructure/socket/socketService.ts |
All socket plumbing |
src/services/payment/shkeeper/shkeeperWebhook.ts |
Webhook signature scheme |
src/services/marketplace/PurchaseRequestService.ts |
Core marketplace state machine |
src/services/auth/authService.ts |
Auth flows, lockout, hashing |
src/models/User.ts |
Central entity with role/preferences |
openapi.json |
Generated API spec — definitive endpoint list |
Related
- System Architecture — full system topology
- Frontend Architecture — how the FE talks to this BE
- Real-time Layer — Socket.IO room model
- Security Architecture — JWT, passkeys, webhook HMAC
- Data Model Overview — entity-relationship map
- Authentication Flow · Payment Flow - SHKeeper · Dispute Flow