Skip to content
Open
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
230 changes: 205 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,32 +1,212 @@
# Backend Boilerplate Express Backend
# Backend Boilerplate Express

Express Backend Application for company wide use in Algoirthm.io.
Tech Stack:
A production-ready Express backend boilerplate for company-wide use at Algorithm.io.

- Node.js `(18.x or above)`
- Express `(5.x)`
- PostgreSQL `(16.x or above)`
- Sequelize `(6.x)`
## Tech Stack

## Setup
- **Node.js** `(18.x or above)`
- **Express** `(5.x)`
- **PostgreSQL** `(16.x or above)`
- **Sequelize ORM** `(6.x)`
- **JWT** for authentication
- **Bcrypt** for password hashing
- **Joi** for request validation
- **KSUID** for unique identifiers
- **Lodash** for utility functions
- **JSON Repair** for JSON parsing and validation

1. Install dependencies: `npm install`
2. Configure environment variables from .env.example
3. Run migrations: `npm run migrate`
4. Start server: `npm start`
## Features

- ✅ JWT-based authentication
- ✅ Centralized error handling
- ✅ Request validation with Joi schemas
- ✅ Database migrations with Sequelize
- ✅ Structured logging and responses
- ✅ CORS enabled
- ✅ Health check endpoint
- ✅ Clean architecture with separation of concerns

## Getting Started

### Prerequisites

- Node.js 18.x or above
- PostgreSQL 16.x or above
- npm or yarn

### Installation

1. **Install dependencies:**
```bash
npm install
```

2. **Configure environment variables:**
Create a `.env` file in the root directory (use `.env.example` as a template)

3. **Run database migrations:**
```bash
npm run migrate
```

4. **Start the server:**
- Development: `npm run dev`
- Production: `npm start`

## Project Structure

- `/logs` - Application logs
- `/src`
- `/config` - Configuration files
- `/controllers` - Request handlers
- `/database` - Database-related files
- `/enums` - Enumeration definitions
- `/errors` - Custom error definitions
- `/middleware` - Express middleware
- `/models` - Database models
- `/routes` - API route definitions
- `/services` - Business logic
- `/utils` - Utility functions
- `/validations` - Request validation schemas
```
backend-boilerplate-express/
├── app.js # Application entry point
├── package.json # Project dependencies and scripts
├── src/
│ ├── config/ # Configuration files (database, JWT, etc.)
│ │ ├── database.config.js
│ │ ├── jwt.config.js
│ │ └── index.js
│ ├── controllers/ # Request handlers and business logic orchestration
│ │ └── auth.controller.js
│ ├── database/ # Database-related files
│ │ ├── migrations/ # Sequelize migrations
│ │ └── seeders/ # Database seeders
│ ├── enums/ # Application-wide enumerations and constants
│ │ └── index.js
│ ├── middleware/ # Express middleware functions
│ │ ├── authenticate-account.middleware.js
│ │ └── error-handling.middleware.js
│ ├── models/ # Sequelize models
│ │ ├── index.js
│ │ └── user.model.js
│ ├── routes/ # API route definitions
│ │ ├── index.js
│ │ └── auth.route.js
│ ├── schemas/ # Joi validation schemas
│ │ └── auth.schema.js
│ ├── services/ # Business logic layer
│ │ └── user.service.js
│ └── utils/ # Utility functions and helpers
│ ├── case.util.js
│ ├── hash-password.util.js
│ ├── isValidKsuid.util.js
│ ├── json.util.js
│ ├── jwt.util.js
│ ├── pagination-metadata.util.js
│ ├── response.util.js
│ ├── sanitizeData.util.js
│ ├── sequelize-filter.util.js
│ ├── validation.util.js
│ └── errors/
│ └── error-classes.js
```

## Architecture

This boilerplate follows a **layered architecture** pattern:

1. **Routes Layer** (`/routes`) - Defines API endpoints and maps them to controllers
2. **Controller Layer** (`/controllers`) - Handles HTTP requests/responses and orchestrates services
3. **Service Layer** (`/services`) - Contains business logic and data access
4. **Model Layer** (`/models`) - Defines database schemas and relationships
5. **Middleware Layer** (`/middleware`) - Authentication, validation, error handling
6. **Utils Layer** (`/utils`) - Reusable helper functions

