# Minimal Express.js Scaffolding This project offers a streamlined and minimalistic scaffolding for Express.js applications. Unlike many generators that require extensive cleanup before you can begin, this boilerplate strikes a balance between simplicity and completeness. It provides just the essentials, allowing you to pull the repository, customize the project name, and start building your API immediately. ## Support the Project If you find this project useful, consider supporting its development: [![Donate using Liberapay][liberapay-logo]][liberapay-link] [liberapay-logo]: https://liberapay.com/assets/widgets/donate.svg "Liberapay Logo" [liberapay-link]: https://liberapay.com/sfiorini/donate ## Installation To install the project dependencies, run the following command: ```bash pnpm install ``` ## Running the App To run the application in development mode with live reloading, use: ```bash pnpm dev ``` To build and run the application for production, use: ```bash pnpm start ``` ## Customization To rename the project, modify the `"name"` field in the `package.json` file. ```json { "name": "your-project-name" } ``` ## Technologies Used * **[Express](https://expressjs.com/):** Fast, unopinionated, minimalist web framework for Node.js. * **[TypeScript](https://www.typescriptlang.org/):** Typed superset of JavaScript that compiles to plain JavaScript. * **[Jest](https://jestjs.io/):** A delightful JavaScript Testing Framework with a focus on simplicity. * **[Knip](https://knip.dev/):** A tool to find unused files, dependencies, and exports in your JavaScript and TypeScript projects. * **[ESLint](https://eslint.org/):** A pluggable and configurable linter tool for identifying and reporting on patterns in JavaScript. * **[Docker](https://www.docker.com/):** A platform for developing, shipping, and running applications in containers. * **[tsx](https://tsx.is/):** A CLI to seamlessly execute TypeScript and ESM. * **[tsdown](https://tsdown.dev/):** A tool for building TypeScript projects. ## Scripts * `pnpm start`: Starts the production server after building the project. * `pnpm dev`: Runs the application in development mode with live reloading. * `pnpm build`: Builds the TypeScript project into JavaScript. * `pnpm test`: Runs the test suite using Jest and generates a coverage report. * `pnpm test:watch`: Runs the tests in watch mode, re-running them on file changes. * `pnpm knip`: Finds unused files, dependencies, and exports. * `pnpm lint`: Lints the codebase using ESLint and automatically fixes issues. ## Building This Scaffold From Scratch Here is a detailed, step-by-step guide to creating this scaffold from the ground up. ### Requirements * [Node.js](https://nodejs.org/) * [pnpm](https://pnpm.io/) ### Step-by-Step Guide 1. **Initialize the project:** ```bash pnpm init ``` 2. **Install Express:** ```bash pnpm install express ``` 3. **Set up TypeScript:** ```bash pnpm install --save-dev typescript @types/node @types/express npx tsc --init ``` 4. **Create the main application file:** Create a file at `src/index.ts`. 5. **Install `tsx` for development and `tsdown` for building:** ```bash pnpm install --save-dev tsx tsdown ``` 6. **Configure `tsdown`:** Create a `tsdown.config.ts` file: ```typescript import { defineConfig } from 'tsdown'; export default defineConfig({ entry: 'src/index.ts', format: ["esm"], target: "ESNext", platform: "node" }); ``` 7. **Configure `tsconfig.json`:** Modify your `tsconfig.json` to look like this: ```json {   "compilerOptions": {     "target": "ESNext",     "module": "ESNext",     "moduleResolution": "Bundler",     "baseUrl": "src",     "paths": {                     "@*": ["*"]     },     "esModuleInterop": true,     "forceConsistentCasingInFileNames": true,     "strict": true,     "skipLibCheck": true   } } ``` 8. **Set up Jest for testing:** ```bash pnpm install --save-dev jest ts-jest @types/jest @types/supertest supertest npx ts-jest config:init ``` This will create a `jest.config.js` file. 9. **Refactor the application for testability:** * Move all your Express app definition logic from `src/index.ts` into a new `src/server.ts` file. * The `src/index.ts` file should only contain the `server.listen()` part, which starts the server. * Create a `tests` folder and mirror the structure of your `src` folder for your test files. 10. **Install Knip to keep the project clean:** ```bash pnpm add -D knip ``` 11. **Set up ESLint for code linting:** ```bash pnpm add --save-dev eslint jiti @eslint/js typescript-eslint @stylistic/eslint-plugin eslint-plugin-n ``` Create an `eslint.config.ts` file with the following settings (modify as needed): ```typescript import eslint from '@eslint/js'; import tseslint from 'typescript-eslint'; import stylistic from '@stylistic/eslint-plugin'; import nodePlugin from 'eslint-plugin-n'; export default tseslint.config( eslint.configs.recommended, nodePlugin.configs['flat/recommended-script'], ...tseslint.configs.strictTypeChecked, ...tseslint.configs.stylisticTypeChecked, { ignores: [ '**/node_modules/*', '**/*.mjs', '**/*.js', ], }, { languageOptions: { parserOptions: { project: './tsconfig.json', warnOnUnsupportedTypeScriptVersion: false, }, }, }, { plugins: { '@stylistic/js': stylistic, '@stylistic/ts': stylistic, }, }, { files: ['**/*.ts'], }, { rules: { '@typescript-eslint/explicit-member-accessibility': 'warn', '@typescript-eslint/no-misused-promises': 0, '@typescript-eslint/no-floating-promises': 0, '@typescript-eslint/no-confusing-void-expression': 0, '@typescript-eslint/no-unnecessary-condition': 0, '@typescript-eslint/restrict-template-expressions': [ 'error', { allowNumber: true }, ], '@typescript-eslint/restrict-plus-operands': [ 'warn', { allowNumberAndString: true }, ], '@typescript-eslint/no-unused-vars': 'warn', '@typescript-eslint/no-unsafe-enum-comparison': 0, '@typescript-eslint/no-unnecessary-type-parameters': 0, '@stylistic/js/no-extra-semi': 'warn', 'max-len': [ 'warn', { 'code': 80, }, ], '@stylistic/ts/semi': ['warn', 'always'], '@stylistic/ts/member-delimiter-style': ['warn', { 'multiline': { 'delimiter': 'comma', 'requireLast': true, }, 'singleline': { 'delimiter': 'comma', 'requireLast': false, }, 'overrides': { 'interface': { 'singleline': { 'delimiter': 'semi', 'requireLast': false, }, 'multiline': { 'delimiter': 'semi', 'requireLast': true, }, }, }, }], '@typescript-eslint/no-non-null-assertion': 0, '@typescript-eslint/no-unused-expressions': 'warn', 'comma-dangle': ['warn', 'always-multiline'], 'no-console': 1, 'no-extra-boolean-cast': 0, 'indent': ['warn', 2], 'quotes': ['warn', 'single'], 'n/no-process-env': 1, 'n/no-missing-import': 0, 'n/no-unpublished-import': 0, 'prefer-const': 'warn', }, }, ); ``` 12. **Set up Docker:** Create a `Dockerfile` file: ```dockerfile # Use the official Node.js image. FROM node:22-slim # Create and change to the app directory. WORKDIR /usr/src/app # Copy package.json and pnpm-lock.yaml COPY package.json pnpm-lock.yaml ./ # Install pnpm RUN npm install -g pnpm # Install dependencies RUN pnpm install # Copy the rest of the application's source code. COPY . . # Build the project RUN pnpm run build # Expose the port the app runs on EXPOSE 3000 # Serve the app CMD [ "pnpm", "start" ] ``` Create a `docker-compose.yml` file: ```yaml services: exp-min: build: . container_name: "exp-min" ports: - "3000:3000" ```