diff --git a/CHANGELOG.md b/CHANGELOG.md index 453ad6e..5e9798f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,5 +43,5 @@ - Real tests for the Cats module: 9 tests covering both the service and the controller with the repository mocked, where the previous specs were Nest CLI stubs that could not resolve their own dependencies. -- `README_EN.md`, `CONTRIBUTING.md` and this changelog. +- `README_CN.md`, `CONTRIBUTING.md` and this changelog. - `ci.yml`: lint, typecheck, test and build on Node 20 and 22. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 79d7800..124aa2d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -74,7 +74,7 @@ Run `pnpm format` before committing so Prettier settles the formatting; CI does - **Commit messages** follow [Conventional Commits](https://www.conventionalcommits.org/): `fix:`, `feat:`, `docs:`, `chore:`, `refactor:`, `test:`. -- **Both READMEs.** If a change affects the documented API, update `README.md` _and_ `README_EN.md`. +- **Both READMEs.** If a change affects the documented API, update `README.md` _and_ `README_CN.md`. - **Declare what you import.** Four packages were imported without ever appearing in `package.json`; they only resolved through hoisting. pnpm's layout catches this, so do not work around a missing module by reaching for a hoisted copy. @@ -163,7 +163,7 @@ pnpm build # 编译到 dist/ ## 一些约定 - **提交信息**遵循 [Conventional Commits](https://www.conventionalcommits.org/):`fix:`、`feat:`、`docs:`、`chore:`、`refactor:`、`test:`。 -- **两份 README。** 如果改动影响了对外 API,请同时更新 `README.md` 和 `README_EN.md`。 +- **两份 README。** 如果改动影响了对外 API,请同时更新 `README.md` 和 `README_CN.md`。 - **用了什么就声明什么。** 之前有四个包被 import 却从没出现在 `package.json` 里,靠依赖提升侥幸能跑。pnpm 的严格目录结构能抓到这类问题,请不要绕过去用提升出来的副本。 - **不要吞掉错误。** `catch (err) { return err }` 会把 `Error` 对象当成 200 响应发给客户端。让错误抛到 filter,由它转成文档里定义的结构。 - **entity 要和 DTO 保持一致。** entity 上缺的列,写入时会被静默丢弃。 diff --git a/README.md b/README.md index 96c8702..262da60 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@
- 面向生产的 Node REST API 脚手架:Nest + PostgreSQL + TypeORM + JWT。 + A production-ready Node REST API scaffold: Nest + PostgreSQL + TypeORM + JWT.
- 更新日志 + Changelog · - 贡献指南 + Contributing · - English + 中文文档
--- -_Nest + PostgreSQL + JWT + TypeORM_ —— 基于 Nest 和传统数据库 PostgreSQL 的 Node RESTful API 脚手架,适用于基础生产项目。 +A RESTful API scaffold built on Nest and PostgreSQL, meant as the starting point for a real +project rather than a toy. -参考 https://github.com/surmon-china/nodepress +Structure and conventions follow [nodepress](https://github.com/surmon-china/nodepress). TypeORM +takes some getting used to if you are coming from raw SQL, but it pays off: it integrates natively +with Nest, entities line up with the tables, and the query API stays readable — +see the [find options](https://typeorm.io/find-options). -刚开始不习惯 TypeORM,想使用传统 SQL 来请求数据库,后面使用 TypeORM 也还习惯:原生兼容 Nest、entity 可以和数据库打通、API 也还简单。查询参考 https://typeorm.biunav.com/zh/find-options.html +Server port, database credentials and auth settings all live in `src/app.config.ts`. -服务器端口及数据库配置、Auth 配置等详见 `src/app.config.ts`。 +## Requirements -## 环境要求 +Node >= 20, PostgreSQL, and [pnpm](https://pnpm.io/). -Node >= 20,PostgreSQL,以及 [pnpm](https://pnpm.io/)。 - -## 命令 +## Commands ```shell pnpm install -pnpm start:dev # 开发环境,watch 模式 -pnpm build # 编译到 dist/ -pnpm start:prod # 运行编译产物 +pnpm start:dev # development, watch mode +pnpm build # compile to dist/ +pnpm start:prod # run the compiled output pnpm lint # eslint pnpm typecheck # tsc --noEmit -pnpm test # vitest(单元测试) -pnpm test:e2e # vitest(e2e,需要可用的数据库) +pnpm test # vitest (unit) +pnpm test:e2e # vitest (e2e — needs a reachable database) pnpm format # prettier --write ``` -## 接口概述 - -- HTTP 状态码(详见 [errors] ) - - - `400` 请求的业务被拒绝 - - `401` 鉴权失败 - - `403` 权限不足/请求参数需要更高的权限 - - `404` 资源不存在 - - `405` 无此方法 - - `500` 服务器挂了 - - `200` 正常 - - `201` POST 正常 - -- 数据特征码(详见 [http.interface.ts] ) - - `status`: - - `success`:正常 - - `error`:异常 - - `message`:永远返回(由 [http.decorator] 装饰) - - `error`:一般会返回错误发生节点的 error;在 `status` 为 `error` 的时候必须返回,方便调试 - - `debug`:开发模式下为发生错误的堆栈,生产模式不返回 - - `result`:在 `status` 为 `success` 的时候必须返回 - - 列表数据:一般返回`{ pagination: {...}, data: {..} }` - - 具体数据:例如文章,则包含直接数据如`{ title: '', content: ... }` - -### 数据模型 - -- 通用 - -略,采用传统关系数据库模型 - -### 应用结构 - -- 入口 - - - `main.ts`:引入配置,启动主程序,引入各种全局服务 - - `app.module.ts`:主程序根模块,负责各业务模块的聚合 - - `app.controller.ts`:主程序根控制器 - - `app.config.ts`:主程序配置,数据库、程序、第三方,一切可配置项 - - `app.environment.ts:`全局环境变量 - -- 请求处理流程 +## API conventions - 1. `request`:收到请求 - 2. `middleware`:中间件过滤(跨域、来源校验等处理) - 3. `guard`:守卫过滤(鉴权) - 4. `interceptor:before`:数据流拦截器(本应用为空,即:无处理) - 5. `pipe`:参数提取(校验)器 - 6. `controller`:业务控制器 - 7. `service`:业务服务 - 8. `interceptor:after`:数据流拦截器(格式化数据、错误) - 9. `filter`:捕获以上所有流程中出现的异常,如果任何一个环节抛出异常,则返回错误 +**HTTP status codes** (see `src/errors/`): -- 鉴权处理流程 +| | | +| --- | --- | +| `200` | OK | +| `201` | Created (POST) | +| `400` | The request was understood but rejected by the business rules | +| `401` | Authentication failed | +| `403` | Insufficient permissions for the request or its parameters | +| `404` | No such resource | +| `405` | No such method | +| `500` | Server error | - 1. `guard`:[守卫] 分析请求 - 2. `guard.canActivate`:继承处理 - 3. `JwtStrategy.validate`:调用 [鉴权服务] - 4. `guard.handleRequest`:[根据鉴权服务返回的结果作判断处理,通行或拦截] +**Response shape** (see `src/interfaces/http.interface.ts`): -- 鉴权级别 +- `status` — `success` or `error`. +- `message` — always present, added by the `@HttpProcessor` decorator. +- `error` — the error from wherever it occurred. Required whenever `status` is `error`, so failures + are debuggable. +- `debug` — the stack trace, in development only. +- `result` — required whenever `status` is `success`. + - list responses: `{ pagination: {...}, data: [...] }` + - single resources: the resource itself, e.g. `{ title: '', content: ... }` - - 任何高级操作(CUD)都会校验必须的 Token(代码见 [auth.guard.ts] ) - - 涉及表数据读取的 GET 请求会智能校验 Token,无 Token 或 Token 验证生效则通行,否则不通行(代码见 [humanized-auth.guard.ts] ) +## Application structure -- 参数校验逻辑(代码见 [query-params.decorator.ts] ) +**Entry points** - - 普通用户使用高级查询参数将被视为无权限,返回 403 - - 任何用户的请求参数不合法,将被校验器拦截,返回 400 +| File | Role | +| --- | --- | +| `main.ts` | Loads config, starts the app, registers the global services | +| `app.module.ts` | Root module; composes the feature modules | +| `app.controller.ts` | Root controller | +| `app.config.ts` | Everything configurable: database, app, third parties | +| `app.environment.ts` | Global environment flags | -- 错误过滤器(代码见 [error.filter.ts] ) +**Request pipeline** -- 拦截器 [interceptors] +1. `request` — the request arrives +2. `middleware` — CORS and origin checks +3. `guard` — authentication +4. `interceptor:before` — inbound stream (empty in this app) +5. `pipe` — parameter extraction and validation +6. `controller` — the route handler +7. `service` — the business logic +8. `interceptor:after` — response and error formatting +9. `filter` — catches anything thrown anywhere above and turns it into an error response - - [缓存拦截器]:自定义这个拦截器是是要弥补框架不支持 ttl 参数的缺陷 - - [数据流转换拦截器]:当控制器所需的 Promise service 成功响应时,将在此被转换为标准的数据结构 - - [数据流异常拦截器]:当控制器所需的 Promise service 发生错误时,错误将在此被捕获 - - [日志拦截器]:代替默认的全局日志 +**Authentication flow** -- 装饰器扩展 [decorators] +1. `guard` inspects the request +2. `guard.canActivate` runs the inherited handling +3. `JwtStrategy.validate` calls the auth service +4. `guard.handleRequest` lets the request through, or blocks it - - [缓存装饰器]:用于配置 `cache key / cache ttl` - - [控制器响应装饰器]:用于输出规范化的信息,如 `message` 和 翻页参数数据 - - [请求参数提取器]:用户自动校验和格式化请求参数,包括 `query/params/辅助信息` +**Authorisation levels** -- 守卫 [guards] +- Every mutating operation (create / update / delete) requires a valid token — see + `src/guards/auth.guard.ts`. +- GET requests that read table data validate the token opportunistically: no token, or a valid + token, both pass; an invalid token does not. - - 默认所有非 GET 请求会使用 [Auth] 守卫鉴权 - - 所有涉及到多角色请求的 GET 接口会使用 [HumanizedJwtAuthGuard] 进行鉴权 +**Parameter validation** (`src/decorators/query-params.decorator.ts`) -- 中间件 [middlewares] +- An ordinary user reaching for an advanced query parameter is treated as unauthorised → `403`. +- Any request with invalid parameters is stopped by the validator → `400`. - - [Cors 中间件],用于处理跨域访问 - - [Origin 中间件],用于拦截各路不明请求 +**Interceptors** (`src/interceptors/`) -- 管道 [pipes] +- *transform* — turns a successful service Promise into the standard response shape. +- *error* — catches a failing service Promise. +- *logging* — replaces the default global logger. - - validation.pipe 用于验证所有基于 class-validate 的验证类 +**Decorators** (`src/decorators/`) -- 业务模块 [modules] +- *controller response* — normalises the output, including `message` and pagination. +- *query params* — validates and formats `query` / `params` and the derived metadata. - - Cats Demo +**Guards** (`src/guards/`) — every non-GET request goes through the `Auth` guard. -- 核心辅助模块 [processors] - - [数据库] - +**Middleware** (`src/middlewares/`) — CORS handling and origin filtering. +**Pipes** (`src/pipes/`) — `validation.pipe` validates everything built on class-validator. -#### Google Indexing API +**Feature modules** (`src/modules/`) — an `Auth` module and a `Cats` demo. -- [完整的配置流程文档](https://developers.google.com/search/apis/indexing-api/v3/quickstart) -- 「 统计用户的所有者角色 」添加页面 [在这里](https://www.google.com/webmains/verification/details?hl=zh-CN&domain=+ 面向生产的 Node REST API 脚手架:Nest + PostgreSQL + TypeORM + JWT。 +
+ + + ++ 更新日志 + · + 贡献指南 + · + English +
+ +--- + +_Nest + PostgreSQL + JWT + TypeORM_ —— 基于 Nest 和传统数据库 PostgreSQL 的 Node RESTful API 脚手架,适用于基础生产项目。 + +参考 https://github.com/surmon-china/nodepress + +刚开始不习惯 TypeORM,想使用传统 SQL 来请求数据库,后面使用 TypeORM 也还习惯:原生兼容 Nest、entity 可以和数据库打通、API 也还简单。查询参考 https://typeorm.biunav.com/zh/find-options.html + +服务器端口及数据库配置、Auth 配置等详见 `src/app.config.ts`。 + +## 环境要求 + +Node >= 20,PostgreSQL,以及 [pnpm](https://pnpm.io/)。 + +## 命令 + +```shell +pnpm install +pnpm start:dev # 开发环境,watch 模式 +pnpm build # 编译到 dist/ +pnpm start:prod # 运行编译产物 + +pnpm lint # eslint +pnpm typecheck # tsc --noEmit +pnpm test # vitest(单元测试) +pnpm test:e2e # vitest(e2e,需要可用的数据库) +pnpm format # prettier --write +``` + +## 接口概述 + +- HTTP 状态码(详见 [errors] ) + + - `400` 请求的业务被拒绝 + - `401` 鉴权失败 + - `403` 权限不足/请求参数需要更高的权限 + - `404` 资源不存在 + - `405` 无此方法 + - `500` 服务器挂了 + - `200` 正常 + - `201` POST 正常 + +- 数据特征码(详见 [http.interface.ts] ) + - `status`: + - `success`:正常 + - `error`:异常 + - `message`:永远返回(由 [http.decorator] 装饰) + - `error`:一般会返回错误发生节点的 error;在 `status` 为 `error` 的时候必须返回,方便调试 + - `debug`:开发模式下为发生错误的堆栈,生产模式不返回 + - `result`:在 `status` 为 `success` 的时候必须返回 + - 列表数据:一般返回`{ pagination: {...}, data: {..} }` + - 具体数据:例如文章,则包含直接数据如`{ title: '', content: ... }` + +### 数据模型 + +- 通用 + +略,采用传统关系数据库模型 + +### 应用结构 + +- 入口 + + - `main.ts`:引入配置,启动主程序,引入各种全局服务 + - `app.module.ts`:主程序根模块,负责各业务模块的聚合 + - `app.controller.ts`:主程序根控制器 + - `app.config.ts`:主程序配置,数据库、程序、第三方,一切可配置项 + - `app.environment.ts:`全局环境变量 + +- 请求处理流程 + + 1. `request`:收到请求 + 2. `middleware`:中间件过滤(跨域、来源校验等处理) + 3. `guard`:守卫过滤(鉴权) + 4. `interceptor:before`:数据流拦截器(本应用为空,即:无处理) + 5. `pipe`:参数提取(校验)器 + 6. `controller`:业务控制器 + 7. `service`:业务服务 + 8. `interceptor:after`:数据流拦截器(格式化数据、错误) + 9. `filter`:捕获以上所有流程中出现的异常,如果任何一个环节抛出异常,则返回错误 + +- 鉴权处理流程 + + 1. `guard`:[守卫] 分析请求 + 2. `guard.canActivate`:继承处理 + 3. `JwtStrategy.validate`:调用 [鉴权服务] + 4. `guard.handleRequest`:[根据鉴权服务返回的结果作判断处理,通行或拦截] + +- 鉴权级别 + + - 任何高级操作(CUD)都会校验必须的 Token(代码见 [auth.guard.ts] ) + - 涉及表数据读取的 GET 请求会智能校验 Token,无 Token 或 Token 验证生效则通行,否则不通行(代码见 [humanized-auth.guard.ts] ) + +- 参数校验逻辑(代码见 [query-params.decorator.ts] ) + + - 普通用户使用高级查询参数将被视为无权限,返回 403 + - 任何用户的请求参数不合法,将被校验器拦截,返回 400 + +- 错误过滤器(代码见 [error.filter.ts] ) + +- 拦截器 [interceptors] + + - [缓存拦截器]:自定义这个拦截器是是要弥补框架不支持 ttl 参数的缺陷 + - [数据流转换拦截器]:当控制器所需的 Promise service 成功响应时,将在此被转换为标准的数据结构 + - [数据流异常拦截器]:当控制器所需的 Promise service 发生错误时,错误将在此被捕获 + - [日志拦截器]:代替默认的全局日志 + +- 装饰器扩展 [decorators] + + - [缓存装饰器]:用于配置 `cache key / cache ttl` + - [控制器响应装饰器]:用于输出规范化的信息,如 `message` 和 翻页参数数据 + - [请求参数提取器]:用户自动校验和格式化请求参数,包括 `query/params/辅助信息` + +- 守卫 [guards] + + - 默认所有非 GET 请求会使用 [Auth] 守卫鉴权 + - 所有涉及到多角色请求的 GET 接口会使用 [HumanizedJwtAuthGuard] 进行鉴权 + +- 中间件 [middlewares] + + - [Cors 中间件],用于处理跨域访问 + - [Origin 中间件],用于拦截各路不明请求 + +- 管道 [pipes] + + - validation.pipe 用于验证所有基于 class-validate 的验证类 + +- 业务模块 [modules] + + - Cats Demo + +- 核心辅助模块 [processors] + - [数据库] + + + +#### Google Indexing API + +- [完整的配置流程文档](https://developers.google.com/search/apis/indexing-api/v3/quickstart) +- 「 统计用户的所有者角色 」添加页面 [在这里](https://www.google.com/webmains/verification/details?hl=zh-CN&domain=- A production-ready Node REST API scaffold: Nest + PostgreSQL + TypeORM + JWT. -
- - - -- Changelog - · - Contributing - · - 中文文档 -
- ---- - -A RESTful API scaffold built on Nest and PostgreSQL, meant as the starting point for a real -project rather than a toy. - -Structure and conventions follow [nodepress](https://github.com/surmon-china/nodepress). TypeORM -takes some getting used to if you are coming from raw SQL, but it pays off: it integrates natively -with Nest, entities line up with the tables, and the query API stays readable — -see the [find options](https://typeorm.io/find-options). - -Server port, database credentials and auth settings all live in `src/app.config.ts`. - -## Requirements - -Node >= 20, PostgreSQL, and [pnpm](https://pnpm.io/). - -## Commands - -```shell -pnpm install -pnpm start:dev # development, watch mode -pnpm build # compile to dist/ -pnpm start:prod # run the compiled output - -pnpm lint # eslint -pnpm typecheck # tsc --noEmit -pnpm test # vitest (unit) -pnpm test:e2e # vitest (e2e — needs a reachable database) -pnpm format # prettier --write -``` - -## API conventions - -**HTTP status codes** (see `src/errors/`): - -| | | -| --- | --- | -| `200` | OK | -| `201` | Created (POST) | -| `400` | The request was understood but rejected by the business rules | -| `401` | Authentication failed | -| `403` | Insufficient permissions for the request or its parameters | -| `404` | No such resource | -| `405` | No such method | -| `500` | Server error | - -**Response shape** (see `src/interfaces/http.interface.ts`): - -- `status` — `success` or `error`. -- `message` — always present, added by the `@HttpProcessor` decorator. -- `error` — the error from wherever it occurred. Required whenever `status` is `error`, so failures - are debuggable. -- `debug` — the stack trace, in development only. -- `result` — required whenever `status` is `success`. - - list responses: `{ pagination: {...}, data: [...] }` - - single resources: the resource itself, e.g. `{ title: '', content: ... }` - -## Application structure - -**Entry points** - -| File | Role | -| --- | --- | -| `main.ts` | Loads config, starts the app, registers the global services | -| `app.module.ts` | Root module; composes the feature modules | -| `app.controller.ts` | Root controller | -| `app.config.ts` | Everything configurable: database, app, third parties | -| `app.environment.ts` | Global environment flags | - -**Request pipeline** - -1. `request` — the request arrives -2. `middleware` — CORS and origin checks -3. `guard` — authentication -4. `interceptor:before` — inbound stream (empty in this app) -5. `pipe` — parameter extraction and validation -6. `controller` — the route handler -7. `service` — the business logic -8. `interceptor:after` — response and error formatting -9. `filter` — catches anything thrown anywhere above and turns it into an error response - -**Authentication flow** - -1. `guard` inspects the request -2. `guard.canActivate` runs the inherited handling -3. `JwtStrategy.validate` calls the auth service -4. `guard.handleRequest` lets the request through, or blocks it - -**Authorisation levels** - -- Every mutating operation (create / update / delete) requires a valid token — see - `src/guards/auth.guard.ts`. -- GET requests that read table data validate the token opportunistically: no token, or a valid - token, both pass; an invalid token does not. - -**Parameter validation** (`src/decorators/query-params.decorator.ts`) - -- An ordinary user reaching for an advanced query parameter is treated as unauthorised → `403`. -- Any request with invalid parameters is stopped by the validator → `400`. - -**Interceptors** (`src/interceptors/`) - -- *transform* — turns a successful service Promise into the standard response shape. -- *error* — catches a failing service Promise. -- *logging* — replaces the default global logger. - -**Decorators** (`src/decorators/`) - -- *controller response* — normalises the output, including `message` and pagination. -- *query params* — validates and formats `query` / `params` and the derived metadata. - -**Guards** (`src/guards/`) — every non-GET request goes through the `Auth` guard. - -**Middleware** (`src/middlewares/`) — CORS handling and origin filtering. - -**Pipes** (`src/pipes/`) — `validation.pipe` validates everything built on class-validator. - -**Feature modules** (`src/modules/`) — an `Auth` module and a `Cats` demo. - -**Core processors** (`src/processors/`) - -- *database* — the TypeORM connection. -- *helper* — email, IP geolocation, SEO submission and Google credentials services. - -### Google Indexing API - -[Full setup guide](https://developers.google.com/search/apis/indexing-api/v3/quickstart) - -## Contributing - -See [CONTRIBUTING.md](./CONTRIBUTING.md). - -## License - -[MIT](./LICENSE)