# create-meno-app — Complete Documentation Source: https://meno.borao.dev | npm: create-meno-app | GitHub: https://github.com/boraoksuzoglu/create-meno-app --- # Introduction **create-meno-app** is a CLI that scaffolds a production-ready **MongoDB · Express · Node.js** backend in seconds. Answer a handful of prompts and you get a complete, conventional API — not a toy starter, but the project you'd otherwise spend a day wiring by hand. ```bash npx create-meno-app my-api ``` ## Why it exists Every new Node backend starts with the same chores: folder structure, error handling, config validation, request logging, a route registry, auth, tests, linting, Docker. Most starters either give you too little (an `app.js` and a prayer) or too much (an opinionated framework you have to fight). create-meno-app takes a different path. It generates **plain, idiomatic Express code** with a few high-leverage conventions baked in, then gets out of your way. Scaffold a full backend in seconds, not hours — AI-friendly, with auto-generated docs that never drift. ## What makes it different - **Zero-boilerplate routing.** Drop a `*.routes.ts` file in `src/modules/` and it auto-mounts. No `app.use()`, no central registry. Async errors are wrapped for you. - **Docs that derive from code.** A single introspection engine reads your routes, Joi schemas and Mongoose models to power both Markdown docs and Swagger — so they can never drift. - **AI-friendly by construction.** Optional `CLAUDE.md`, Cursor and Kiro context files, plus a strict, repeatable module pattern that coding agents understand on the first try. - **A real feature matrix.** Auth, RBAC, email, uploads, rate limiting, logging, testing, Docker and CI are all opt-in toggles — JavaScript or TypeScript. ## Who it's for Anyone building a REST API on the MongoDB · Express · Node stack who wants to skip the setup ritual and start on features — solo developers, teams standardizing their services, and AI agents scaffolding backends on demand. ## Next steps - [Quick start](/docs/getting-started) — generate and boot your first project. - [CLI options](/docs/cli-options) — every prompt explained. - [Architecture](/docs/architecture) — the conventions that make it tick. --- # Quick start ## Prerequisites - **Node.js 18 or newer** (`node --version`) - A **MongoDB** instance — local, Docker, or a hosted Atlas cluster - npm (ships with Node) ## Scaffold a project Run the generator with `npx` — no global install needed: ```bash npx create-meno-app my-api ``` You'll be guided through a short set of prompts (language, auth, rate limiting, email, testing, Docker and more). Every answer is optional — accept the defaults for a fully loaded project or trim it down to the essentials. See [CLI options](/docs/cli-options) for the full list. ## Install and configure ```bash cd my-api npm install cp .env.example .env ``` Open `.env` and fill in your values — at minimum a `MONGODB_URI` and, if you enabled auth, a `SESSION_SECRET`. The app reads **every** environment variable through a single validated [config module](/docs/architecture#validated-config) and refuses to start if a required one is missing. If you enabled the auth module, bootstrap an admin account with: ```bash npm run create:admin ``` ## Run it ```bash npm run dev ``` The server boots with hot reload. Verify it's alive: - `GET /health` ```json { "status": "ok", "uptime": 12.4, "db": "connected", "timestamp": "..." } ``` If you enabled Swagger, the interactive API explorer is at `GET /docs` in development. ## Project scripts | Script | What it does | | --- | --- | | `npm run dev` | Start the dev server with auto-reload | | `npm start` | Start the production server | | `npm run generate ` | [Scaffold a new module](/docs/generate-command) | | `npm run docs` | [Generate Markdown API docs](/docs/docs-generator) | | `npm test` | Run the Jest suite (if enabled) | | `npm run build` | Compile TypeScript to `dist/` (TS projects) | ## Next steps - Generate your first feature module: [`npm run generate product`](/docs/generate-command) - Understand the [project structure](/docs/project-structure) - Learn the [core architecture](/docs/architecture) --- # CLI options The CLI is fully interactive — run it and answer the prompts. There are no flags to memorize; each answer flips a feature on or off and the generator renders only the files you need. ```bash npx create-meno-app my-api ``` ## Project basics ### Language Choose **TypeScript** or **JavaScript (ESM)**. This determines the file extensions (`.ts` / `.js`), whether a `tsconfig.json` is generated, and which dev tooling is wired up (`tsx`, `@types/*`, `@swc/jest` for tests). ## Authentication ### Include Auth module? Generates a complete auth module mounted at `/auth`: register, login, logout, forgot / reset password and change password. Adds the `User` model, session middleware (`express-session` + `connect-mongo`), bcrypt hashing and a `create:admin` script. See [Auth & RBAC](/docs/auth-rbac). ### Include RBAC? *(Only when auth is enabled.)* Adds role-and-permission middleware so you can guard routes by role. ## Core features ### Rate limiting? Adds request rate-limiting middleware. You then choose a **store**: - **In-memory** — simplest, but not safe across multiple instances. Good for dev. - **MongoDB** — shared across instances using your existing database. Recommended for production. - **Redis** — fast, distributed; requires a Redis server. See [Rate limiting](/docs/rate-limiting). ### Winston logger with daily rotation? Adds a structured Winston logger with daily-rotating log files. See [Logging](/docs/logging). ## File upload ### Include file upload? Adds upload handling. Choose a **provider**: - **Local disk (Multer)** — writes to disk; great for development. - **Google Cloud Storage** — production-grade, with a signed-URL cache for downloads. See [File upload](/docs/file-upload). ## Email ### Gmail API email service? Adds an email service built on the Gmail API with Handlebars templates (welcome, forgot-password, password-changed). ### Multi-language email templates? *(Only when email is enabled.)* Adds English **and** Turkish (`en` + `tr`) locale files for email copy. See [Email](/docs/email). ## Testing & quality ### Jest test setup? Adds Jest, `supertest` and `mongodb-memory-server` for fast, isolated integration tests — plus an example auth test suite. See [Testing](/docs/testing). ### ESLint + Prettier? Adds an ESLint flat config, Prettier, Husky and lint-staged with a pre-commit hook. See [Code quality](/docs/code-quality). ## DevOps & docs ### Swagger / OpenAPI docs? Serves an interactive Swagger UI at `GET /docs` in development. See [Swagger](/docs/swagger). ### Markdown docs generator? Adds `npm run docs`, which writes Markdown API documentation to `docs/`. See [Docs generator](/docs/docs-generator). ### Docker setup? Adds a `Dockerfile`, `docker-compose.yml` (with MongoDB) and `.dockerignore`, with a configured `HEALTHCHECK`. See [Docker & CI](/docs/docker-ci). ### GitHub Actions CI? Adds a CI workflow (lint → test → build → docs check) and a Dependabot config. ## AI context files A multi-select for which AI assistant context files to generate: - **Claude** → `CLAUDE.md` - **Cursor** → `.cursor/rules/project.mdc` - **Kiro** → `.kiro/steering/project.md` Each explains the stack, the non-negotiable conventions and the module pattern so an agent can extend the project correctly. See [AI-friendly](/docs/ai-context). Not sure what to pick? The defaults shown in the CLI produce a well-rounded, production-ready TypeScript backend. You can always add a feature later by generating a fresh project and copying the pieces you want — the structure is identical. --- # Project structure A generated project follows one consistent shape. Conditional folders appear only when you enable the relevant feature. ```text my-api/ ├── src/ │ ├── config/ │ │ └── config.ts # the single source of env config │ ├── constants/ │ │ ├── error-codes.ts │ │ └── roles.ts # (auth) │ ├── middlewares/ │ │ ├── auth.middleware.ts # (auth) │ │ ├── cors.middleware.ts │ │ ├── error.middleware.ts │ │ ├── ratelimit.middleware.ts # (rate limit) │ │ ├── request-id.middleware.ts │ │ └── validation.middleware.ts │ ├── models/ # ALL Mongoose models live here │ │ ├── example.model.ts │ │ └── user.model.ts # (auth) │ ├── modules/ # auto-mounted feature modules │ │ ├── auth/ # (auth) → /auth │ │ ├── example/ # → /example │ │ └── health/ # → /health │ ├── services/email/ # (email) │ ├── templates/emails/ # (email) Handlebars templates │ ├── locales/email/ # (email) en + tr │ ├── scripts/ │ │ ├── create-admin.ts # (auth) │ │ └── generate-docs.ts # (md docs) │ ├── utils/ │ │ ├── route-loader.ts # the auto-mount engine │ │ ├── doc-introspect.ts # (swagger or md docs) │ │ ├── paginate.ts │ │ └── ... │ └── server.ts ├── package.json ├── .env.example ├── tsconfig.json # (TypeScript) └── README.md ``` ## Two rules that shape everything ### Models live in `src/models/`, never in modules Every Mongoose schema lives in `src/models/`, regardless of which module uses it. This keeps data definitions discoverable and avoids circular imports between feature modules. ### Modules follow one pattern Each feature in `src/modules//` is exactly four files: ```text .validation.ts # Joi schemas .service.ts # business logic + DB queries (no HTTP) .controller.ts # HTTP handlers (plain async functions) .routes.ts # router + middleware — auto-mounts at / ``` The [`generate` command](/docs/generate-command) scaffolds all four (plus the model) for you. ## The config module Application code **never** reads `process.env` directly. Instead it imports the validated config: ```ts import { config } from '@/config/config.js'; const port = config.app.port; // ✅ // const port = process.env.PORT; ❌ ``` This centralizes validation, gives you typed access, and makes missing variables a startup error rather than a 3am surprise. More in [Architecture](/docs/architecture#validated-config). ## Path alias The `@/` alias points to `src/`, wired through `module-alias` at runtime and `tsconfig.json` for types — so imports stay clean from anywhere in the tree. --- # Architecture create-meno-app's value is in its conventions. They're deliberately small in number and high in leverage — each one removes a category of boilerplate you'd otherwise write by hand. ## Auto route loader Any file matching `src/modules//.routes.` is **automatically mounted** at `/`. There is no central registry and no `app.use()` to maintain. | File | Mounted at | | --- | --- | | `src/modules/auth/auth.routes.ts` | `/auth` | | `src/modules/product/product.routes.ts` | `/product` | | `src/modules/health/health.routes.ts` | `/health` | ```ts import express from 'express'; import * as ctrl from './product.controller.js'; const router = express.Router(); router.get('/', ctrl.list); router.post('/', ctrl.create); export default router; // auto-mounts at /product ``` Add `// @no-auto-load` as the first line of a routes file to skip auto-mounting and wire it yourself. ## Auto async wrapping Controllers are **plain async functions**. The route loader wraps every handler at mount time, so a thrown error or rejected promise is forwarded to the error middleware automatically. No `asyncHandler()`, no `try/catch` ceremony. ```ts // controllers are just async functions export const create = async (req, res) => res.status(201).json(await service.createProduct(req.body)); ``` Errors are normalized through a central error handler and the codes in `src/constants/error-codes.ts` — no magic strings scattered around. ## Validated config All environment access flows through `src/config/config.ts`. It reads each variable once, validates that required ones are present, and **throws at startup** with a clear message if something is missing — turning a runtime surprise into an immediate, obvious failure. ```ts import { config } from '@/config/config.js'; const uri = config.db.uri; ``` ## Request IDs Every request is tagged with an `X-Request-ID` (generated if the client didn't send one), so log lines and traces can be correlated across a request's lifecycle. ## Graceful shutdown On `SIGTERM` / `SIGINT`, the server stops accepting new connections, lets in-flight requests finish, and closes the MongoDB connection cleanly — the behavior orchestrators and load balancers expect during a rolling deploy. ## Health check - `GET /health` Returns status, uptime and database connectivity — ready to drop into a Docker `HEALTHCHECK` or a load-balancer probe. ## Pagination A small utility gives every list endpoint consistent paging: ```ts import { paginate, paginatedResponse } from '@/utils/paginate.js'; export const listProducts = async (query) => { const { page, limit, skip } = paginate(query); const [items, total] = await Promise.all([ Product.find().skip(skip).limit(limit), Product.countDocuments(), ]); return paginatedResponse(items, total, page, limit); // → { items, total, page, limit, totalPages, hasNext, hasPrev } }; ``` ## Index sync at startup `ensureIndexes()` runs on boot so the indexes declared on your schemas are guaranteed to exist — a missing index never silently degrades production performance. ## Security defaults Generated apps ship with `helmet` security headers, a configurable CORS middleware, cookie parsing and session handling already wired into `server.ts`. --- # Generate command Inside a generated project, `npm run generate` scaffolds a complete feature module that follows the project's conventions and auto-mounts immediately. ```bash npm run generate product ``` ## What it creates Five files, ready to extend: ```text ✓ src/models/product.model.ts ✓ src/modules/product/product.validation.ts ✓ src/modules/product/product.service.ts ✓ src/modules/product/product.controller.ts ✓ src/modules/product/product.routes.ts → mounted at /product ``` Because of the [auto route loader](/docs/architecture#auto-route-loader), the new routes are live the moment you save — no registration step. ## The module pattern Every generated module separates concerns the same way: - **`validation`** — Joi schemas only. Used by `validateBody` / `validateQuery` middleware and read by the [docs generator](/docs/docs-generator). - **`service`** — business logic and database queries. No HTTP objects here, so it's easy to test and reuse. - **`controller`** — thin HTTP handlers, written as plain async functions. - **`routes`** — the router and its middleware chain. ```ts // product.service.ts — pure logic, HTTP-free import { Product } from '@/models/product.model.js'; export const createProduct = (data) => Product.create(data); ``` ## Flags | Command | Effect | | --- | --- | | `npm run generate product` | Scaffold the `product` module | | `npm run generate product --dry-run` | Preview the files without writing them | | `npm run generate -- --list` | List existing modules | The generator is the same render engine the CLI uses to create projects, so generated modules always match the structure and style of the rest of your codebase. --- # Docs generator The Markdown docs generator turns your code into documentation. Run it and it writes a `docs/` tree describing every module and model — request bodies, query params and responses included. ```bash npm run docs ``` ## How it works A single introspection engine — `src/utils/doc-introspect.ts` — does the reading: 1. **Scans** `src/modules/` and `src/models/`. 2. **Derives request bodies** from your Joi schemas (`validateBody` / `validateQuery`). 3. **Infers responses** by tracing the controller → service → model chain. 4. **Reads Mongoose schemas** for fields, enums, `ref`s and indexes. Because the documentation is *derived*, it stays accurate as your code changes — there's no second source of truth to forget to update. ## Annotations Most routes need only one annotation to be documented: ```ts // @doc Create example | 201 // @desc Creates a new example and returns it. router.post('/', validateBody(createExampleSchema), ctrl.create); ``` | Annotation | Purpose | | --- | --- | | `// @doc \| ` | Include the route; sets summary and status code | | `// @desc ` | Longer description | | `// @body ` | Override the inferred request body *(rare)* | | `// @query ` | Override the inferred query params *(rare)* | | `// @response ` | Override the inferred response *(rare)* | `@doc` and `@desc` are the only annotations you'll reach for routinely — the body, query and response overrides exist for the occasional case the engine can't infer. ## Output ```text docs/ ├── README.md ├── MODULES.md ├── MODELS.md ├── modules/ # one page per module └── models/ # one page per model ``` ## Keep docs honest in CI ```bash npm run docs -- --check ``` The `--check` flag regenerates docs in memory and **fails** if the committed `docs/` is out of date — so stale documentation can't be merged. Wire it into your [GitHub Actions](/docs/docker-ci) pipeline. The same `doc-introspect` engine powers both this Markdown generator and the [Swagger UI](/docs/swagger). They read the same source, so they can never disagree. --- # Swagger / OpenAPI When you enable Swagger, the generated app serves an interactive **Swagger UI** for exploring and testing your API. - `GET /docs` ## Generated from your code The OpenAPI spec is produced by the shared `doc-introspect` engine — the same one behind the [Markdown docs generator](/docs/docs-generator). It reads your route annotations, Joi schemas and Mongoose models, so the Swagger UI reflects your real API without a hand-maintained spec. Because both outputs share one engine, the Swagger UI and the Markdown docs are always in sync. ## Development only Swagger UI is mounted in development by default and disabled in production, keeping your public surface area minimal. Adjust this in your [config](/docs/architecture#validated-config) if you want it available elsewhere. ## Documenting a route Annotate routes exactly as you would for the Markdown docs: ```ts // @doc List products // @desc Returns a paginated list of products. router.get('/', validateQuery(listSchema), ctrl.list); ``` The request body, query parameters and response shape are inferred automatically — see [Docs generator → Annotations](/docs/docs-generator#annotations) for the full reference. --- # AI-friendly create-meno-app is built so that an AI pair-programmer can be productive in your codebase immediately. That comes from two things: **generated context files** and **predictable conventions**. ## Generated context files During setup you can opt into context files for the assistants you use: | Assistant | File | | --- | --- | | Claude | `CLAUDE.md` | | Cursor | `.cursor/rules/project.mdc` | | Kiro | `.kiro/steering/project.md` | Each one documents the same essentials: - the stack (Node · Express · MongoDB) and language choice - the non-negotiable rules — where models live, how routes auto-mount, that controllers are plain async functions, and that env is read only through the config module - the module pattern (`validation → service → controller → routes`) - the scaffold command (`npm run generate`) and docs command (`npm run docs`) - the built-in endpoints (`/health`, and `/docs` when Swagger is on) ```md ## Non-negotiable rules - All Mongoose models live in src/models/ - Routes auto-mount from src/modules//.routes.ts - Controllers are plain async functions — no asyncHandler - Read env only via @/config/config ``` ## Why the conventions matter for AI The same properties that make the codebase pleasant for humans make it legible to models: - **One pattern, everywhere.** Because every module is structured identically, an agent that has seen one knows where to put the next — it edits the right file the first time. - **A single source of config.** No `process.env` scattered across files for a model to miss or duplicate. - **Derived documentation.** The [docs generator](/docs/docs-generator) emits clean Markdown an LLM can ingest as flat context. ## llms.txt This documentation site exposes machine-readable indexes for retrieval: - [`/llms.txt`](/llms.txt) — a concise index of every page with links - [`/llms-full.txt`](/llms-full.txt) — the entire documentation concatenated into one file Every docs page also offers a **Copy page** button (top-right) to grab the Markdown or open the page directly in ChatGPT, Claude, Claude Code or Cursor. Point your agent at /llms-full.txt for the whole manual in one request, or use a page's "Copy Markdown" action to feed it just the section you're working on. --- # Auth & RBAC Enabling the auth module generates a complete authentication system mounted at `/auth`, along with the `User` model and the middleware to protect your routes. ## Endpoints - `POST /auth/register` - `POST /auth/login` - `POST /auth/logout` - `POST /auth/forgot-password` - `POST /auth/reset-password/:token` - `POST /auth/change-password` ## How it works - **Sessions** — authentication is session-based using `express-session` with `connect-mongo`, so sessions persist in your MongoDB database. - **Password hashing** — passwords are hashed with `bcrypt`; plaintext is never stored. - **Password flows** — forgot / reset / change password are wired end to end. When the [email service](/docs/email) is enabled, reset links and confirmations are sent automatically using the bundled templates. ## Bootstrap an admin After your first install, create an initial admin user: ```bash npm run create:admin ``` ## Role-based access control If you enabled **RBAC**, role-and-permission middleware is generated so you can guard routes: ```ts import { requireRole } from '@/middlewares/auth.middleware.js'; router.delete('/:id', requireRole('admin'), ctrl.remove); ``` Roles are defined in `src/constants/roles.ts`. The `User` model carries a role field that the middleware checks on protected routes. RBAC is only available when the auth module is enabled — it builds on the same `User` model and session middleware. --- # Social sign-in When you enable Google and/or Apple sign-in, the auth module gains endpoints that verify a client-issued **ID token** and sign the user in with the **same session cookie** as password login. There are no redirect URIs and no callback routes — the same backend serves web, iOS, Android and React Native. Social sign-in builds on the auth module, so it's only offered when **Auth** is enabled. It reuses the `User` model and session middleware described in [Auth & RBAC](/docs/auth-rbac). ## How it works 1. The **client** (web SDK or native iOS/Android/React Native) runs the sign-in UI and obtains a signed **ID token (JWT)**. 2. It POSTs that token to the backend. 3. The backend **verifies the signature** against Google's / Apple's public keys and checks the token's `aud` against your configured client IDs. 4. It **finds or creates** the user (linking to an existing account when the verified email matches) and issues the session cookie. Because verification is all the server does, the flow is identical for every platform — you just list every client ID that may call your API as an accepted audience. ## Endpoints - `POST /auth/google` - `POST /auth/apple` ## Configuration Each provider accepts a **comma-separated list of client IDs** — add one per platform you ship (web, iOS, Android). The token's `aud` claim must match one of them. ```bash # Google — web, iOS and/or Android OAuth client IDs GOOGLE_CLIENT_IDS=your-web-client-id.apps.googleusercontent.com,your-ios-client-id.apps.googleusercontent.com # Apple — Services ID (web) and/or app bundle ID (native) APPLE_CLIENT_IDS=com.yourcompany.app ``` Create the IDs in the [Google Cloud Console](https://console.cloud.google.com/apis/credentials) and the [Apple Developer portal](https://developer.apple.com/account/resources/identifiers/list/serviceId) (enable *Sign in with Apple*). ## Web client Send requests with `credentials: 'include'` so the browser stores the session cookie. ```js // Google Identity Services google.accounts.id.initialize({ client_id: 'YOUR_WEB_CLIENT_ID.apps.googleusercontent.com', callback: ({ credential }) => fetch('/auth/google', { method: 'POST', headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify({ idToken: credential }), }), }); // Sign in with Apple JS const res = await AppleID.auth.signIn(); await fetch('/auth/apple', { method: 'POST', headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify({ identityToken: res.authorization.id_token }), }); ``` ## Mobile client ```js // React Native — @react-native-google-signin/google-signin (configure webClientId) const { data } = await GoogleSignin.signIn(); await fetch('https://your-api/auth/google', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ idToken: data.idToken }), }); // Expo — expo-apple-authentication const credential = await AppleAuthentication.signInAsync({ requestedScopes: [ AppleAuthentication.AppleAuthenticationScope.FULL_NAME, AppleAuthentication.AppleAuthenticationScope.EMAIL, ], }); await fetch('https://your-api/auth/apple', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ identityToken: credential.identityToken, name: credential.fullName?.givenName, }), }); ``` ## Account linking When a provider reports a **verified email** that already belongs to a user, the provider ID is attached to that existing account instead of creating a duplicate — so a user who first registered with a password and later taps "Sign in with Google" keeps a single account. Apple only returns the user's **name on the first sign-in**, and it arrives in the client payload rather than inside the token. Forward it as `name` on that first request — the backend stores it then. Social-only accounts are created without a password. --- # Email The email service sends transactional mail through the **Gmail API**, with HTML rendered from **Handlebars** templates. ## What's included - `src/services/email/email.service.ts` — the sending service - `src/templates/emails/*.hbs` — HTML templates - `src/locales/email/` — copy, per language ### Bundled templates | Template | Sent when | | --- | --- | | `welcome.hbs` | A new user registers | | `forgot-password.hbs` | A password reset is requested *(with auth)* | | `password-changed.hbs` | A password is changed *(with auth)* | ## Multi-language Enable **multi-language email templates** and you get `en` and `tr` locale files. The service picks the right copy at send time, so you can localize transactional mail without duplicating template markup. ```ts import { sendWelcomeEmail } from '@/services/email/email.service.js'; await sendWelcomeEmail(user.email, { name: user.name, lang: 'tr' }); ``` ## Configuration Gmail API credentials are read through the [config module](/docs/architecture#validated-config) from your `.env`. Fill in the relevant values (client ID, secret, refresh token and sender address) before sending in production. The email service integrates with the [auth module](/docs/auth-rbac): when both are enabled, password-reset and password-changed mails are sent automatically as part of the auth flows. --- # File upload Enabling uploads generates `src/utils/upload.ts` and the middleware to accept files. You pick a provider at setup time. ## Local disk (Multer) Writes uploaded files to disk using **Multer**. Simple and dependency-light — ideal for development and small, single-instance deployments. ```ts import { upload } from '@/utils/upload.js'; router.post('/avatar', upload.single('file'), ctrl.setAvatar); ``` ## Google Cloud Storage Uploads to a **GCS** bucket, with a **signed-URL cache** for serving downloads efficiently. This is the production-grade option: object storage scales independently of your app servers and works across multiple instances. Configure your bucket and credentials through the [config module](/docs/architecture#validated-config) via `.env`. ## Choosing | | Local disk | Google Cloud Storage | | --- | --- | --- | | Best for | Dev, small apps | Production, scale | | Multi-instance safe | No | Yes | | External dependency | None | GCS bucket + credentials | Start on local disk in development and switch to GCS for production — the upload call sites in your controllers stay the same; only the provider configuration changes. --- # Rate limiting Rate limiting adds middleware that caps how many requests a client can make in a window. You choose where the counters are stored. ## Stores ### In-memory Counters live in the process. Zero setup, but **not safe across multiple instances** — each replica counts independently. Use it for development or a single-process deployment. ### MongoDB Counters are stored in your existing MongoDB, shared across every instance. No extra infrastructure to run. **Recommended for most production deployments.** ### Redis Counters live in Redis — fast and distributed. Best when you already operate Redis or need the lowest-latency limiting at high request volumes. ## Choosing | Store | Multi-instance | Extra infra | Use when | | --- | --- | --- | --- | | In-memory | No | None | Dev, single process | | MongoDB | Yes | None (reuses your DB) | Most production apps | | Redis | Yes | Redis server | High volume / already on Redis | ## Configuration Limits (window and max requests) are read through the [config module](/docs/architecture#validated-config). Adjust them in `.env` per environment. The in-memory store looks fine in development but silently under-counts once you run more than one instance behind a load balancer. Switch to MongoDB or Redis before scaling out. --- # Logging The logger is a configured **Winston** instance with **daily-rotating files**, generated at `src/utils/logger.ts`. ```ts import { logger } from '@/utils/logger.js'; logger.info('Order placed', { orderId, userId }); logger.error('Payment failed', { err }); ``` ## What you get - **Structured logs** — log with a message and a metadata object; output is consistent and machine-parseable. - **Daily rotation** — files roll over each day so logs stay manageable and old logs are retained on a schedule rather than growing unbounded. - **Request correlation** — pair log lines with the [`X-Request-ID`](/docs/architecture#request-ids) to trace a single request end to end. ## Levels and configuration Log level and output behavior are driven through the [config module](/docs/architecture#validated-config), so you can run verbose logs in development and trim to warnings in production via `.env`. In containerized deployments you'll often also want logs on stdout for your platform to collect — the Winston setup is a normal logger you can extend with additional transports. --- # Testing The testing setup gives you real integration tests that run against an **in-memory MongoDB**, so they're fast, isolated and need no running database. ## What's included - **Jest** — the test runner - **supertest** — HTTP assertions against your Express app - **mongodb-memory-server** — a throwaway MongoDB spun up per test run - an **example auth test suite** to copy from ## Running tests ```bash npm test # run the suite npm run test:coverage # with a coverage report ``` ## Writing a test ```ts import request from 'supertest'; import { app } from '@/server.js'; it('creates a product', async () => { const res = await request(app).post('/product').send({ name: 'Widget' }); expect(res.status).toBe(201); expect(res.body.name).toBe('Widget'); }); ``` ## TypeScript For TypeScript projects, tests run through **`@swc/jest`** for fast transpilation, with a `moduleNameMapper` that strips `.js` from imports so `@/x.js` resolves to the `.ts` source. You write `import { x } from '@/x.js'` everywhere and it just works in both runtime and tests. Because each run gets a clean in-memory database, tests never depend on leftover state — and they pass the same way on your machine and in [CI](/docs/docker-ci). --- # Code quality Enabling code quality wires up linting, formatting and a pre-commit hook so style stays consistent without anyone thinking about it. ## Tools - **ESLint** — using the modern flat-config format - **Prettier** — opinionated formatting - **Husky** — Git hooks - **lint-staged** — run linters only on staged files ## Commands ```bash npm run lint # report problems npm run lint:fix # auto-fix what can be fixed npm run format # Prettier --write npm run format:check # verify formatting (CI-friendly) ``` ## Pre-commit hook Husky installs a `pre-commit` hook that runs **lint-staged**, which lints and formats your staged files before each commit. Broken or unformatted code never makes it into history. ```text .husky/ └── pre-commit # runs lint-staged ``` The ESLint and Prettier versions in a generated project are pinned to a coherent, mutually compatible set so the tooling works on Node 18+ out of the box. --- # Docker & CI The DevOps options generate everything you need to containerize the app and run a CI pipeline. ## Docker Enabling Docker adds: ```text Dockerfile # multi-stage build (compiles TS → dist/ for TypeScript) docker-compose.yml # app + MongoDB service .dockerignore # lean build context ``` - The `Dockerfile` is multi-stage for TypeScript projects, producing a small runtime image from the compiled `dist/`. - A `HEALTHCHECK` is configured against [`GET /health`](/docs/architecture#health-check). - `docker-compose.yml` brings up the app alongside a MongoDB container for local end-to-end runs. ```bash docker compose up ``` ## GitHub Actions CI Enabling CI adds a workflow at `.github/workflows/ci.yml` plus a Dependabot config. The pipeline runs the checks that matter: 1. **Lint** — ESLint 2. **Test** — Jest with coverage 3. **Build** — `npm run build` / TypeScript compile 4. **Docs check** — `npm run docs -- --check`, failing if the committed docs are [stale](/docs/docs-generator#keep-docs-honest-in-ci) ```text .github/ ├── workflows/ci.yml └── dependabot.yml ``` The docs-check step is what keeps your API documentation trustworthy over time — a PR that changes a route but forgets to regenerate docs fails CI instead of silently drifting. --- # Deployment A generated project is a standard Node application, so it deploys anywhere that runs Node — a VM, a container platform, or a managed host. ## Environment variables All configuration is read through the [config module](/docs/architecture#validated-config), which validates required variables at startup. Set them in your platform's environment (never commit `.env`). Common ones: | Variable | Purpose | | --- | --- | | `MONGODB_URI` | Connection string for your database | | `PORT` | Port the server listens on | | `NODE_ENV` | `production` in production | | `SESSION_SECRET` | Session signing secret *(with auth)* | If a required variable is missing, the app fails fast on boot with a clear message — so a misconfigured deploy never limps along in a half-broken state. ## Build & run ### TypeScript ```bash npm run build # compile to dist/ npm start # run the compiled server ``` ### JavaScript ```bash npm start ``` ## Containerized If you enabled [Docker](/docs/docker-ci), build and run the image directly: ```bash docker build -t my-api . docker run -p 3000:3000 --env-file .env my-api ``` The configured `HEALTHCHECK` hits [`GET /health`](/docs/architecture#health-check), and [graceful shutdown](/docs/architecture#graceful-shutdown) handles rolling deploys cleanly. ## Production checklist - Set `NODE_ENV=production` - Provide all required environment variables - Use a multi-instance-safe [rate-limit store](/docs/rate-limiting) (MongoDB or Redis) - Point logging where your platform collects it - Swagger UI is disabled in production by default — keep it that way unless you need it Run your [tests](/docs/testing) and `npm run docs -- --check` in CI before deploying so a broken build or stale docs never reaches production.