├── .husky ├── .gitignore └── commit-msg ├── .commitlintrc ├── .gitattributes ├── nest-cli.json ├── src ├── app.service.ts ├── app.module.ts ├── app.controller.ts └── app.ts ├── tsconfig.build.json ├── .prettierrc ├── .gitignore ├── nx.json ├── tsconfig.json ├── package.json ├── eslint.config.mjs └── README.md /.husky/.gitignore: -------------------------------------------------------------------------------- 1 | _ 2 | -------------------------------------------------------------------------------- /.commitlintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["@commitlint/config-conventional"], 3 | "rules": {} 4 | } 5 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx --no-install commitlint --edit $1 5 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # JS and TS files must always use LF for tools to work 5 | *.js eol=lf 6 | *.ts eol=lf 7 | -------------------------------------------------------------------------------- /nest-cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "sourceRoot": "src", 3 | "entryFile": "app", 4 | "compilerOptions": { 5 | "tsConfigPath": "tsconfig.build.json", 6 | "deleteOutDir": true 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/app.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | 3 | @Injectable() 4 | export class AppService { 5 | public getHello(): string { 6 | return 'Hello, world!'; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "paths": {}, 5 | "outDir": "dist" 6 | }, 7 | "include": ["src/**/*"], 8 | "exclude": ["node_modules"] 9 | } 10 | -------------------------------------------------------------------------------- /src/app.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | 3 | import { AppController } from './app.controller.js'; 4 | import { AppService } from './app.service.js'; 5 | 6 | @Module({ 7 | imports: [], 8 | controllers: [AppController], 9 | providers: [AppService], 10 | }) 11 | export class AppModule {} 12 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 140, 3 | "tabWidth": 2, 4 | "useTabs": false, 5 | "semi": true, 6 | "singleQuote": true, 7 | "quoteProps": "as-needed", 8 | "jsxSingleQuote": false, 9 | "trailingComma": "all", 10 | "bracketSpacing": true, 11 | "jsxBracketSameLine": true, 12 | "arrowParens": "always" 13 | } 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | .vscode 3 | .history 4 | .DS_Store 5 | .nvmrc 6 | dist 7 | 8 | # Logs 9 | logs 10 | *.log 11 | npm-debug.log* 12 | 13 | # Dependency directories 14 | node_modules 15 | jspm_packages 16 | 17 | # Optional npm cache directory 18 | .npm 19 | 20 | # Optional REPL history 21 | .node_repl_history 22 | 23 | .nx/cache 24 | .nx/workspace-data 25 | -------------------------------------------------------------------------------- /src/app.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get } from '@nestjs/common'; 2 | 3 | import { AppService } from './app.service.js'; 4 | 5 | @Controller() 6 | export class AppController { 7 | constructor(private appService: AppService) {} 8 | 9 | @Get('/') 10 | public main(): string { 11 | return 'OK'; 12 | } 13 | 14 | @Get('/hello') 15 | public getHello(): string { 16 | return this.appService.getHello(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/app.ts: -------------------------------------------------------------------------------- 1 | import { Logger } from '@nestjs/common'; 2 | import { NestFactory } from '@nestjs/core'; 3 | 4 | import { AppModule } from './app.module.js'; 5 | 6 | async function bootstrap(): Promise { 7 | const app = await NestFactory.create(AppModule); 8 | await app.listen(8000); 9 | 10 | return app.getUrl(); 11 | } 12 | 13 | try { 14 | const url = await bootstrap(); 15 | Logger.log(url, 'Bootstrap'); 16 | } catch (error) { 17 | Logger.error(error, 'Bootstrap'); 18 | } 19 | -------------------------------------------------------------------------------- /nx.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/nx/schemas/nx-schema.json", 3 | "extends": "nx/presets/npm.json", 4 | "targetDefaults": { 5 | "lint": { 6 | "cache": true 7 | }, 8 | "build": { 9 | "cache": true 10 | } 11 | }, 12 | "release": { 13 | "version": { 14 | "conventionalCommits": true, 15 | "generatorOptions": { 16 | "fallbackCurrentVersionResolver": "disk" 17 | } 18 | }, 19 | "changelog": { 20 | "automaticFromRef": true 21 | } 22 | }, 23 | "plugins": [ 24 | { 25 | "plugin": "@nx/js/typescript", 26 | "options": { 27 | "typecheck": { 28 | "targetName": "typecheck" 29 | }, 30 | "build": { 31 | "targetName": "build", 32 | "configName": "tsconfig.lib.json", 33 | "buildDepsName": "build-deps", 34 | "watchDepsName": "watch-deps" 35 | } 36 | } 37 | } 38 | ] 39 | } 40 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "paths": {}, 5 | "target": "es2023", 6 | "module": "nodenext", 7 | "moduleResolution": "nodenext", 8 | "incremental": true, 9 | "declaration": true, 10 | "newLine": "lf", 11 | "strict": true, 12 | "allowUnreachableCode": false, 13 | "allowUnusedLabels": false, 14 | "forceConsistentCasingInFileNames": true, 15 | "noEmitOnError": true, 16 | "noFallthroughCasesInSwitch": true, 17 | "noImplicitOverride": true, 18 | "noImplicitReturns": true, 19 | "noPropertyAccessFromIndexSignature": true, 20 | "noUncheckedSideEffectImports": true, 21 | "noUnusedLocals": true, 22 | "noUnusedParameters": true, 23 | "useDefineForClassFields": false, 24 | "verbatimModuleSyntax": true, 25 | "removeComments": true, 26 | "sourceMap": true, 27 | "experimentalDecorators": true, 28 | "emitDecoratorMetadata": true, 29 | "esModuleInterop": true, 30 | "skipLibCheck": true 31 | }, 32 | "include": ["src/**/*"], 33 | "exclude": ["node_modules"] 34 | } 35 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "better-nodejs-project", 3 | "version": "0.0.0", 4 | "description": "Ways for a better Node.js backend project", 5 | "type": "module", 6 | "main": "dist/app.js", 7 | "engines": { 8 | "node": ">=22" 9 | }, 10 | "scripts": { 11 | "lint": "eslint", 12 | "build": "nest build", 13 | "prepare": "husky install", 14 | "prestart": "npm run lint && npm run build", 15 | "start": "node dist/app", 16 | "start:dev": "nest start --watch", 17 | "start:debug": "nest start --debug --watch", 18 | "release": "nx release" 19 | }, 20 | "dependencies": { 21 | "@nestjs/common": "^11.1.0", 22 | "@nestjs/core": "^11.1.0", 23 | "@nestjs/platform-express": "^11.1.0", 24 | "reflect-metadata": "^0.2.2", 25 | "rxjs": "^7.8.1" 26 | }, 27 | "devDependencies": { 28 | "@commitlint/cli": "^19.8.0", 29 | "@commitlint/config-conventional": "^19.8.0", 30 | "@eslint/js": "^9.25.1", 31 | "@nestjs/cli": "^11.0.7", 32 | "@nx/js": "^20.8.1", 33 | "@swc-node/register": "~1.9.1", 34 | "@swc/core": "~1.5.7", 35 | "@swc/helpers": "~0.5.11", 36 | "@types/node": "^22.15.3", 37 | "eslint": "^9.25.1", 38 | "eslint-config-prettier": "^10.1.2", 39 | "eslint-import-resolver-typescript": "^4.3.4", 40 | "eslint-plugin-import": "^2.31.0", 41 | "eslint-plugin-prettier": "^5.2.6", 42 | "eslint-plugin-sonarjs": "^3.0.2", 43 | "eslint-plugin-unicorn": "^59.0.0", 44 | "husky": "^9.1.7", 45 | "nx": "20.8.1", 46 | "prettier": "^3.5.3", 47 | "typescript": "^5.8.3", 48 | "typescript-eslint": "^8.31.1" 49 | }, 50 | "author": "CatsMiaow", 51 | "license": "UNLICENSED", 52 | "nx": {} 53 | } 54 | -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-default-export */ 2 | import eslint from '@eslint/js'; 3 | import importPlugin from 'eslint-plugin-import'; 4 | import prettierRecommended from 'eslint-plugin-prettier/recommended'; 5 | import sonarjs from 'eslint-plugin-sonarjs'; 6 | import unicorn from 'eslint-plugin-unicorn'; 7 | import tseslint, { configs, plugin } from 'typescript-eslint'; 8 | 9 | export default tseslint.config( 10 | eslint.configs.recommended, 11 | configs.recommendedTypeChecked, 12 | configs.strictTypeChecked, 13 | configs.stylisticTypeChecked, 14 | prettierRecommended, 15 | unicorn.configs.recommended, 16 | sonarjs.configs.recommended, 17 | { 18 | ignores: ['**/node_modules/**', 'dist/**'], 19 | }, 20 | { 21 | languageOptions: { 22 | parserOptions: { 23 | // projectService: true, 24 | projectService: { 25 | allowDefaultProject: ['*.cjs', '*.mjs'], 26 | }, 27 | tsconfigRootDir: import.meta.dirname, 28 | }, 29 | }, 30 | plugins: { 31 | '@typescript-eslint': plugin, 32 | }, 33 | extends: [importPlugin.flatConfigs.recommended, importPlugin.flatConfigs.typescript], 34 | settings: { 35 | 'import/resolver': { 36 | typescript: true, 37 | node: true, 38 | }, 39 | }, 40 | // Turn off rules you don't like. 41 | rules: { 42 | 'class-methods-use-this': 'off', 43 | 44 | 'import/no-default-export': 'error', 45 | 'import/prefer-default-export': 'off', 46 | 47 | '@typescript-eslint/no-extraneous-class': 'off', 48 | '@typescript-eslint/no-floating-promises': ['error', { ignoreIIFE: true, ignoreVoid: true }], 49 | 50 | 'sonarjs/no-commented-code': 'off', 51 | }, 52 | }, 53 | ); 54 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # better-nodejs-project 2 | 3 | Ways for a better Node.js backend project 4 | 5 | ## ECMAScript Modules (ESM) 6 | 7 | For modern, future-proof Node.js development, using ESM instead of CommonJS can provide benefits such as Top-level await, clearer handling of Circular dependency, and more 8 | 9 | - [Node.js Docs: ECMAScript modules](https://nodejs.org/docs/latest/api/esm.html) 10 | - [MDN Docs: JavaScript modules](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules) 11 | 12 | ## TypeScript 13 | 14 | For preventing errors in your code with static analysis 15 | 16 | - [TypeScript from scratch](https://www.typescriptlang.org/docs/handbook/typescript-from-scratch.html) 17 | - [TypeScript Handbook](https://www.typescriptlang.org/docs/handbook/intro.html) 18 | 19 | ## NestJS Framework 20 | 21 | For extensible architectural structures and production-ready functionality 22 | 23 | - 24 | - 25 | 26 | ## TypeScript ESLint 27 | 28 | For correcting bad code and writing consistent code 29 | 30 | - 31 | 32 | It is recommended to apply the known extended presets in addition to the basic rules \ 33 | The following rules are available in flat config in eslint v9 34 | 35 | - [eslint-plugin-sonarjs](https://github.com/SonarSource/SonarJS/blob/master/packages/jsts/src/rules/README.md) 36 | - [eslint-plugin-unicorn](https://github.com/sindresorhus/eslint-plugin-unicorn) 37 | - [eslint-plugin-github](https://github.com/github/eslint-plugin-github) 38 | - [eslint-config-love](https://github.com/mightyiam/eslint-config-love) 39 | 40 | ## Conventional Commits 41 | 42 | For consistent commit message 43 | 44 | - 45 | - 46 | 47 | ## Semantic Versioning 48 | 49 | For automatic versioning and changelog based on consistent commit messages 50 | 51 | - [Automatically Version with Conventional Commits](https://nx.dev/recipes/nx-release/automatically-version-with-conventional-commits) 52 | - [nx release version](https://nx.dev/nx-api/nx/documents/release#version) 53 | - [nx release changelog](https://nx.dev/nx-api/nx/documents/release#changelog) 54 | - If you publish the project as a package, use [nx release publish](https://nx.dev/nx-api/nx/documents/release#publish). 55 | - [Manage Releases]() 56 | - [Get Started with Nx Release](https://nx.dev/recipes/nx-release/get-started-with-nx-release) 57 | 58 | ## Husky - Git hooks 59 | 60 | For consistent work to avoid mistakes before committing 61 | 62 | - 63 | - [commitlint](https://commitlint.js.org), [lint-staged](https://www.npmjs.com/package/lint-staged), [check-dependencies](https://www.npmjs.com/package/check-dependencies), etc. 64 | 65 | ### Example 66 | 67 | See the source [code](package.json) of this repository 68 | 69 | ### Links 70 | 71 | Other helpful links 72 | 73 | - [Awesome Node.js](https://github.com/sindresorhus/awesome-nodejs) 74 | - [Node.js Best Practices](https://github.com/goldbergyoni/nodebestpractices) 75 | - [JavaScript Clean Code](https://github.com/ryanmcdermott/clean-code-javascript) 76 | - [Goodbye, Clean Code](https://overreacted.io/goodbye-clean-code) 77 | - [JavaScript Questions](https://github.com/lydiahallie/javascript-questions) 78 | - [Modern JavaScript Tutorial](https://javascript.info) 79 | --------------------------------------------------------------------------------