Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Application
NODE_ENV=development

# Backend
BACKEND_PORT=3000

# Frontend
FRONTEND_PORT=80

# MongoDB (for local development - override in docker-compose)
MONGODB_URI=mongodb://localhost:27017/mean_db
MONGODB_PORT=27017

# Security
CORS_ORIGIN=http://localhost:4200
RATE_LIMIT_WINDOW_MS=900000
RATE_LIMIT_MAX=100

# JWT (optional - for auth module)
JWT_SECRET=your-super-secret-jwt-key-change-in-production
JWT_EXPIRES_IN=7d
44 changes: 44 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{
"root": true,
"env": {
"node": true,
"es2022": true,
"browser": true
},
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:@typescript-eslint/strict",
"plugin:@typescript-eslint/stylistic",
"prettier"
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 2022,
"sourceType": "module"
},
"plugins": ["@typescript-eslint"],
"ignorePatterns": [
"node_modules",
"dist",
"build",
"coverage",
"*.config.js",
"*.config.cjs",
".angular"
],
"overrides": [
{
"files": ["frontend/**/*.ts", "frontend/**/*.html"],
"parserOptions": {
"project": "frontend/tsconfig.json"
}
},
{
"files": ["backend/**/*.ts"],
"parserOptions": {
"project": "backend/tsconfig.json"
}
}
]
}
70 changes: 70 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
name: CI

on:
push:
branches: [main, develop, 'cursor/**']
pull_request:
branches: [main, develop, 'cursor/**']

jobs:
backend:
name: Backend
runs-on: ubuntu-latest
defaults:
run:
working-directory: backend

steps:
- uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'

- name: Install dependencies
run: npm ci

- name: Lint
run: npm run lint

- name: Test
run: npm test
env:
NODE_ENV: test
MONGODB_URI: mongodb://localhost:27017/mean_test
PORT: 3000

- name: Build
run: npm run build

frontend:
name: Frontend
runs-on: ubuntu-latest
defaults:
run:
working-directory: frontend

steps:
- uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'

- name: Install dependencies
run: npm ci

- name: Lint
run: npm run lint

- name: Test
run: npm test -- --watch=false --browsers=ChromeHeadless --no-progress

- name: Build
run: npm run build
env:
NODE_ENV: production
13 changes: 13 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
node_modules
dist
build
coverage
.angular
.env
*.log
.DS_Store
*.tsbuildinfo
out-tsc
.idea
.vscode
*.local
4 changes: 4 additions & 0 deletions .husky/commit-msg
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

npx --no -- commitlint --edit "$1"
4 changes: 4 additions & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

npx lint-staged
8 changes: 8 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
node_modules
dist
build
coverage
.angular
*.min.js
*.min.css
package-lock.json
9 changes: 9 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"semi": true,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "es5",
"printWidth": 100,
"bracketSpacing": true,
"arrowParens": "always"
}
142 changes: 141 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,141 @@
# mean-boilerplate
# MEAN Stack Boilerplate

A cloud-native, **12-Factor** MEAN stack (MongoDB, Express.js, Angular, Node.js) boilerplate fully written in **TypeScript**.

## Technology Stack

| Layer | Technology |
| -------- | --------------------------------- |
| Database | MongoDB + Mongoose ODM |
| Backend | Node.js v20+ / Express.js |
| Frontend | Angular 17+ (Standalone, Signals) |
| Language | TypeScript (strict mode) |
| Styling | SCSS |

## Directory Structure

```
/
├── .github/workflows/ci.yml # CI/CD pipeline
├── .husky/ # Git hooks (lint-staged, commitlint)
├── backend/ # Node.js/Express API
│ ├── src/
│ │ ├── config/ # Env validation (Zod)
│ │ ├── db/ # MongoDB connection
│ │ ├── middleware/ # Error handling, rate limiting
│ │ ├── modules/ # Domain modules (health, users, auth)
│ │ ├── utils/ # Logger, AppError, asyncHandler
│ │ ├── app.ts
│ │ └── server.ts
│ ├── tests/
│ ├── Dockerfile
│ └── package.json
├── frontend/ # Angular SPA
│ ├── src/
│ │ ├── app/
│ │ │ ├── core/ # Interceptors, services, guards
│ │ │ ├── features/ # Lazy-loaded feature modules
│ │ │ └── layout/
│ │ ├── environments/
│ │ └── styles/
│ ├── Dockerfile
│ └── package.json
├── docker-compose.yml
├── .env.example
└── package.json # Root scripts, lint-staged, husky
```

## Quick Start

