Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
N
nicot
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Locked Files
Issues
0
Issues
0
List
Boards
Labels
Service Desk
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Security & Compliance
Security & Compliance
Dependency List
License Compliance
Packages
Packages
List
Container Registry
Analytics
Analytics
CI / CD
Code Review
Insights
Issues
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
3rdeye
nicot
Commits
f674d72b
Commit
f674d72b
authored
Apr 23, 2025
by
nanahira
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
readme
parent
5eb9bb58
Changes
2
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
742 additions
and
94 deletions
+742
-94
README.md
README.md
+739
-86
src/bases/id-base.ts
src/bases/id-base.ts
+3
-8
No files found.
README.md
View file @
f674d72b
#
nicot
#
NICOT
Nest.js interacting with class-validator + OpenAPI + TypeORM for Nest.js Restful API development.
**NICOT**
是一个基于 NestJS + TypeORM 的后端开发框架。通过实体定义即生成:
-
数据库模型(TypeORM)
-
字段校验(class-validator)
-
请求 DTO(Create / Update / Query)
-
RESTful 接口与文档(Swagger)
-
统一返回结构、查询控制、权限注入等
## Install
适用于希望快速搭建标准化接口、减少重复代码的后端项目。
In your Nest.js project, run the following command:
---
## 📦 安装
```
bash
npm
install
@nestjs/swagger typeorm @nestjs/typeorm class-validator class-transformer nicot
npm
install
nicot typeorm @nestjs/typeorm class-validator class-transformer reflect-metadata @nestjs/swagger
```
## Entity
Those decorators would all decorate the following, with the SAME settings.
---
-
TypeORM
`@Entity()`
settings.
-
class-validator validation settings.
-
`@nestjs/swagger`
`@ApiProperty()`
settings.
## 🧱 定义实体 Entity
```
ts
@
Entity
()
export
class
User
extends
IdBase
()
{
@
Index
()
@
QueryLike
()
// queries as 'where name LIKE :name%'
@
StringColumn
(
5
,
{
class
User
extends
IdBase
()
{
@
QueryEqual
()
@
StringColumn
(
255
,
{
required
:
true
,
description
:
'
User name
'
,
description
:
'
用户名
'
,
})
name
:
string
;
@
QueryEqual
()
// queries as 'where age = :age'
@
IntColumn
(
'
int
'
,
{
unsigned
:
true
,
description
:
'
User age
'
,
default
:
20
})
@
IntColumn
(
'
int
'
,
{
unsigned
:
true
})
age
:
number
;
@
EnumColumn
(
Gender
,
{
description
:
'
User gender
'
})
gender
:
Gender
;
@
StringColumn
(
255
)
@
NotInResult
()
password
:
string
;
@
NotColumn
()
somethingElse
:
any
;
// Would not come from client input, and would not go into OpenAPI document.
@
DateColumn
()
@
NotWritable
()
createdAt
:
Date
;
}
```
// possible optional override operations
---
override
isValidInCreate
()
{
// Custom before-create check.
if
(
!
this
.
name
.
length
)
{
return
'
Name cannot be empty!
'
;
}
}
## 🧾 主键基础类:IdBase / StringIdBase
override
isValidInUpdate
()
{
// Custom before-update check.
if
(
this
.
name
&&
!
this
.
name
.
length
)
{
return
'
Name cannot be empty!
'
;
}
}
在定义实体时,NICOT 提供了两种基础类
`IdBase`
与
`StringIdBase`
,可作为实体的继承基类,为你自动处理:
override
async
beforeCreate
()
{
this
.
name
=
this
.
name
.
toLowerCase
();
// Do something before create.
}
-
主键字段定义(自增或字符串主键)
-
主键字段的权限控制与文档注解
-
默认排序逻辑(id 降序 / 升序)
-
支持 queryBuilder 查询条件注入
override
async
afterCreate
()
{
this
.
name
=
this
.
name
.
toUpperCase
();
// Do something after create before sending to user.
}
---
### 1. `IdBase()` - 数字主键(自增)
适合常见的自增整型主键使用场景。
```
ts
@
Entity
()
class
User
extends
IdBase
()
{
// 继承字段:id: number (bigint unsigned, primary key, auto-increment)
}
```
-
自动添加字段:
`id: number`
-
默认排序为
`ORDER BY id DESC`
-
使用
`Generated('increment')`
作为主键生成策略
-
搭配
`@IntColumn`
+
`@NotWritable()`
,在创建 / 修改时不可写
---
### 2. `StringIdBase()` - 字符串主键(手动或 UUID)
适合你希望使用业务主键或 UUID 作为主键的场景。传入
`uuid: true`
参数后自动生成 UUID 主键。
```
ts
@
Entity
()
class
ApiKey
extends
StringIdBase
({
uuid
:
true
,
description
:
'
API 密钥 ID
'
})
{
// 继承字段:id: string (uuid, primary key)
}
```
-
自动添加字段:
`id: string`
-
默认排序为
`ORDER BY id ASC`
-
支持配置长度(
`length`
)和描述(
`description`
)
-
`uuid: true`
时自动添加
`@Generated('uuid')`
---
### 3. 示例对比
```
ts
@
Entity
()
class
Article
extends
IdBase
({
description
:
'
文章 ID
'
})
{
// id: number 自动生成
}
@
Entity
()
class
Token
extends
StringIdBase
({
uuid
:
true
,
description
:
'
访问令牌
'
,
})
{
// id: string,自动生成 UUID
}
```
---
### 小结
| 基类 | 主键类型 | 排序默认 | ID 生成策略 | 使用场景 |
|-----------------|------------|----------|----------------------|------------------------|
|
`IdBase()`
| number | DESC | 自增
`Generated('increment')`
| 常规实体 ID |
|
`StringIdBase()`
| string | ASC | 可选 UUID / 手动输入 | UUID 主键、业务主键等 |
建议你为每个实体都继承其中一个基类,以统一主键结构和查询逻辑。
---
## 🧠 字段装饰器总览
NICOT 提供了一系列
`***Column()`
装饰器,统一处理字段的:
-
数据类型定义(TypeORM)
-
输入校验(class-validator)
-
文档描述(@nestjs/swagger)
### 字段类型装饰器(`***Column()`)
| 装饰器名 | 数据类型 | 自动添加的验证与文档 |
|----------------------|----------------|---------------------------------|
|
`@StringColumn(len)`
| string |
`@IsString()`
+
`@Length()`
|
|
`@IntColumn(type)`
| int/bigint/... |
`@IsInt()`
+ Swagger number 类型 |
|
`@FloatColumn(type)`
| float/decimal |
`@IsNumber()`
|
|
`@BoolColumn()`
| boolean |
`@IsBoolean()`
|
|
`@DateColumn()`
| Date |
`@IsDate()`
|
|
`@JsonColumn(T)`
| 任意对象/数组 |
`@IsObject()`
/
`@ValidateNested()`
等 |
所有字段装饰器都支持第二个参数
`options`
:
```
ts
@
StringColumn
(
255
,
{
required
:
true
,
description
:
'
用户名
'
,
default
:
'
Anonymous
'
,
})
name
:
string
;
```
---
## 🔒 字段访问限制装饰器(行为控制)
NICOT 提供以下装饰器用于控制字段在不同接口中的表现:
| 装饰器名 | 行为控制说明 |
|-----------------------|--------------------------------------------------------|
|
`@NotWritable()`
| 不允许在创建(POST)或修改(PATCH)时传入 |
|
`@NotChangeable()`
| 不允许在修改(PATCH)时更新(只可创建) |
|
`@NotQueryable()`
| 不允许在 GET 查询参数中使用该字段 |
|
`@NotInResult()`
| 不会出现在任何返回结果中(如密码字段) |
|
`@NotColumn()`
| 不是数据库字段(仅逻辑字段,如计算用字段) |
RestfulFactory 处理 Entity 类的时候,会以这些装饰器为依据,裁剪生成的 DTO 和查询参数。
这些限制装饰器非常适合处理:
-
安全字段(如密码、Token)
-
系统字段(如创建时间、创建者 ID)
-
只读字段(如 auto-increment 主键)
---
### 示例:完整字段定义
```
ts
@
StringColumn
(
255
,
{
required
:
true
,
description
:
'
用户昵称
'
,
})
@
NotWritable
()
nickname
:
string
;
@
BoolColumn
()
@
QueryMatchBoolean
()
isActive
:
boolean
;
```
---
override
async
beforeGet
()
{
## 🔍 查询装饰器总览(Query 系列)
NICOT 提供了一套查询装饰器,用于在 Entity 字段上声明支持的 GET 查询条件。它们会自动应用到
`findAll()`
中的 queryBuilder。
### ✅ 内建查询装饰器
| 装饰器名 | 查询效果 |
|-----------------------------|------------------------------------------------|
|
`@QueryEqual()`
| 精确匹配:
`WHERE field = :value`
|
|
`@QueryLike()`
| 前缀模糊匹配:
`WHERE field LIKE :value%`
|
|
`@QuerySearch()`
| 宽泛模糊搜索:
`WHERE field LIKE %:value%`
|
|
`@QueryMatchBoolean()`
|
`true/false/1/0`
转换为布尔类型查询 |
|
`@QueryEqualZeroNullable()`
|
`0 → IS NULL`
,否则
`= :value`
(适合 nullable)|
|
`@QueryGreater()`
| 大于查询:
`WHERE field > :value`
|
|
`@QueryOrderBy()`
| 排序字段控制:
`ORDER BY field ASC|DESC`
|
---
## 🛠 自定义查询装饰器:`QueryCondition()`
如果你需要构建更复杂或专用的查询逻辑,可以使用
`QueryCondition()`
创建自己的装饰器:
### 示例:大于查询
```
ts
export
const
QueryGreater
=
()
=>
QueryCondition
((
dto
,
qb
,
alias
,
key
)
=>
{
if
(
dto
[
key
]
!=
null
)
{
qb
.
andWhere
(
`
${
alias
}
.
${
key
}
> :
${
key
}
`
,
{
[
key
]:
dto
[
key
]
});
}
});
```
### 示例:动态排序字段(带字段名映射)
override
async
afterGet
()
{
```
ts
export
const
QueryOrderBy
=
()
=>
QueryCondition
((
dto
,
qb
,
alias
,
key
)
=>
{
const
orderValue
=
dto
[
key
];
if
(
orderValue
)
{
const
originalKey
=
key
.
replace
(
/OrderBy$/
,
''
);
qb
.
addOrderBy
(
`
${
alias
}
.
${
originalKey
}
`
,
orderValue
);
}
});
```
override
async
beforeUpdate
()
{
> 使用方式与普通装饰器一致,应用在实体字段上即可。
---
### 使用效果示例
```
ts
@
IntColumn
(
'
int
'
,
{
unsigned
:
true
})
@
QueryGreater
()
views
:
number
;
@
StringColumn
(
255
)
@
QueryLike
()
title
:
string
;
@
BoolColumn
()
@
QueryMatchBoolean
()
isPublished
:
boolean
;
@
NotWritable
()
@
NotInResult
()
@
QueryOrderBy
()
@
IsIn
([
'
ASC
'
,
'
DESC
'
])
@
ApiProperty
({
enum
:
[
'
ASC
'
,
'
DESC
'
],
description
:
'
Order by views
'
})
viewsOrderBy
?:
'
ASC
'
|
'
DESC
'
;
```
---
## 🔁 生命周期钩子
支持在实体中定义以下方法:
```
ts
class User {
async beforeCreate() {}
async afterCreate() {}
async beforeUpdate() {}
async afterUpdate() {}
async beforeGet() {}
async afterGet() {}
isValidInCreate()
:
string | undefined {
return this.name ? undefined
:
'
必须填写名称';
}
}
```
There are also other following decorators to control accessibility:
---
-
`@NotWritable()`
Can only come from GET requests.
-
`@NotChangeable()`
Cannot be changed by PATCH requests.
## 🛠 使用 CrudService(服务层标准写法)
## CrudService
NICOT 提供了
`CrudService(Entity, options)`
,是所有资源的标准服务实现方式。
Creates a service for database operation in one word.
你只需继承它,并传入对应的实体和配置,即可拥有完整的:
-
查询(支持分页、排序、过滤、关系)
-
创建、更新、删除(带钩子、校验、字段控制)
-
统一返回结构
---
### 定义 Service
```
ts
import
{
CrudService
}
from
'
nicot
'
;
@
Injectable
()
export
class
UserService
extends
CrudService
(
User
)
{
constructor
(@
InjectDataSource
()
db
:
DataSource
)
{
super
(
db
.
getRepository
(
User
));
export
class
ArticleService
extends
CrudService
(
Article
,
{
relations
:
[
'
user
'
],
// 自动关联 user 实体(LEFT JOIN)
})
{
constructor
(@
InjectRepository
(
Article
)
repo
)
{
super
(
repo
);
}
// 可根据需要添加业务方法(非覆盖)
async
downloadArticle
(
id
:
number
):
Promise
<
Buffer
>
{
const
res
=
await
this
.
findOne
(
id
);
return
res
.
data
.
getContentAsBuffer
();
}
}
```
## Controller decorators
---
### 关于 relations
`relations: string[]`
是
`CrudService`
的核心配置项之一。它用于在查询中自动加载关联实体(即 TypeORM 的
`leftJoinAndSelect`
)。
-
`'user'`
表示加载
`article.user`
-
`'user.articles'`
表示递归加载嵌套关系
-
默认使用
`LEFT JOIN`
,如需
`INNER JOIN`
可通过
`Inner('user')`
指定
这能确保你在 Controller 中无需手动构建复杂的 join 查询。
---
### 方法列表
| 方法名 | 说明 |
|------------------|----------------------------------------|
|
`findAll(dto, qb?)`
| 查询列表(支持查询装饰器 / 分页) |
|
`findOne(id, qb?)`
| 查单条数据,自动关联 / 过滤 / 封装 |
|
`create(dto)`
| 创建数据,带验证、钩子处理 |
|
`update(id, dto, extraConditions?)`
| 更新数据并支持条件限制 |
|
`delete(id, extraConditions?)`
| 删除数据(软删) |
---
### 示例:条件限制用户只能操作自己数据
```
ts
async
findOne
(
id
:
number
,
user
:
User
)
{
return
this
.
service
.
findOne
(
id
,
qb
=>
qb
.
andWhere
(
'
userId = :uid
'
,
{
uid
:
user
.
id
}));
}
async
update
(
id
:
number
,
dto
:
UpdateDto
,
user
:
User
)
{
return
this
.
service
.
update
(
id
,
dto
,
{
userId
:
user
.
id
});
// 附加 where 条件
}
```
---
### 建议实践
-
所有实体的服务类都应继承
`CrudService(Entity, options)`
-
`relations`
是推荐使用的配置方式,替代手动 join
-
如果你有定制查询逻辑,建议用
`super.findAll(...)`
+
`.data`
进行后处理
-
避免直接使用
`repo`
,使用封装后的方法保持一致性与钩子逻辑生效
---
## 🧩 Controller 自动生成(RestfulFactory)
NICOT 提供了
`RestfulFactory(Entity)`
工厂函数,自动为实体生成标准 RESTful Controller 接口装饰器及参数提取器。
你不再需要手动定义每个路由,只需:
1.
创建 DTO(工厂生成)
2.
使用工厂提供的装饰器
---
### 一键生成的接口说明
| 方法 | 路径 | 功能说明 |
|--------------------------|-------------------------|---------------------------|
|
`@factory.create()`
|
`POST /`
| 创建,使用
`createDto`
|
|
`@factory.findOne()`
|
`GET /:id`
| 获取单条数据 |
|
`@factory.findAll()`
|
`GET /`
| 查询列表,支持过滤 / 分页 |
|
`@factory.update()`
|
`PATCH /:id`
| 修改单条数据 |
|
`@factory.delete()`
|
`DELETE /:id`
| 删除单条数据(软删) |
---
### 参数提取装饰器一览
| 装饰器 | 用途说明 |
|----------------------------|-----------------------------------------|
|
`@factory.createParam()`
| 注入
`createDto`
,自动校验 body |
|
`@factory.updateParam()`
| 注入
`updateDto`
,自动校验 body |
|
`@factory.findAllParam()`
| 注入
`queryDto`
,自动校验 query |
|
`@factory.idParam()`
| 注入路径参数中的 id |
这些参数装饰器全部内建了
`ValidationPipe`
,支持自动转换与校验。
---
### 查询能力:基于实体字段的装饰器
`@factory.findAll()`
所生成的接口具有完整的查询能力,其行为由实体字段上的
`@QueryXXX()`
装饰器控制:
```
ts
@
StringColumn
(
255
)
@
QueryEqual
()
name
:
string
;
@
BoolColumn
()
@
QueryMatchBoolean
()
isActive
:
boolean
;
```
则生成的
`GET /resource?name=Tom&isActive=true`
接口会自动构建对应的 SQL 条件。
---
Would also register proper OpenAPI documentation for the controller.
### 示例 Controller
```
ts
const
dec
=
new
RestfulFactory
(
User
);
class
FindAllUsersDto
extends
dec
.
findAllDto
{}
// to extract type and class
class
UpdateUserDto
extends
dec
.
updateDto
{}
const
factory
=
new
RestfulFactory
(
User
);
class
CreateUserDto
extends
factory
.
createDto
{}
class
UpdateUserDto
extends
factory
.
updateDto
{}
class
FindAllUserDto
extends
factory
.
findAllDto
{}
@
Controller
(
'
user
'
)
export
class
UserController
{
constructor
(
private
userS
ervice
:
UserService
)
{}
constructor
(
private
readonly
s
ervice
:
UserService
)
{}
@
dec
.
create
()
// POST /
create
(@
dec
.
createParam
()
user
:
User
)
{
return
this
.
userService
.
create
(
user
);
@
factory
.
create
()
async
create
(@
factory
.
createParam
()
dto
:
CreateUserDto
)
{
return
this
.
service
.
create
(
dto
);
}
@
dec
.
findOne
()
// GET /:id
findOne
(@
dec
.
idParam
()
id
:
number
)
{
return
this
.
userService
.
findOne
(
id
);
@
factory
.
findAll
()
async
findAll
(@
factory
.
findAllParam
()
dto
:
FindAllUserDto
)
{
return
this
.
service
.
findAll
(
dto
);
}
@
dec
.
findAll
()
// GET /
findAll
(@
dec
.
findAllParam
()
user
:
FindAllUsersDto
)
{
return
this
.
userService
.
findAll
(
user
);
@
factory
.
findOne
()
async
findOne
(@
factory
.
idParam
()
id
:
number
)
{
return
this
.
service
.
findOne
(
id
);
}
@
dec
.
update
()
// PATCH /:id
update
(@
dec
.
idParam
()
id
:
number
,
@
dec
.
updateParam
()
user
:
UpdateUserDto
)
{
return
this
.
userService
.
update
(
id
,
user
);
@
factory
.
update
()
async
update
(@
factory
.
idParam
()
id
:
number
,
@
factory
.
updateParam
()
dto
:
UpdateUserDto
)
{
return
this
.
service
.
update
(
id
,
dto
);
}
@
dec
.
delete
()
// DELETE /:id
delete
(@
dec
.
idParam
()
id
:
number
)
{
return
this
.
userS
ervice
.
delete
(
id
);
@
factory
.
delete
()
async
delete
(@
factory
.
idParam
()
id
:
number
)
{
return
this
.
s
ervice
.
delete
(
id
);
}
}
```
## Return message
---
### 补充说明
-
所有路由默认返回统一结构(
`GenericReturnMessageDto`
/
`BlankReturnMessageDto`
)
-
所有参数自动校验,无需手动加
`ValidationPipe`
-
`findAll()`
自动支持分页、排序、模糊查询、布尔匹配等
-
如果你使用了实体关系(relations),则
`findOne()`
/
`findAll()`
也自动关联查询
-
所有的接口都是返回状态码 200。
-
OpenAPI 文档会自动生成,包含所有 DTO 类型与查询参数。
-
Service 需要使用
`CrudService(Entity, options)`
进行标准化实现。
---
## 📄 分页查询(自动支持)
NICOT 的
`findAll()`
方法默认支持分页,
**无需你手动声明分页字段**
,框架内部已内置分页 DTO 与逻辑。
---
### ✅ 默认分页行为
所有
`findAll()`
查询接口会自动识别以下 query 参数:
| 参数 | 类型 | 默认值 | 说明 |
|------------------|----------|--------|---------------------------------|
|
`pageCount`
| number |
`1`
| 第几页,从 1 开始 |
|
`recordsPerPage`
| number |
`25`
| 每页多少条数据 |
这些字段由框架内置的
`PageSettingsDto`
管理,自动注入到
`findAllParam()`
的 DTO 中,无需你自己定义。
分页逻辑最终会转化为:
```
ts
qb
.
take
(
recordsPerPage
).
skip
((
pageCount
-
1
)
*
recordsPerPage
);
```
---
### 🔧 如何更改分页行为
分页逻辑由实体继承类中的方法控制(如
`getRecordsPerPage()`
),如果你希望关闭分页或调高上限,可以 override 这些方法:
```
ts
@
Entity
()
class
LogEntry
extends
IdBase
()
{
// ...其他字段
override
getRecordsPerPage
()
{
return
this
.
recordsPerPage
||
99999
;
// 禁用分页(或返回极大值)
}
}
```
这样处理后,该实体的
`findAll()`
查询将默认返回所有数据。
---
### 示例:分页 + 条件查询
```
ts
GET
/
user
?
name
=
Tom
&
pageCount
=
2
&
recordsPerPage
=
10
// 查询第 2 页,每页 10 条,筛选 name = Tom 的用户
```
你可以在 Controller 中完全不关心这些字段,它们已由 NICOT 自动注入、处理并应用在 QueryBuilder 上。
---
## 📦 统一返回结构与接口注解
NICOT 默认提供统一的接口返回格式与 Swagger 自动注解能力,便于前后端标准化对接。
---
### ✅ 返回结构 DTO 类型(用于 Swagger 类型标注)
#### `ReturnMessageDto(EntityClass)`
用于生成带数据的标准返回结构类型(
**不是直接返回值**
,用于
`@nestjs/swagger`
)。
返回结构样式:
```
json
{
"statusCode"
:
200
,
"success"
:
true
,
"message"
:
"success"
,
"timestamp"
:
"2025-04-25T12:00:00.000Z"
,
"data"
:
{
...
}
}
```
#### `BlankReturnMessageDto`
无数据返回结构的类型(用于 DELETE、UPDATE 等空响应)。
---
### 📊 实际返回结构
-
**返回数据:**
```
ts
import
{
GenericReturnMessageDto
}
from
'
nicot
'
;
return
new
GenericReturnMessageDto
(
200
,
'
操作成功
'
,
data
);
```
-
**返回空结构:**
```
ts
import
{
BlankReturnMessageDto
}
from
'
nicot
'
;
return
new
BlankReturnMessageDto
(
204
,
'
删除成功
'
);
```
-
**抛出异常结构:**
```
ts
throw
new
BlankReturnMessageDto
(
404
,
'
未找到资源
'
).
toException
();
```
---
### 📚 Swagger 注解装饰器
NICOT 提供以下装饰器帮助你自动声明接口返回结构,无需手动写复杂的
`@ApiResponse(...)`
:
#### `@ApiTypeResponse(EntityClass)`
等价于:
```
ts
@
ApiOkResponse
({
type
:
ReturnMessageDto
(
EntityClass
),
description
:
'
成功响应结构
'
,
})
```
#### `@ApiError(code, message)`
等价于:
```
ts
@
ApiResponse
({
status
:
code
,
description
:
message
,
type
:
BlankReturnMessageDto
,
})
```
---
Return data of all APIs are in the following format, with proper OpenAPI documentation:
### 示例用法
```
ts
export
interface
ReturnMessage
<
T
>
{
statusCode
:
number
;
message
:
string
;
success
:
boolean
;
data
:
T
;
@
Get
()
@
ApiTypeResponse
(
User
)
@
ApiError
(
404
,
'
未找到用户
'
)
async
findOne
(@
Query
()
dto
:
SearchDto
)
{
const
user
=
await
this
.
service
.
findOne
(
dto
);
if
(
!
user
)
{
throw
new
BlankReturnMessageDto
(
404
,
'
未找到用户
'
).
toException
();
}
return
new
GenericReturnMessageDto
(
200
,
'
成功
'
,
user
);
}
```
You may also create a Dto class like this by the following way:
---
## 📥 参数解析 + 验证(DataQuery / DataBody)
NICOT 提供便捷装饰器
`@DataQuery()`
与
`@DataBody()`
,用于自动完成:
-
参数绑定(从 query 或 body)
-
数据校验(class-validator)
-
类型转换(
`transform: true`
)
-
避免重复书写 ValidationPipe
---
### ✅ 装饰器对照说明
| 装饰器 | 等价于标准写法 |
|----------------|-------------------------------------------------------------------------------|
|
`@DataQuery()`
|
`@Query(new ValidationPipe({ transform: true }))`
|
|
`@DataBody()`
|
`@Body(new ValidationPipe({ transform: true }))`
|
这些装饰器默认启用了:
-
自动类型转换(如 query string 转 number)
-
自动剔除未声明字段(
`whitelist: true`
)
-
自动抛出校验异常(422)
---
### 示例用法
```
ts
export
class
UserReturnMessage
extends
ReturnMessageDto
(
User
)
{}
@
Get
()
async
findAll
(@
DataQuery
()
dto
:
SearchUserDto
)
{
return
this
.
service
.
findAll
(
dto
);
}
@
Post
()
async
create
(@
DataBody
()
dto
:
CreateUserDto
)
{
return
this
.
service
.
create
(
dto
);
}
```
With result into the following class, also with proper OpenAPI documentation:
你无需手动加
`ValidationPipe`
,也无需手动处理转换错误或格式校验,NICOT 帮你做好了这一切。
---
## 🧩 实体关系示例
```
ts
export
class
UserReturnMessage
{
statusCode
:
number
;
message
:
string
;
success
:
boolean
;
data
:
User
;
@
Entity
()
class
Article
extends
IdBase
()
{
@
QueryEqual
()
@
IntColumn
(
'
bigint
'
,
{
unsigned
:
true
})
userId
:
number
;
@
ManyToOne
(()
=>
User
,
user
=>
user
.
articles
,
{
onDelete
:
'
CASCADE
'
})
user
:
User
;
}
@
Entity
()
class
User
extends
IdBase
()
{
@
OneToMany
(()
=>
Article
,
article
=>
article
.
user
)
articles
:
Article
[];
async
afterGet
()
{
this
.
articleCount
=
this
.
articles
.
length
;
}
}
```
---
## 📊 和同类框架的对比
在实际开发中,很多框架也提供了 CRUD 接口构建能力,但存在不同程度的痛点。NICOT 从底层设计上解决了这些问题,适合长期维护的中大型后端项目。
---
### ✅ FastAPI / SQLModel(Python)
-
✅ 代码简洁,自动生成 OpenAPI 文档
-
❌ 无字段权限控制(不能区分不可写/不可查)
-
❌ 查询能力不够细致,字段粒度控制弱
-
❌ DTO 拆分需手动处理,复杂模型重复多
🔹
**NICOT 优势:**
-
字段级别控制查询/写入/输出行为
-
自动生成 DTO + 查询 + OpenAPI + 验证
-
生命周期钩子和逻辑注入更灵活
---
### ✅ @nestjsx/crud(NestJS)
-
✅ 快速生成接口
-
❌ 安全性差:字段查询/排序过于开放
-
❌ 控制力弱:很难注入逻辑或自定义查询
-
❌ Swagger 文档支持不完整
🔹
**NICOT 优势:**
-
每个字段查询能力需显式声明(不开放默认)
-
完全类型安全 + 文档自动生成
-
逻辑钩子、权限注入、返回结构标准化
---
### ✅ nestjs-query
-
✅ 支持 GraphQL / REST,类型安全强
-
❌ 学习曲线陡峭,文档不友好
-
❌ 查询逻辑复杂,难以上手
-
❌ 重度依赖 GraphQL 思维模式
🔹
**NICOT 优势:**
-
更贴合 REST 直觉思维
-
默认封装,低学习成本
-
保留足够扩展点,轻松注入业务逻辑
---
### ✅ GraphQL
-
✅ 查询自由,前端控制力强
-
❌ 后端控制弱,权限处理复杂
-
❌ 易产生过度查询,性能不稳定
-
❌ 每个字段都必须写解析器,开发成本高
🔹
**NICOT 优势:**
-
后端主导接口结构,前端只调 REST
-
查询能力与字段权限完全可控
-
无需额外解析器,开发更快速
---
### ✅ MyBatis-Plus / Hibernate(Java)
-
✅ 成熟,生态强,Java 企业常用
-
❌ 配置繁杂,样板代码多
-
❌ 缺乏统一的返回结构与接口注解
-
❌ 参数校验 / DTO 拆分手动重复
🔹
**NICOT 优势:**
-
一套装饰器统一字段校验 + ORM + 文档
-
自动 DTO 拆分,减少重复代码
-
全自动接口 + 验证 + 注解集成
---
### 🏆 框架能力矩阵对比
| 框架 | 自动接口 | 安全性 | 文档支持 | 类型安全 | 查询控制 | 关系联查支持 | 开发效率 |
|-----------------------------|----------------|----------------|----------------|----------------|------------------|------------------|----------------|
|
**NICOT**
| ✅ 全自动 | ✅ 字段级控制 | ✅ 实体即文档 | ✅ 完整类型推导 | ✅ 装饰器精细控制 | ✅ 自动 relations | ✅ 极高 |
| FastAPI + SQLModel | ✅ 模型映射生成 | ❌ 缺乏限制 | ✅ 自动生成 | ❌ 运行时类型 | ❌ 查询不受控 | 🟡 手写关系加载 | ✅ 高 |
| @nestjsx/crud | ✅ 快速注册 | ❌ 默认全暴露 | ❌ Swagger 不完整 | ✅ Nest 类型系统 | ❌ 全字段可查 | 🟡 需手动配置 | ✅ 快速上手 |
| nestjs-query | ✅ 自动暴露接口 | 🟡 DTO 控权限 | 🟡 手动标注文档 | ✅ 强类型推导 | 🟡 灵活但复杂 | ✅ 关系抽象良好 | ❌ 配置繁琐 |
| GraphQL(code-first) | ❌ Resolver 必写| ❌ 查询不受控 | ✅ 类型强大 | ✅ 静态推导 | ❌ 查询过度灵活 | ✅ 查询关系强 | ❌ 繁琐/易错 |
| Hibernate(Java) | ❌ 需配 Service | 🟡 靠注解控制 | ❌ 文档需插件 | 🟡 Java 泛型弱 | 🟡 XML/HQL 控制 | ✅ JPA 级联支持 | ❌ 模板代码多 |
| MyBatis-Plus(Java) | ✅ 注解生成 | ✅ 手写控制 | ❌ 文档缺失 | ❌ 运行期校验 | ❌ 手写 SQL | ❌ 需 JOIN SQL | ❌ 重复手写多 |
| NestJS + TypeORM + 手动 DTO | ❌ 全手写 | ✅ 自由控制 | ✅ 自己写 | ✅ 类型安全 | 🟡 逻辑自己处理 | 🟡 手写 relations | ❌ 重复代码多 |
---
NICOT 作为一个 “Entity 驱动” 的框架,在开发体验、安全性、自动化程度之间找到了平衡,真正做到:
> 一份实体定义 → 自动生成完整、安全、文档完备的接口系统
---
## ✅ 总结
**NICOT = Entity 驱动 + 自动生成的一体化后端框架**
,涵盖:
-
实体建模 → 校验规则 → DTO → OpenAPI
-
自动生成 Controller / Service
-
灵活字段控制、查询扩展、用户注入、生命周期钩子
-
内建返回结构、Swagger 注解、守卫装饰器等功能
是构建 NestJS 标准化、低重复、文档完善的后端服务的理想选择。
src/bases/id-base.ts
View file @
f674d72b
...
...
@@ -62,14 +62,9 @@ export function StringIdBase(idOptions: StringIdOptions) {
columnExtras
:
{
primary
:
true
,
nullable
:
false
},
}),
Reflect
.
metadata
(
'
design:type
'
,
String
),
...(
idOptions
.
uuid
?
[
Generated
(
'
uuid
'
),
NotWritable
(),
]
:
[
IsString
(),
IsNotEmpty
(),
NotChangeable
(),
])
...(
idOptions
.
uuid
?
[
Generated
(
'
uuid
'
),
NotWritable
()]
:
[
IsString
(),
IsNotEmpty
(),
NotChangeable
()]),
];
const
dec
=
MergePropertyDecorators
(
decs
);
dec
(
cl
.
prototype
,
'
id
'
);
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment