Files
exp-min/README.md

309 lines
8.5 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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"
```