Skip to content
Merged
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
38 changes: 17 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,42 +5,38 @@
[![code Style: Prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat&logo=prettier)](https://prettier.io/)
[![ESLint](https://img.shields.io/badge/linting-eslint-blue.svg?style=flat&logo=eslint)](https://eslint.org/)
[![TypeScript](https://img.shields.io/badge/language-typescript-blue.svg?style=flat&logo=typescript)](https://www.typescriptlang.org/)
[![Swagger UI](https://img.shields.io/badge/docs-Swagger_UI-blue?logo=swagger)](http://localhost:3000/api-docs)

## Configuration

This backend API is configured using environment variables. You can set these variables in a `.env` file in the root of the project (copy `.env.sample` to `.env` and modify the values). **Important: Ensure your `.env` file is not committed to your version control system.**
You can define your environmental variables in a `.env` file at the root of the project. (Start by copying `.env.sample` → `.env`).\
**⚠️ Important:** Never commit your `.env` file to version control.

The following environment variables are used:

- `MONGO_URL`: **Required.** The connection string for your MongoDB database. You can obtain this from your MongoDB provider (e.g., MongoDB Atlas). Example: `mongodb+srv://<username>:<password>@<cluster-url>/<database-name>?retryWrites=true&w=majority`
- `PORT`: **Optional.** The port number that the Express server will listen on. If not specified, it defaults to `3000`. Example: `8080`
- `ALLOWED_ORIGINS`: **Optional.** A comma-separated list of allowed origins for Cross-Origin Resource Sharing (CORS). This controls which websites can make requests to your API. Example: `http://localhost:3000,https://your-frontend.com`
- `ALLOWED_METHODS`: **Optional.** A comma-separated list of allowed HTTP methods for CORS. Example: `GET,POST,PUT,DELETE`
- `ALLOWED_HEADERS`: **Optional.** A comma-separated list of allowed request headers for CORS. Example: `Content-Type,Authorization`
- `NODE_ENV`: Specifies the application environment.
- `test`: When set to `test`, the application will use an in-memory MongoDB server provided by `mongodb-memory-server` for testing purposes. In this mode, the `MONGO_URL` variable is not required.
- `development`: Typically used during development. You might have specific development configurations.
- `production`: Used in the production environment.
- `JWT_SECRET`: **Required and Critically Important.** A secret key used to sign and verify JSON Web Tokens (JWTs) for authentication. **This should be a long, random, and secure string. Do not share or expose this secret.**
```bash
node -e "console.log(require('crypto').randomBytes(64).toString('hex'))"
```
| Variable | Required | Description | Example |
| ----------------- | ---------- | ---------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
| `MONGO_URL` | ✅ Yes | Connection string for MongoDB. Obtain this from your MongoDB provider. | `mongodb+srv://<username>:<password>@<cluster-url>/<database-name>?retryWrites=true&w=majority` |
| `PORT` | ❌ No | Port for the Express server to listen on. Defaults to `3000`. | `8080` |
| `ALLOWED_ORIGINS` | ❌ No | Comma-separated list of allowed origins for CORS. | `http://localhost:3000,https://your-frontend.com` |
| `ALLOWED_METHODS` | ❌ No | Comma-separated list of allowed HTTP methods for CORS. | `GET,POST,PUT,DELETE` |
| `ALLOWED_HEADERS` | ❌ No | Comma-separated list of allowed request headers for CORS. | `Content-Type,Authorization` |
| `NODE_ENV` | ⚠️ Depends | Application environment: `development`, `production`, or `test`. `MONGO_URL` not required in `test`. | `development` |
| `JWT_SECRET` | ✅ Yes | Secret key used for signing/verifying JWTs. Must be secure and private. | `Use: node -e "console.log(require('crypto').randomBytes(64).toString('hex'))"` |

## Getting Started

Copy this file to .env and fill in the actual values
1. Install Dependencies

```bash
cp .env.sample .env
npm run install
```

Install Dependencies
2. Create .env file and gather your variable values.

```bash
npm run install
cp .env.sample .env
```

Run a script:
3. Run script:

```json
"scripts": {
Expand Down
14 changes: 9 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
"version": "1.0.1",
"description": "",
"main": "./src/index.ts",
"type": "module",
"private": true,
"type": "module",
"scripts": {
"prebuild": "rm -rf dist",
"build": "tsc",
Expand All @@ -29,31 +29,35 @@
"author": "",
"license": "ISC",
"dependencies": {
"@types/swagger-jsdoc": "^6.0.4",
"bcrypt": "^6.0.0",
"compression": "^1.8.0",
"cors": "^2.8.5",
"dotenv": "^16.5.0",
"env-cmd": "^10.1.0",
"express": "^4.16.4",
"express": "^4.21.2",
"express-rate-limit": "^7.5.0",
"helmet": "^3.15.1",
"jsonwebtoken": "^9.0.2",
"mongodb": "^6.16.0",
"morgan": "^1.9.1",
"nodemon": "^3.1.10"
"nodemon": "^3.1.10",
"swagger-jsdoc": "^6.2.8",
"swagger-ui-express": "^5.0.1"
},
"devDependencies": {
"@eslint/js": "^9.27.0",
"@types/bcrypt": "^5.0.2",
"@types/compression": "^1.7.5",
"@types/cors": "^2.8.18",
"@types/express": "^5.0.1",
"@types/express": "^5.0.2",
"@types/helmet": "^0.0.48",
"@types/jest": "^29.5.14",
"@types/jsonwebtoken": "^9.0.9",
"@types/morgan": "^1.9.9",
"@types/node": "^22.15.17",
"@types/node": "^22.15.18",
"@types/supertest": "^6.0.3",
"@types/swagger-ui-express": "^4.1.8",
"@typescript-eslint/eslint-plugin": "^8.32.1",
"@typescript-eslint/parser": "^8.32.1",
"eslint": "^9.27.0",
Expand Down
3 changes: 3 additions & 0 deletions src/app.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import express, { Application } from 'express';
import swaggerUi from 'swagger-ui-express';

import swaggerSpec from './documentation/swaggerOptions';
import compression from './midleware/compression';
import cors from './midleware/cors';
import errorHandler from './midleware/errorHandler';
Expand Down Expand Up @@ -32,6 +34,7 @@ app.use('/', rootRouter);
app.use('/health', healthRouter);
app.use('/stores', storesRouter);
app.use('/auth', authRouter);
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec));
app.use('*', notFoundRouter);

export default app;
19 changes: 19 additions & 0 deletions src/documentation/swaggerOptions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import swaggerJsdoc, { Options } from 'swagger-jsdoc';

import { name } from '../../package.json';

const options: Options = {
apis: ['./src/**/*.ts'], // recursive, includes subfolders like ./src/routes
definition: {
info: {
description: 'A sample API documentation',
title: name,
version: '1.0.0',
},
openapi: '3.0.0',
},
};

const swaggerSpec = swaggerJsdoc(options);

export default swaggerSpec;
2 changes: 0 additions & 2 deletions src/midleware/authMiddleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ if (!JWT_SECRET) {
throw new Error('JWT_SECRET is not defined');
}

// Extend the Request interface to include the user payload from the token

const authMiddleware = (req: Request, res: Response, next: NextFunction): void => {
const authHeader = req.headers.authorization;
const token = authHeader?.split(' ')[1];
Expand Down
29 changes: 28 additions & 1 deletion src/routes/healthRoute.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,34 @@
import { Request, Response, Router } from 'express';

const router: Router = Router();

/**
* @swagger
* /health:
* get:
* tags:
* - Health
* summary: Health check
* description: Returns service status, current timestamp, and uptime.
* responses:
* 200:
* description: Service is running
* content:
* application/json:
* schema:
* type: object
* properties:
* status:
* type: string
* example: OK
* timestamp:
* type: string
* format: date-time
* example: "2025-05-17T12:34:56.789Z"
* uptime:
* type: number
* description: Time in seconds since the server started
* example: 123.456
*/
router.get('/', (req: Request, res: Response): void => {
res.status(200).json({
status: 'OK',
Expand Down