├── .dockerignore ├── .env.example ├── .github ├── dependabot.yml └── workflows │ └── build.yml ├── .gitignore ├── .husky └── pre-commit ├── .lintstagedrc.json ├── .nvmrc ├── .prettierrc ├── .swcrc ├── .vscode └── settings.json ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── docker-compose.yml ├── eslint.config.mjs ├── package.json ├── public └── favicon.ico ├── script ├── filelist.ts └── index.ts ├── src ├── api │ ├── auth │ │ ├── auth.controller.ts │ │ ├── auth.route.ts │ │ ├── auth.service.ts │ │ └── auth.validation.ts │ └── user │ │ ├── user.controller.ts │ │ ├── user.route.ts │ │ └── user.validation.ts ├── app.ts ├── config │ ├── envs.ts │ ├── index.ts │ ├── logger.ts │ └── swaggerConfig.ts ├── database │ └── index.ts ├── middlewares │ └── common │ │ ├── NotFound.middlewares.ts │ │ ├── globalErrorHandler.middlewares.ts │ │ └── index.ts ├── server.ts └── test │ └── app.test.ts ├── tsconfig.json └── vitest.config.ts /.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | PORT= -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "npm" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "weekly" 12 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [main, develop] 6 | pull_request: 7 | branches: [main, develop] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | 13 | strategy: 14 | matrix: 15 | node-version: [20.x] 16 | 17 | steps: 18 | - uses: actions/checkout@v4 19 | 20 | - name: Use Node.js ${{ matrix.node-version }} 21 | uses: actions/setup-node@v4 22 | with: 23 | node-version: ${{ matrix.node-version }} 24 | 25 | - name: cache Dependencies 26 | uses: actions/cache@v4 27 | with: 28 | path: ~/.npm 29 | key: ${{ runner.os }}-node-${{ hashFiles('**/package.json') }} 30 | - name: Install Dependencies 31 | run: npm install 32 | 33 | - name: Lint 34 | run: npm run lint 35 | 36 | - name: Run tests 37 | run: npm run test 38 | 39 | - name: Build 40 | run: npm run build 41 | 42 | # Optional: Cache the build output 43 | - name: Cache build output 44 | uses: actions/cache@v4 45 | with: 46 | path: dist 47 | key: ${{ runner.os }}-build-${{ github.sha }} 48 | restore-keys: | 49 | ${{ runner.os }}-build- 50 | 51 | # Optional: Add test coverage reporting 52 | - name: Upload test coverage 53 | if: matrix.node-version == '20.x' 54 | uses: actions/upload-artifact@v4 55 | with: 56 | name: coverage 57 | path: coverage/ 58 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore 2 | 3 | # Logs 4 | 5 | logs 6 | _.log 7 | npm-debug.log_ 8 | yarn-debug.log* 9 | yarn-error.log* 10 | lerna-debug.log* 11 | .pnpm-debug.log* 12 | 13 | # Caches 14 | 15 | .cache 16 | 17 | # Diagnostic reports (https://nodejs.org/api/report.html) 18 | 19 | report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json 20 | 21 | # Runtime data 22 | 23 | pids 24 | _.pid 25 | _.seed 26 | *.pid.lock 27 | 28 | # Directory for instrumented libs generated by jscoverage/JSCover 29 | 30 | lib-cov 31 | 32 | # Coverage directory used by tools like istanbul 33 | 34 | coverage 35 | *.lcov 36 | 37 | # nyc test coverage 38 | 39 | .nyc_output 40 | 41 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 42 | 43 | .grunt 44 | 45 | # Bower dependency directory (https://bower.io/) 46 | 47 | bower_components 48 | 49 | # node-waf configuration 50 | 51 | .lock-wscript 52 | 53 | # Compiled binary addons (https://nodejs.org/api/addons.html) 54 | 55 | build/Release 56 | 57 | # Dependency directories 58 | 59 | node_modules/ 60 | jspm_packages/ 61 | 62 | # Snowpack dependency directory (https://snowpack.dev/) 63 | 64 | web_modules/ 65 | 66 | # TypeScript cache 67 | 68 | *.tsbuildinfo 69 | 70 | # Optional npm cache directory 71 | 72 | .npm 73 | 74 | # Optional eslint cache 75 | 76 | .eslintcache 77 | 78 | # Optional stylelint cache 79 | 80 | .stylelintcache 81 | 82 | # Microbundle cache 83 | 84 | .rpt2_cache/ 85 | .rts2_cache_cjs/ 86 | .rts2_cache_es/ 87 | .rts2_cache_umd/ 88 | 89 | # Optional REPL history 90 | 91 | .node_repl_history 92 | 93 | # Output of 'npm pack' 94 | 95 | *.tgz 96 | 97 | # Yarn Integrity file 98 | 99 | .yarn-integrity 100 | 101 | # dotenv environment variable files 102 | 103 | .env 104 | .env.development.local 105 | .env.test.local 106 | .env.production.local 107 | .env.local 108 | 109 | # parcel-bundler cache (https://parceljs.org/) 110 | 111 | .parcel-cache 112 | 113 | # Next.js build output 114 | 115 | .next 116 | out 117 | 118 | # Nuxt.js build / generate output 119 | 120 | .nuxt 121 | dist 122 | 123 | # Gatsby files 124 | 125 | # Comment in the public line in if your project uses Gatsby and not Next.js 126 | 127 | # https://nextjs.org/blog/next-9-1#public-directory-support 128 | 129 | # public 130 | 131 | # vuepress build output 132 | 133 | .vuepress/dist 134 | 135 | # vuepress v2.x temp and cache directory 136 | 137 | .temp 138 | 139 | # Docusaurus cache and generated files 140 | 141 | .docusaurus 142 | 143 | # Serverless directories 144 | 145 | .serverless/ 146 | 147 | # FuseBox cache 148 | 149 | .fusebox/ 150 | 151 | # DynamoDB Local files 152 | 153 | .dynamodb/ 154 | 155 | # TernJS port file 156 | 157 | .tern-port 158 | 159 | # Stores VSCode versions used for testing VSCode extensions 160 | 161 | .vscode-test 162 | 163 | # yarn v2 164 | 165 | .yarn/cache 166 | yarn.lock/* 167 | .yarn/unplugged 168 | .yarn/build-state.yml 169 | .yarn/install-state.gz 170 | .pnp.* 171 | bun.* 172 | 173 | # IntelliJ based IDEs 174 | .idea 175 | 176 | # Finder (MacOS) folder config 177 | .DS_Store 178 | 179 | 180 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | npm run lint-staged -------------------------------------------------------------------------------- /.lintstagedrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "*.{js,jsx,ts,tsx}": [ 3 | "prettier --write 'src/**/*.{ts,js,json,md}'", 4 | "eslint 'src/**/*.{ts,js}" 5 | ], 6 | "*.{json,md,yml}": ["prettier --write"] 7 | } 8 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 22.14.0 2 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "singleQuote": false, 4 | "trailingComma": "all", 5 | "tabWidth": 2, 6 | "printWidth": 80, 7 | "plugins": [ 8 | "@ianvs/prettier-plugin-sort-imports" 9 | ] 10 | } -------------------------------------------------------------------------------- /.swcrc: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://swc.rs/schema.json", 3 | "jsc": { 4 | "parser": { 5 | "syntax": "typescript", 6 | "tsx": false, 7 | "decorators": false, 8 | "dynamicImport": true 9 | }, 10 | "target": "es2016", 11 | "loose": false, 12 | "minify": { 13 | "compress": true, 14 | "mangle": true 15 | }, 16 | "transform": { 17 | "legacyDecorator": false, 18 | "decoratorMetadata": false 19 | }, 20 | // type alias 21 | "baseUrl": "./src", 22 | "paths": { 23 | "@/*": ["./*"] 24 | } 25 | }, 26 | "module": { 27 | "type": "commonjs", 28 | "strict": true, 29 | "strictMode": true, 30 | "lazy": false, 31 | "noInterop": false 32 | }, 33 | "sourceMaps": false, 34 | "exclude": ["node_modules", "dist", "./**/*.js"] 35 | } 36 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": true, 3 | "[javascript]": { 4 | "editor.formatOnSave": true 5 | }, 6 | "explorer.fileNesting.enabled": true, 7 | "explorer.fileNesting.patterns": { 8 | "tsconfig.json": "tsconfig.*.json, global.d.ts", 9 | "vite.config.*": "jsconfig*, vite.config.*, cypress.config.*, playwright.config.*", 10 | "Dockerfile": "docker-compose.*, .dockerignore", 11 | "package.json": "package-lock.json, pnpm*, .yarnrc*, yarn*, .eslint*, eslint*, .prettier*, prettier*, .editorconfig, Makefile, bun.lockb, bun.lock", 12 | "README.md": "LICENSE", 13 | ".swcrc": "vitest.config.*, jest.config.*, .lintstagedrc.*" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Dockerfile 2 | 3 | # Use an official Node.js runtime as a parent image 4 | FROM node:20-alpine 5 | 6 | # Set the working directory inside the container 7 | WORKDIR /usr/src/app 8 | 9 | # Copy the package.json and package-lock.json files 10 | COPY package*.json ./ 11 | 12 | # Install the project dependencies 13 | RUN npm install 14 | 15 | # Copy the rest of the project files 16 | COPY . . 17 | 18 | # Build the TypeScript code 19 | RUN npm run build 20 | 21 | # Expose the port the app runs on 22 | EXPOSE 3000 23 | 24 | # Start the app 25 | CMD ["npm", "start"] 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) [year] [fullname] 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Makefile 2 | 3 | # Variables 4 | NODE_ENV=production 5 | 6 | .PHONY: start dev build lint format test docker-build docker-up docker-down prepare lint-staged cli 7 | 8 | # Targets 9 | start: 10 | $(NODE_ENV)=production node dist/app.js 11 | 12 | dev: 13 | tsx watch ./src/app.ts --w 14 | 15 | build: 16 | rimraf dist && tsc --noEmit && npm run lint && swc src -d dist --out-dir dist --source-maps --strip-leading-paths 17 | 18 | lint: 19 | eslint 'src/**/*.{ts,js}' 20 | 21 | format: 22 | prettier --write 'src/**/*.{ts,js,json,md}' 23 | 24 | test: 25 | jest 26 | 27 | docker-build: 28 | docker-compose build 29 | 30 | docker-up: 31 | docker-compose up 32 | 33 | docker-down: 34 | docker-compose down 35 | 36 | prepare: 37 | husky 38 | 39 | lint-staged: 40 | lint-staged --concurrent false 41 | 42 | cli: 43 | tsx ./script/index.ts 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # express-ts-starter 2 | 3 | ### Features 4 | 5 | - Basic Express Server 6 | - import aliases like ` @/controller` instead of `../../controller` 7 | - Linting and Formatting, pre-commit 8 | - Standard Fold Structure 9 | - Custom import aliases 10 | - TypeScript Support and Config 11 | - Basic CLI for crating new module 12 | 13 | ### How to use CLI 14 | 15 | ```bash 16 | npm run cli 17 | ``` 18 | 19 | or 20 | 21 | ```bash 22 | make cli 23 | ``` 24 | 25 | - It will ask you module name 26 | - Based on that will create controller, route and validation files inside API folder 27 | 28 | ### Recommended Folder Structure: 29 | 30 | ```bash 31 | 32 | project-root/ 33 | │ 34 | ├── src/ 35 | │ ├── api/ # Group controllers, routes, and validation by feature 36 | │ │ ├── user/ 37 | │ │ │ ├── user.controller.ts # User controller 38 | │ │ │ ├── user.route.ts # User routes 39 | │ │ │ ├── user.validation.ts # User input validation (optional) 40 | │ │ │ └── user.service.ts # User-specific services 41 | │ │ ├── auth/ 42 | │ │ │ ├── auth.controller.ts # auth controller 43 | │ │ │ ├── auth.route.ts # auth routes 44 | │ │ │ ├── auth.validation.ts # auth input validation (optional) 45 | │ │ │ └── auth.service.ts # auth-specific services 46 | │ ├── database/ 47 | │ │ ├── Redis.database.js 48 | │ │ ├── Mongo.database.js 49 | │ │ └── auth/ 50 | │ │ ├── auth.controller.ts # Auth controller 51 | │ │ ├── auth.route.ts # Auth routes 52 | │ │ ├── auth.service.ts # Auth service 53 | │ │ └── auth.validation.ts # Auth validation (optional) 54 | │ │ 55 | │ ├── config/ # App configuration (environment, database, etc.) 56 | │ │ ├── database.ts # Database connection 57 | │ │ ├── env.ts # Environment variable configuration 58 | │ │ └── logger.ts # Logger configuration 59 | │ │ 60 | │ ├── middlewares/ # Custom middleware (authentication, error handling) 61 | │ │ ├── error.middleware.ts # Centralized error handling 62 | │ │ ├── auth.middleware.ts # Auth middleware for protected routes 63 | │ │ └── validate.middleware.ts # Validation middleware for request schemas 64 | │ │ 65 | │ ├── models/ # Mongoose/Sequelize models or DB schemas 66 | │ │ ├── user.model.ts # User model (Mongoose, Sequelize, etc.) 67 | │ │ └── auth.model.ts # Auth-related model (tokens, sessions, etc.) 68 | │ │ 69 | │ ├── services/ # Business logic and reusable services 70 | │ │ ├── email.service.t # Email service (send emails) 71 | │ │ ├── auth.service.ts # Authentication and authorization service 72 | │ │ └── user.service.ts # User-related services (CRUD operations) 73 | │ │ 74 | │ ├── utils/ # Helper functions/utilities (non-business logic) 75 | │ │ ├── httpResponse.ts # Standardized response format 76 | │ │ ├── constants.ts # App constants 77 | │ │ └── hash.ts # Password hashing utility 78 | │ │ 79 | │ ├── validations/ # Centralized validation schemas (using Zod, Joi, etc.) 80 | │ │ ├── user.validation.ts # User-related validation 81 | │ │ └── auth.validation.ts # Auth validation 82 | │ │ 83 | │ ├── app.ts # Initialize Express app 84 | │ └── index.ts # Main entry point to start the server 85 | │ 86 | ├── dist/ # Compiled JavaScript files (from TypeScript) 87 | │ 88 | ├── node_modules/ # Dependencies 89 | │ 90 | ├── .env # Environment variables 91 | ├── .eslintignore # ESLint ignore patterns 92 | ├── .eslintrc.json # ESLint configuration 93 | ├── .gitignore # Ignore node_modules and dist 94 | ├── package.json # Project dependencies and scripts 95 | ├── tsconfig.json # TypeScript configuration 96 | └── README.md 97 | 98 | 99 | ``` 100 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | 3 | services: 4 | app: 5 | build: 6 | context: . 7 | dockerfile: Dockerfile 8 | ports: 9 | - '3000:3000' 10 | volumes: 11 | - .:/usr/src/app 12 | - /usr/src/app/node_modules 13 | environment: 14 | NODE_ENV: development 15 | command: npm run dev 16 | -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import pluginJs from "@eslint/js"; 2 | import globals from "globals"; 3 | import tseslint from "typescript-eslint"; 4 | 5 | export default tseslint.config( 6 | pluginJs.configs.recommended, 7 | ...tseslint.configs.recommended, 8 | 9 | { 10 | languageOptions: { globals: globals.browser }, 11 | rules: { 12 | "@typescript-eslint/explicit-member-accessibility": "error", 13 | "@typescript-eslint/no-empty-object-type": "off", 14 | "@typescript-eslint/no-explicit-any": "warn", 15 | "@typescript-eslint/no-unused-vars": "off", 16 | "@typescript-eslint/no-namespace": "off", 17 | "no-undef": "off", 18 | }, 19 | }, 20 | ); 21 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/package", 3 | "name": "express-ts-starter", 4 | "description": "An Express boilerplate backend", 5 | "author": "Md Kawsar Islam Yeasin", 6 | "repository": "yeasin2002/express-ts-starter", 7 | "module": "src/app.ts", 8 | "type": "commonjs", 9 | "license": "MIT", 10 | "scripts": { 11 | "start": "cross-env NODE_ENV=production node dist/app.js", 12 | "dev": "tsx watch --require tsconfig-paths/register ./src/app.ts", 13 | "build": "rimraf /dist && tsc --noEmit && npm run lint && swc src -d dist --out-dir dist strip-leading-paths", 14 | "lint": "eslint ./src", 15 | "format": "prettier --write 'src/**/*.{ts,js,json,md}'", 16 | "test": "vitest", 17 | "docker:build": "docker-compose build", 18 | "docker:up": "docker-compose up", 19 | "docker:down": "docker-compose down", 20 | "prepare": "husky", 21 | "lint-staged": "lint-staged --concurrent false", 22 | "cli": "tsx ./script/index.ts" 23 | }, 24 | "dependencies": { 25 | "@types/swagger-jsdoc": "^6.0.4", 26 | "@types/swagger-ui-express": "^4.1.7", 27 | "compression": "^1.7.5", 28 | "cors": "^2.8.5", 29 | "dotenv": "^16.4.7", 30 | "express": "^5.0.1", 31 | "helmet": "^8.0.0", 32 | "morgan": "^1.10.0", 33 | "supertest": "^7.0.0", 34 | "swagger-jsdoc": "^6.2.8", 35 | "swagger-ui-express": "^5.0.1", 36 | "tsconfig-paths": "^4.2.0" 37 | }, 38 | "devDependencies": { 39 | "@eslint/js": "^9.19.0", 40 | "@ianvs/prettier-plugin-sort-imports": "^4.4.1", 41 | "@swc/cli": "^0.6.0", 42 | "@swc/core": "^1.10.11", 43 | "@types/bun": "latest", 44 | "@types/compression": "^1.7.5", 45 | "@types/cors": "^2.8.17", 46 | "@types/dotenv": "^8.2.3", 47 | "@types/express": "^5.0.0", 48 | "@types/morgan": "^1.9.9", 49 | "@types/node": "^22.12.0", 50 | "@types/supertest": "^6.0.2", 51 | "cross-env": "^7.0.3", 52 | "eslint": "^9.19.0", 53 | "eslint-config-prettier": "^10.0.2", 54 | "eslint-plugin-node": "^11.1.0", 55 | "eslint-plugin-prettier": "^5.2.3", 56 | "globals": "^16.0.0", 57 | "husky": "^9.1.7", 58 | "lint-staged": "^15.4.3", 59 | "prettier": "^3.4.2", 60 | "rimraf": "^6.0.1", 61 | "tsx": "^4.19.2", 62 | "typescript": "^5.7.3", 63 | "typescript-eslint": "^8.22.0", 64 | "vitest": "^3.0.4", 65 | "winston": "^3.17.0" 66 | }, 67 | "_moduleAliases": { 68 | "@": "dist" 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yeasin2002/express-ts-starter/988c6841f678d3d7e8c4011772263e855be4ba6b/public/favicon.ico -------------------------------------------------------------------------------- /script/filelist.ts: -------------------------------------------------------------------------------- 1 | export const fileList = (folderName: string) => { 2 | return [ 3 | { 4 | name: `${folderName}.route.ts`, 5 | content: ` 6 | import express from "express"; 7 | export const router = express.Router(); 8 | 9 | router.post("/create", () => {}); 10 | 11 | 12 | export { router as ${folderName}Router}; 13 | `, 14 | }, 15 | { 16 | name: `${folderName}.controller.ts`, 17 | content: "", 18 | }, 19 | { 20 | name: `${folderName}.validation.ts`, 21 | content: "// zod,joi, valibot, express validator etc. validation ", 22 | }, 23 | ]; 24 | }; 25 | -------------------------------------------------------------------------------- /script/index.ts: -------------------------------------------------------------------------------- 1 | import fs from "fs/promises"; 2 | import readline from "readline/promises"; 3 | import { fileList } from "./filelist"; 4 | 5 | async function createFolderAndFiles(folderName: string) { 6 | const folderPath = `${process.cwd()}/src/api/${folderName}`; 7 | 8 | try { 9 | await fs.access(folderPath); 10 | console.error(`Error: Folder "${folderName}" already exists.`); 11 | return; 12 | } catch (err) { 13 | await fs.mkdir(folderPath, { recursive: true }); 14 | } 15 | 16 | const files = fileList(folderName); 17 | 18 | await Promise.all( 19 | files.map(async (file) => { 20 | const filePath = `${folderPath}/${file.name}`; 21 | try { 22 | await fs.writeFile(filePath, file.content); 23 | } catch (err) { 24 | console.error(`Error creating file ${file}: ${err.message}`); 25 | } 26 | }), 27 | ); 28 | 29 | console.log("Congratulations! Folder and files created successfully."); 30 | } 31 | 32 | (async () => { 33 | const rl = readline.createInterface({ 34 | input: process.stdin, 35 | output: process.stdout, 36 | }); 37 | 38 | const folderName = await rl.question("Enter folder name: "); 39 | await createFolderAndFiles(folderName); 40 | 41 | rl.close(); 42 | })(); 43 | -------------------------------------------------------------------------------- /src/api/auth/auth.controller.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yeasin2002/express-ts-starter/988c6841f678d3d7e8c4011772263e855be4ba6b/src/api/auth/auth.controller.ts -------------------------------------------------------------------------------- /src/api/auth/auth.route.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yeasin2002/express-ts-starter/988c6841f678d3d7e8c4011772263e855be4ba6b/src/api/auth/auth.route.ts -------------------------------------------------------------------------------- /src/api/auth/auth.service.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yeasin2002/express-ts-starter/988c6841f678d3d7e8c4011772263e855be4ba6b/src/api/auth/auth.service.ts -------------------------------------------------------------------------------- /src/api/auth/auth.validation.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yeasin2002/express-ts-starter/988c6841f678d3d7e8c4011772263e855be4ba6b/src/api/auth/auth.validation.ts -------------------------------------------------------------------------------- /src/api/user/user.controller.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yeasin2002/express-ts-starter/988c6841f678d3d7e8c4011772263e855be4ba6b/src/api/user/user.controller.ts -------------------------------------------------------------------------------- /src/api/user/user.route.ts: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | 3 | export const router = express.Router(); 4 | 5 | router.post("/create", () => {}); 6 | 7 | export { router as userRouter }; 8 | -------------------------------------------------------------------------------- /src/api/user/user.validation.ts: -------------------------------------------------------------------------------- 1 | // zod,joi, valibot, express validator etc. validation 2 | -------------------------------------------------------------------------------- /src/app.ts: -------------------------------------------------------------------------------- 1 | import { PORT } from "@/config"; 2 | import { 3 | globalErrorHandler, 4 | globalNotFoundHandler, 5 | } from "@/middlewares/common"; 6 | import type { Request, Response } from "express"; 7 | import { app } from "./server"; 8 | 9 | /** 10 | * @swagger 11 | * /users: 12 | * get: 13 | * summary: Get all users 14 | * description: Retrieve a list of users 15 | * responses: 16 | * 200: 17 | * description: Successfully retrieved list 18 | */ 19 | app.get("/", (req: Request, res: Response) => { 20 | res.status(200).json({ data: `Hello, world! - ${PORT}` }); 21 | }); 22 | 23 | app.use(globalNotFoundHandler); 24 | app.use(globalErrorHandler); 25 | 26 | app.listen(PORT, () => { 27 | console.log(`Server running at http://localhost:${PORT}`); 28 | console.log(`Swagger docs available at http://localhost:${PORT}/api-docs`); 29 | }); 30 | 31 | export { app }; 32 | -------------------------------------------------------------------------------- /src/config/envs.ts: -------------------------------------------------------------------------------- 1 | import dotenv from "dotenv"; 2 | 3 | dotenv.config(); 4 | 5 | export const PORT = process.env.PORT || 3000; 6 | -------------------------------------------------------------------------------- /src/config/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./envs"; 2 | export * from "./logger"; 3 | -------------------------------------------------------------------------------- /src/config/logger.ts: -------------------------------------------------------------------------------- 1 | import winston from "winston"; 2 | 3 | // const logFilePath = process.cwd() + '/logs'; 4 | 5 | export const winstonLogger = winston.createLogger({ 6 | level: "info", 7 | format: winston.format.json(), 8 | transports: [new winston.transports.Console()], 9 | }); 10 | -------------------------------------------------------------------------------- /src/config/swaggerConfig.ts: -------------------------------------------------------------------------------- 1 | import swaggerJSDoc from "swagger-jsdoc"; 2 | 3 | const options = { 4 | definition: { 5 | openapi: "3.0.0", 6 | info: { 7 | title: "My API", 8 | version: "1.0.0", 9 | description: "API documentation for my Express app", 10 | }, 11 | servers: [ 12 | { 13 | url: "http://localhost:5000", 14 | }, 15 | ], 16 | }, 17 | apis: ["./src/*.ts"], 18 | }; 19 | 20 | export const swaggerSpec = swaggerJSDoc(options); 21 | -------------------------------------------------------------------------------- /src/database/index.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yeasin2002/express-ts-starter/988c6841f678d3d7e8c4011772263e855be4ba6b/src/database/index.ts -------------------------------------------------------------------------------- /src/middlewares/common/NotFound.middlewares.ts: -------------------------------------------------------------------------------- 1 | import { NextFunction, Request, Response } from "express"; 2 | 3 | export const globalNotFoundHandler = ( 4 | req: Request, 5 | res: Response, 6 | next: NextFunction, 7 | ) => { 8 | res.status(404).json({ message: "Route not found" }); 9 | }; 10 | -------------------------------------------------------------------------------- /src/middlewares/common/globalErrorHandler.middlewares.ts: -------------------------------------------------------------------------------- 1 | import { NextFunction, Request, Response } from "express"; 2 | 3 | export const globalErrorHandler = ( 4 | err: Error, 5 | req: Request, 6 | res: Response, 7 | next: NextFunction, 8 | ) => { 9 | console.error(err.stack); 10 | res.status(500).send({ message: "Internal Server Error" }); 11 | }; 12 | -------------------------------------------------------------------------------- /src/middlewares/common/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./globalErrorHandler.middlewares"; 2 | export * from "./NotFound.middlewares"; 3 | -------------------------------------------------------------------------------- /src/server.ts: -------------------------------------------------------------------------------- 1 | import path from "path"; 2 | import bodyParser from "body-parser"; 3 | import compression from "compression"; 4 | import cors from "cors"; 5 | import express from "express"; 6 | import helmet from "helmet"; 7 | import morgan from "morgan"; 8 | import swaggerUi from "swagger-ui-express"; 9 | import { swaggerSpec } from "./config/swaggerConfig"; 10 | 11 | const app = express(); 12 | 13 | app.use(express.static(path.join(__dirname, "public"))); 14 | 15 | app.use(bodyParser.json()); 16 | app.use(bodyParser.urlencoded({ extended: true })); 17 | app.use(cors()); 18 | app.use(helmet()); 19 | app.use(compression()); 20 | app.use(morgan("dev")); 21 | app.use("/api-docs", swaggerUi.serve, swaggerUi.setup(swaggerSpec)); 22 | 23 | export { app }; 24 | -------------------------------------------------------------------------------- /src/test/app.test.ts: -------------------------------------------------------------------------------- 1 | import { app } from "@/app"; // Instead of "@/server" 2 | import request from "supertest"; 3 | import { describe, expect, it } from "vitest"; 4 | 5 | describe("GET /", () => { 6 | it("should return 200 and correct data", async () => { 7 | const response = await request(app).get("/"); 8 | 9 | expect(response.status).toBe(200); 10 | expect(response.body).toHaveProperty("data"); 11 | expect(response.body.data).toContain("Hello, world!"); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "rootDir": "./src", 4 | "outDir": "./dist", 5 | "target": "es2016", 6 | "module": "commonjs", 7 | "esModuleInterop": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "strict": true, 10 | "skipLibCheck": true, 11 | 12 | // type alias 13 | "baseUrl": "./src", 14 | "paths": { 15 | "@/*": ["*"] 16 | } 17 | }, 18 | "include": ["src/**/*.ts", "tests/**/*.ts"], 19 | "exclude": ["node_modules", "dist"] 20 | } 21 | -------------------------------------------------------------------------------- /vitest.config.ts: -------------------------------------------------------------------------------- 1 | import path from "path"; 2 | import { defineConfig } from "vitest/config"; 3 | 4 | export default defineConfig({ 5 | test: { 6 | globals: true, 7 | environment: "node", 8 | include: ["**/*.{test,spec}.{js,ts}"], 9 | alias: { 10 | "@": path.resolve(__dirname, "./src"), 11 | }, 12 | }, 13 | }); 14 | --------------------------------------------------------------------------------