From e26c4419a13881ebe6a6d02c3c909541bc1114c7 Mon Sep 17 00:00:00 2001 From: pakeku Date: Sat, 17 May 2025 04:25:27 -0400 Subject: [PATCH 1/4] feat: add swagger documentation --- README.md | 1 + package.json | 13 ++++++++----- src/app.ts | 3 +++ src/documentation/swaggerOptions.ts | 18 ++++++++++++++++++ src/midleware/authMiddleware.ts | 2 -- src/routes/healthRoute.ts | 29 ++++++++++++++++++++++++++++- 6 files changed, 58 insertions(+), 8 deletions(-) create mode 100644 src/documentation/swaggerOptions.ts diff --git a/README.md b/README.md index 9adec7f..c434b21 100755 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ [![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 diff --git a/package.json b/package.json index d325ae9..a8806b5 100755 --- a/package.json +++ b/package.json @@ -3,7 +3,6 @@ "version": "1.0.1", "description": "", "main": "./src/index.ts", - "type": "module", "private": true, "scripts": { "prebuild": "rm -rf dist", @@ -29,31 +28,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", diff --git a/src/app.ts b/src/app.ts index 3ac052c..7925e2c 100644 --- a/src/app.ts +++ b/src/app.ts @@ -12,6 +12,8 @@ import healthRouter from './routes/healthRoute'; import notFoundRouter from './routes/notFoundRoute'; import rootRouter from './routes/rootRoute'; import storesRouter from './routes/storesRoute'; +import swaggerSpec from './documentation/swaggerOptions'; +import swaggerUi from 'swagger-ui-express'; const app: Application = express(); @@ -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; diff --git a/src/documentation/swaggerOptions.ts b/src/documentation/swaggerOptions.ts new file mode 100644 index 0000000..583a26a --- /dev/null +++ b/src/documentation/swaggerOptions.ts @@ -0,0 +1,18 @@ +import swaggerJsdoc, { Options } from 'swagger-jsdoc'; +import { name } from '../../package.json'; + +const options: Options = { + definition: { + openapi: '3.0.0', + info: { + title: name, + version: '1.0.0', + description: 'A sample API documentation', + }, + }, + apis: ['./src/**/*.ts'], // recursive, includes subfolders like ./src/routes +}; + +const swaggerSpec = swaggerJsdoc(options); + +export default swaggerSpec; diff --git a/src/midleware/authMiddleware.ts b/src/midleware/authMiddleware.ts index 99386b4..4bd2016 100644 --- a/src/midleware/authMiddleware.ts +++ b/src/midleware/authMiddleware.ts @@ -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]; diff --git a/src/routes/healthRoute.ts b/src/routes/healthRoute.ts index ea2b486..b2bee86 100644 --- a/src/routes/healthRoute.ts +++ b/src/routes/healthRoute.ts @@ -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', From ab14e0ca755db7e509b9df74a0e94aa640c1df6b Mon Sep 17 00:00:00 2001 From: pakeku Date: Sat, 17 May 2025 04:30:36 -0400 Subject: [PATCH 2/4] update documentation, orgnize --- README.md | 44 +++++++++++++++++--------------------------- 1 file changed, 17 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index c434b21..3ec250e 100755 --- a/README.md +++ b/README.md @@ -8,41 +8,31 @@ [![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.** - -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://:@/?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'))" - ``` +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. + +| Variable | Required | Description | Example | +| --- | --- | --- | --- | +| `MONGO_URL` | ✅ Yes | Connection string for MongoDB. Obtain this from your MongoDB provider. | `mongodb+srv://:@/?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": { "prebuild":"rm -rf dist", From 31c88493e04282fd53676526ba488cbacc5f48c9 Mon Sep 17 00:00:00 2001 From: pakeku Date: Sat, 17 May 2025 04:39:07 -0400 Subject: [PATCH 3/4] format --- README.md | 25 +++++++++++++++---------- src/app.ts | 4 ++-- src/documentation/swaggerOptions.ts | 7 ++++--- 3 files changed, 21 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 3ec250e..7ee8bcb 100755 --- a/README.md +++ b/README.md @@ -8,31 +8,36 @@ [![Swagger UI](https://img.shields.io/badge/docs-Swagger_UI-blue?logo=swagger)](http://localhost:3000/api-docs) ## Configuration + 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. -| Variable | Required | Description | Example | -| --- | --- | --- | --- | -| `MONGO_URL` | ✅ Yes | Connection string for MongoDB. Obtain this from your MongoDB provider. | `mongodb+srv://:@/?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'))"` | +| Variable | Required | Description | Example | +| ----------------- | ---------- | ---------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | +| `MONGO_URL` | ✅ Yes | Connection string for MongoDB. Obtain this from your MongoDB provider. | `mongodb+srv://:@/?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 -1. Install Dependencies + +1. Install Dependencies + ```bash npm run install ``` 2. Create .env file and gather your variable values. + ```bash cp .env.sample .env ``` 3. Run script: + ```json "scripts": { "prebuild":"rm -rf dist", diff --git a/src/app.ts b/src/app.ts index 7925e2c..cd55ad3 100644 --- a/src/app.ts +++ b/src/app.ts @@ -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'; @@ -12,8 +14,6 @@ import healthRouter from './routes/healthRoute'; import notFoundRouter from './routes/notFoundRoute'; import rootRouter from './routes/rootRoute'; import storesRouter from './routes/storesRoute'; -import swaggerSpec from './documentation/swaggerOptions'; -import swaggerUi from 'swagger-ui-express'; const app: Application = express(); diff --git a/src/documentation/swaggerOptions.ts b/src/documentation/swaggerOptions.ts index 583a26a..5c62a57 100644 --- a/src/documentation/swaggerOptions.ts +++ b/src/documentation/swaggerOptions.ts @@ -1,16 +1,17 @@ import swaggerJsdoc, { Options } from 'swagger-jsdoc'; + import { name } from '../../package.json'; const options: Options = { + apis: ['./src/**/*.ts'], // recursive, includes subfolders like ./src/routes definition: { - openapi: '3.0.0', info: { + description: 'A sample API documentation', title: name, version: '1.0.0', - description: 'A sample API documentation', }, + openapi: '3.0.0', }, - apis: ['./src/**/*.ts'], // recursive, includes subfolders like ./src/routes }; const swaggerSpec = swaggerJsdoc(options); From 905023f84b0a3219192a77df2fd287520e1ee154 Mon Sep 17 00:00:00 2001 From: pakeku Date: Sat, 17 May 2025 11:34:46 -0400 Subject: [PATCH 4/4] add type:module --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index a8806b5..80f4610 100755 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "description": "", "main": "./src/index.ts", "private": true, + "type": "module", "scripts": { "prebuild": "rm -rf dist", "build": "tsc",