Node / Express Guide
Folder Structure
Project layout, entry points, and environment configuration for the Express backend.
Folder Structure
src/
├── app.ts ← Express app setup, Socket.IO, global middleware
├── server.ts ← Entry point: DB connect → Redis init → listen
├── config.ts ← All env vars via typed getEnv() helpers
├── setup.ts ← MongoDB index creation on startup
├── controllers/ ← Request handlers (factory functions)
│ ├── auth.controller.ts
│ ├── user.controller.ts
│ └── resource.controller.ts
├── services/ ← Business logic and orchestration
│ ├── auth.service.ts
│ ├── user.service.ts
│ └── resource.service.ts
├── repositories/ ← MongoDB + Redis data access
│ ├── base.repo.ts ← Generic useRepo<T> with CRUD + caching
│ ├── user.repo.ts
│ └── resource.repo.ts
├── models/ ← Interfaces (IUser…) + classes (User…)
│ ├── user.model.ts
│ └── resource.model.ts
├── routes/ ← Express Router factories
│ ├── index.ts ← useRoutes() mounts all routers
│ ├── auth.route.ts
│ ├── user.route.ts
│ └── resource.route.ts
├── middleware/ ← Tenant resolution, role guards, load-organization
│ ├── tenant.middleware.ts
│ ├── load-organization.middleware.ts
│ └── role-guard.middleware.ts
├── validations/ ← Joi schema definitions
│ ├── local.validation.ts ← Shared helpers (mongoId, commonSchemas)
│ ├── auth.validation.ts
│ └── resource.validation.ts
├── types/ ← Shared TypeScript types
│ └── common.types.ts ← MongoQuery<T>, PaginatedResult<T>
├── utils/ ← log, pipeline, format, generate helpers
│ └── log.util.ts
├── enums/ ← TypeScript enums
│ ├── user.enum.ts
│ └── db-collections.enum.ts
├── events/ ← Socket.IO event handlers
├── cache/ ← Cache invalidation helpers
├── database/ ← Database migration / seed utilities
└── public/ ← Static assets
Environment Configuration
All env vars are accessed through typed helpers in src/config.ts — never process.env directly in other files.
Three helper functions handle different value types:
// src/config.ts
function getEnv(key: string, fallback?: string): string {
/* ... */
}
function getEnvNumber(key: string, fallback?: number): number {
/* ... */
}
function getEnvBoolean(key: string, fallback?: boolean): boolean {
/* ... */
}
If a required variable is missing (no fallback provided), the process exits immediately:
console.error(`\n❌ Missing required environment variable: ${key}`);
process.exit(1);
Exports use SCREAMING_SNAKE_CASE:
export const MONGO_URI = getEnv("MONGO_URI", "mongodb://localhost:27017");
export const MONGO_DB = getEnv("MONGO_DB", "default");
export const REDIS_HOST = getEnv("REDIS_HOST");
export const REDIS_PORT = getEnvNumber("REDIS_PORT", 6379);
export const REDIS_PASSWORD = getEnv("REDIS_PASSWORD");
export const ACCESS_TOKEN_SECRET = getEnv("ACCESS_TOKEN_SECRET");
export const REFRESH_TOKEN_SECRET = getEnv("REFRESH_TOKEN_SECRET");
export const PORT = getEnvNumber("PORT", 3001);
export const isDev = process.env.NODE_ENV !== "production";
app.ts Structure
// src/app.ts
import cors from "cors";
import express from "express";
import rateLimit from "express-rate-limit";
import helmet from "helmet";
import { errorHandler } from "@codisolutions23/node-utils";
import tenantMiddleware from "./middleware/tenant.middleware";
import loadOrganizationMiddleware from "./middleware/load-organization.middleware";
import { isDev } from "./config";
import router from "./routes";
const app = express();
app.set("trust proxy", 1);
app.use(cors({ origin: "*", credentials: true }));
app.use(express.json());
app.use(helmet());
app.disable("x-powered-by");
if (!isDev) {
app.use(rateLimit({ windowMs: 1 * 60 * 1000, max: 1000 }));
}
// Global middleware — runs before all routes
app.use(tenantMiddleware); // resolve organizationId
app.use(loadOrganizationMiddleware); // attach full org object
app.use("/api", router());
app.use(errorHandler); // must be last
export default function useApp() {
/* ... */
}
No
express.urlencoded() — JSON-only API. Rate limiting is production-only (1000 req/min). Tenant and load-organization middleware run globally, not per-route.server.ts Entry Point
// src/server.ts
import { useAtlas, useRedis, logger } from "@codisolutions23/node-utils";
import useApp from "./app";
import {
MONGO_DB,
MONGO_URI,
PORT,
REDIS_HOST,
REDIS_PORT,
REDIS_PASSWORD,
} from "./config";
async function startServer() {
// useAtlas is a static class — call methods directly
await useAtlas.connect({ uri: MONGO_URI, db: MONGO_DB, name: "default" });
const { initialize } = useRedis();
initialize({ host: REDIS_HOST, port: REDIS_PORT, password: REDIS_PASSWORD });
const { server } = useApp();
server.listen(PORT, () => {
logger.info(`Server listening at http://localhost:${PORT}`);
});
}
startServer();