## Available Scripts

### Server
- `npm start` - Start the production server
- `npm run dev` - Start development server with hot reload (nodemon)

### Database Migrations
- `npm run migrate` - Run all pending database migrations
- `npm run migrate:create -- <migration-name>` - Create a new migration file
- `npm run migrate:undo -- <migration-name>` - Undo a specific migration by name
- `npm run migrate:undo:last` - Undo the last migration
- `npm run migrate:undo:all` - Undo all migrations

### Database Seeders
- `npm run seed` - Run all seeders
- `npm run seed:create -- <seeder-name>` - Create a new seeder file
- `npm run seed:run -- <seeder-name>` - Run a specific seeder
- `npm run seed:undo -- <seeder-name>` - Undo a specific seeder
- `npm run seed:undo:last` - Undo the last seeder
- `npm run seed:undo:all` - Undo all seeders

### Utilities
- `npm run setup` - Run all migrations and seeders (useful for initial setup)
- `npm run reset` - Reset database completely (undo all migrations, run migrations, run seeders)

## API Endpoints

### Health Check
- `GET /health` - Returns server health status

### Authentication
- `POST /api/auth/login` - User login

## Environment Variables

Create a `.env` file with the following variables:

```env
# Server
PORT=5000
NODE_ENV=development

# Database
DB_HOST=localhost
DB_PORT=5432
DB_NAME=your_database
DB_USER=your_username
DB_PASSWORD=your_password

# JWT
JWT_SECRET=your_jwt_secret
JWT_EXPIRES_IN=24h
JWT_REFRESH_SECRET=your_refresh_secret
JWT_REFRESH_EXPIRES_IN=7d
```

## Error Handling

The application uses centralized error handling with custom error classes:

- `ValidationError` - For validation failures
- `NotFoundError` - For resource not found
- `UnauthorizedError` - For authentication failures
- `ForbiddenError` - For authorization failures
- `ConflictError` - For data conflicts
- `InternalServerError` - For server errors

## Response Format

All API responses follow a consistent format:

**Success Response:**
```json
{
"success": true,
"data": { ... },
"metadata": { ... }
}
```

**Error Response:**
```json
{
"success": false,
"error": {
"code": "ERROR_CODE",
"message": "Error message"
}
}
```

## Contributing

1. Create a feature branch from `main`
2. Make your changes
3. Submit a pull request

## License

ISC
65 changes: 60 additions & 5 deletions app.js
Original file line number Diff line number Diff line change
@@ -1,26 +1,81 @@
require("dotenv").config();

const express = require("express");
const config = require("./src/config");
const errorHandlingMiddleware = require("./src/middleware/error-handling.middleware");
const { response } = require("./src/utils/response.util");
const cors = require("cors");
const router = require("./src/routes");

const app = express();

app.use(express.json());

app.use(express.urlencoded({ extended: true }));

app.use(
cors({
origin: "*",
methods: "*",
allowedHeaders: "*",
methods: ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"],
allowedHeaders: ["Content-Type", "Authorization"],
})
);

app.use("/api", router);

app.get("/health", (req, res) => {
return response(res).success({
data: {
status: "healthy",
timestamp: new Date().toISOString(),
environment: process.env.NODE_ENV || "development",
},
});
});

app.use(errorHandlingMiddleware);

const PORT = config.server.port || 5000;

app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});
// Start server
const startServer = () => {
try {
const server = app.listen(PORT, "0.0.0.0", () => {
console.log(`Server is running on port ${PORT}`);
});

// Handle server errors
server.on("error", (error) => {
console.error("Server error:", error);
process.exit(1);
});

// Handle unhandled promise rejections
process.on("unhandledRejection", (reason, promise) => {
console.error("Unhandled Rejection at:", promise, "reason:", reason);
});

// Handle uncaught exceptions
process.on("uncaughtException", (error) => {
console.error("Uncaught Exception:", error);
process.exit(1);
});

const shutdown = () => {
console.log("Shutdown signal received. Closing server...");
server.close(() => {
console.log("Server closed. Exiting.");
process.exit(0);
});
};

// Graceful shutdown
process.on("SIGTERM", shutdown);
process.on("SIGINT", shutdown);
} catch (error) {
console.error("Failed to start server:", error);
process.exit(1);
}
};

startServer();
Loading