### Prerequisites

- Node.js 20+
- Docker & Docker Compose (optional, for containerized run)
- MongoDB (local or Docker)

### Local Development

```bash
# 1. Clone and install
npm run install:all

# 2. Copy env files
cp .env.example .env
cp backend/.env.example backend/.env
Comment on lines +63 to +64
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The setup requires two separate .env files (.env at the root and backend/.env) with several overlapping variables (e.g., NODE_ENV, MONGODB_URI, JWT_SECRET). This can be confusing for developers and prone to configuration errors. Consider simplifying this to a single .env file at the project root. The backend application can be configured (e.g., using the dotenv package's path option) to read from the root-level file during local development.


# 3. Start MongoDB (if not running)
docker run -d -p 27017:27017 mongo:7

# 4. Start backend
npm run backend:dev

# 5. In another terminal, start frontend
npm run frontend:dev
```

- **Backend:** http://localhost:3000
- **Frontend:** http://localhost:4200

### Docker Compose

```bash
cp .env.example .env
docker-compose up -d
```

- **Backend:** http://localhost:3000
- **Frontend:** http://localhost:80
- **MongoDB:** localhost:27017

## API Endpoints

| Method | Path | Description |
| ------ | ----------------- | -------------------------- |
| GET | / | API info |
| GET | /health/liveness | Kubernetes liveness probe |
| GET | /health/readiness | Kubernetes readiness probe |
| GET | /api/users | List users (paginated) |
| GET | /api/users/:id | Get user by ID |
| POST | /api/auth/login | Login (placeholder) |
| GET | /api/auth/me | Current user (placeholder) |

## Configuration

Environment variables are validated at startup using **Zod**. See `.env.example` and `backend/.env.example` for the full schema.

Key variables:

- `NODE_ENV` – development | test | production
- `PORT` – Backend port (default 3000)
- `MONGODB_URI` – MongoDB connection string
- `CORS_ORIGIN` – Allowed frontend origin(s)
- `RATE_LIMIT_WINDOW_MS` / `RATE_LIMIT_MAX` – Rate limiting

## Code Quality

- **ESLint** + **Prettier** – shared config at repo root
- **Husky** – pre-commit (lint-staged) and commit-msg (commitlint)
- **Conventional Commits** – `feat:`, `fix:`, `chore:`, etc.

```bash
npm run lint
npm run format
```

## Testing

```bash
npm run backend:test # Jest
npm run frontend:test # Karma + Jasmine
```

## CI/CD

GitHub Actions (`.github/workflows/ci.yml`) runs on push/PR to `main`, `develop`, and `cursor/**`:

- Backend: lint, test, build
- Frontend: lint, test, build

## License

MIT
15 changes: 15 additions & 0 deletions backend/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Server
NODE_ENV=development
PORT=3000

# MongoDB
MONGODB_URI=mongodb://localhost:27017/mean_db

# Security
CORS_ORIGIN=http://localhost:4200
RATE_LIMIT_WINDOW_MS=900000
RATE_LIMIT_MAX=100

# JWT (for auth module)
JWT_SECRET=your-super-secret-jwt-key-change-in-production
JWT_EXPIRES_IN=7d
9 changes: 9 additions & 0 deletions backend/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"extends": ["../.eslintrc.json"],
"parserOptions": {
"project": "./tsconfig.json"
},
"rules": {
"@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }]
}
}
32 changes: 32 additions & 0 deletions backend/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# ----- Build stage -----
FROM node:20-alpine AS builder

WORKDIR /app

COPY package*.json ./
RUN npm ci --only=production=false
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The flag --only=production=false is not a valid or necessary flag for npm ci. By default, npm ci installs all dependencies listed in package-lock.json (dependencies and devDependencies), which is the desired behavior for a build stage. You can safely remove this flag.

RUN npm ci


COPY tsconfig.json ./
COPY src ./src

RUN npm run build

# ----- Production stage -----
FROM node:20-alpine AS production

RUN addgroup -g 1001 -S nodejs && adduser -S nodejs -u 1001

WORKDIR /app

COPY --from=builder /app/dist ./dist
COPY --from=builder /app/package*.json ./

RUN npm ci --only=production && npm cache clean --force
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

To fix the healthcheck defined in docker-compose.yml, you need to install a utility like wget in this production image, as it's not included in node:20-alpine by default.

RUN npm ci --only=production && npm cache clean --force
RUN apk add --no-cache wget


USER nodejs

EXPOSE 3000

ENV NODE_ENV=production

CMD ["node", "dist/server.js"]
Loading