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.tsnever 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();