nestia 2.1.2 → 2.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +566 -467
- package/lib/factories/SchemaFactory.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,373 +1,344 @@
|
|
|
1
1
|
# Nestia
|
|
2
|
-
Automatic `SDK` and `
|
|
2
|
+
Automatic `SDK` and `Swagger` generator for the `NestJS`.
|
|
3
3
|
|
|
4
|
-
[](https://github.com/samchon/nestia/blob/master/LICENSE)
|
|
5
|
-
[](https://www.npmjs.com/package/nestia)
|
|
6
|
-
[](https://www.npmjs.com/package/nestia)
|
|
7
|
-
[](https://github.com/samchon/nestia/actions?query=workflow%3Abuild)
|
|
4
|
+
[](https://github.com/samchon/nestia/blob/master/LICENSE)
|
|
5
|
+
[](https://www.npmjs.com/package/nestia)
|
|
6
|
+
[](https://www.npmjs.com/package/nestia)
|
|
7
|
+
[](https://github.com/samchon/nestia/actions?query=workflow%3Abuild)
|
|
8
|
+
[](https://github.com/samchon/nestia/wiki)
|
|
9
|
+
|
|
10
|
+
- Github: https://github.com/samchon/nestia
|
|
11
|
+
- NPM: https://www.npmjs.com/packages/nestia
|
|
12
|
+
- Guide Documents: https://github.com/samchon/nestia/wiki
|
|
8
13
|
|
|
9
14
|
```bash
|
|
10
15
|
# INSTALL NESTIA
|
|
11
16
|
npm install --save-dev nestia
|
|
12
17
|
|
|
13
|
-
#
|
|
18
|
+
# BUILDING SDK LIBRARY
|
|
14
19
|
npx nestia sdk "src/controller" --out "src/api"
|
|
15
|
-
|
|
16
|
-
# REGULAR NESTJS PATTERN
|
|
17
20
|
npx nestia sdk "src/**/*.controller.ts" --out "src/api"
|
|
21
|
+
npx nestia sdk "src/controller" \
|
|
22
|
+
--exclude "src/controller/test" \
|
|
23
|
+
--out "src/api"
|
|
18
24
|
|
|
19
25
|
# BUILDING SWAGGER.JSON IS ALSO POSSIBLE
|
|
20
26
|
npx nestia swagger "src/controller" -- out "swagger.json"
|
|
21
27
|
```
|
|
22
28
|
|
|
23
|
-
Don't write any
|
|
29
|
+
Don't write any swagger comment and DTO decorator. Just run the `nestia` up.
|
|
24
30
|
|
|
25
31
|
- No swagger comment/decorator
|
|
26
32
|
- No DTO comment/decorator
|
|
27
|
-
- Only pure NestJS code
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
- Only pure `interface`s and NestJS code are required
|
|
34
|
+
- Suppors generic typed `interface`s and `controller`s
|
|
35
|
+
- Support union/intersection types
|
|
36
|
+
- Support conditional types
|
|
37
|
+
|
|
38
|
+
The `nestia` is an evolved automatic `SDK` and `Swagger` generator than ever, who does not require any type of swagger comment or DTO decorator function, by analyzing your `NestJS` developed backend server code in the compilation level (`nestia` utilizes the TypeScript Compiler API).
|
|
39
|
+
|
|
40
|
+
Therefore, don't write any swagger comment and don't use any DTO related decorator function. Just use the pure interface type with the pure `NestJS` code. Reading below sections and looking at example codes of the `nestia`, feel how the `nestia` is much stronger than the legacy `@nestjs/swagger`.
|
|
41
|
+
|
|
42
|
+
Components | `nestia`::SDK | `nestia`::swagger | `@nestjs/swagger`
|
|
43
|
+
-----------|---|---|---
|
|
44
|
+
DTO with pure interface | ✔ | ✔ | ❌
|
|
45
|
+
descriptions by comments | ✔ | ✔ | ❌
|
|
46
|
+
Simple structure | ✔ | ✔ | ✔
|
|
47
|
+
Generic typed structure | ✔ | ✔ | ❌
|
|
48
|
+
Union typed structure | ✔ | ✔ | ▲
|
|
49
|
+
Intersection typed structure | ✔ | ✔ | ▲
|
|
50
|
+
Conditional typed structure | ✔ | ▲ | ❌
|
|
51
|
+
Auto completion | ✔ | ❌ | ❌
|
|
52
|
+
Type hints | ✔ | ❌ | ❌
|
|
53
|
+
2x faster `JSON.stringify()` | ✔ | ❌ | ❌
|
|
54
|
+
Ensure type safety | ✔ | ❌ | ❌
|
|
35
55
|
|
|
36
56
|
```typescript
|
|
37
|
-
|
|
38
|
-
import
|
|
39
|
-
import { IPage } from "@samchon/
|
|
40
|
-
|
|
41
|
-
|
|
57
|
+
// IMPORT SDK LIBRARY WHO'VE BEEN GENERATED BY THE NESTIA
|
|
58
|
+
import api from "@samchon/shopping-api";
|
|
59
|
+
import { IPage } from "@samchon/shopping-api/lib/structures/IPage";
|
|
60
|
+
import { ISale } from "@samchon/shopping-api/lib/structures/ISale";
|
|
61
|
+
import { ISaleArticleComment } from "@samchon/shopping-api/lib/structures/ISaleArticleComment";
|
|
62
|
+
import { ISaleQuestion } from "@samchon/shopping-api/lib/structures/ISaleQuestion";
|
|
63
|
+
|
|
64
|
+
export async function trace_sale_question_and_comment
|
|
65
|
+
(connection: api.IConnection): Promise<void>
|
|
42
66
|
{
|
|
43
|
-
// LIST UP
|
|
44
|
-
const index: IPage<
|
|
67
|
+
// LIST UP SALE SUMMARIES
|
|
68
|
+
const index: IPage<ISale.ISummary> = await api.functional.shoppings.sales.index
|
|
45
69
|
(
|
|
46
70
|
connection,
|
|
47
|
-
"
|
|
71
|
+
"general",
|
|
48
72
|
{ limit: 100, page: 1 }
|
|
49
73
|
);
|
|
50
74
|
|
|
51
|
-
//
|
|
52
|
-
const
|
|
75
|
+
// PICK A SALE
|
|
76
|
+
const sale: ISale = await api.functional.shoppings.sales.at
|
|
53
77
|
(
|
|
54
|
-
connection,
|
|
55
|
-
"free",
|
|
78
|
+
connection,
|
|
56
79
|
index.data[0].id
|
|
57
80
|
);
|
|
58
|
-
console.log(
|
|
59
|
-
}
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
|
|
81
|
+
console.log("sale", sale);
|
|
63
82
|
|
|
83
|
+
// WRITE A QUESTION
|
|
84
|
+
const question: ISaleQuestion = await api.functional.shoppings.sales.questions.store
|
|
85
|
+
(
|
|
86
|
+
connection,
|
|
87
|
+
"general",
|
|
88
|
+
sale.id,
|
|
89
|
+
{
|
|
90
|
+
title: "How to use this product?",
|
|
91
|
+
body: "The description is not fully enough. Can you introduce me more?",
|
|
92
|
+
files: []
|
|
93
|
+
}
|
|
94
|
+
);
|
|
95
|
+
console.log("question", question);
|
|
64
96
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
npx nestia sdk "src/**/*.controller.ts" --out "src/api"
|
|
80
|
-
npx nestia sdk "src/controllers" --out "src/api"
|
|
81
|
-
npx nestia sdk "src/controllers/consumers" "src/controllers/sellers" --out "src/api"
|
|
82
|
-
npx nestia sdk "src/controllers" --exclude "src/**/Fake*.ts" --out "src/api"
|
|
97
|
+
// WRITE A COMMENT
|
|
98
|
+
const comment: ISaleArticleComment = await api.functional.shoppings.sales.comments.store
|
|
99
|
+
(
|
|
100
|
+
connection,
|
|
101
|
+
"general",
|
|
102
|
+
sale.id,
|
|
103
|
+
question.id,
|
|
104
|
+
{
|
|
105
|
+
body: "p.s) Can you send me a detailed catalogue?",
|
|
106
|
+
anonymous: false
|
|
107
|
+
}
|
|
108
|
+
);
|
|
109
|
+
console.log("comment", comment);
|
|
110
|
+
}
|
|
83
111
|
```
|
|
84
112
|
|
|
85
|
-
To generate a SDK library through the [nestia](https://github.com/samchon/nestia) is very easy.
|
|
86
|
-
|
|
87
|
-
Just type the `nestia sdk <input> --out <output>` command in the console. When there're multiple source directories containing the NestJS controller classes, type all of them separating by a `space` word. If you want to exclude some directories or files from the SDK generation, the `--exclude` option would be useful.
|
|
88
|
-
|
|
89
|
-
Also, when generating a SDK using the cli options, `compilerOptions` would follow the `tsconfig.json`, that is configured for the backend server. If no `tsconfig.json` file exists in your project, the configuration would be default option (`ES5` with `strict` mode). If you want to use different `compilerOptions` with the `tsconfig.json`, you should configure the [nestia.config.ts](#nestiaconfigts).
|
|
90
|
-
|
|
91
|
-
### Swagger generation
|
|
92
|
-
```bash
|
|
93
|
-
npx nestia <source_controller_of_directory> --out <output_path>
|
|
94
|
-
|
|
95
|
-
npx nestia swagger "src/**/*.controller.ts" --out "./"
|
|
96
|
-
npx nestia swagger "src/controllers" --out "./swagger.json"
|
|
97
|
-
npx nestia swagger "src/consumers" "src/sellers" --out "actors.json"
|
|
98
|
-
npx nestia swagger "src/controllers" --exclude "src/**/Fake*.ts" -out "./"
|
|
99
|
-
```
|
|
100
113
|
|
|
101
|
-
The [nestia](https://github.com/samchon/nestia) even supports the `swagger.json` generation and it's also extermely easy.
|
|
102
114
|
|
|
103
|
-
Jsut type the `nestia swagger <input> --out <output>` command in the console. When there're multiple source directories containing the NestJS controller classes, type all of them separating by a `space` word. If you want to exclude some directories or files from the `swagger.json` generation, the `--exclude` option would be useful.
|
|
104
115
|
|
|
105
|
-
|
|
116
|
+
## Demonstrations
|
|
117
|
+
### Pure DTO Interface
|
|
118
|
+
The `nestia` can utilize the pure interface type as DTO.
|
|
106
119
|
|
|
107
|
-
|
|
108
|
-
```bash
|
|
109
|
-
npx nestia install
|
|
110
|
-
```
|
|
120
|
+
Unlike the legacy `@nestjs/swagger` who requires a class with decorator functions when defining the DTO, the `nestia` can use the pure interface directly. Also, `nestia` can use the descriptive comments of the pure DTO interface, too.
|
|
111
121
|
|
|
112
|
-
|
|
122
|
+
Furthermore, as the `nestia` can use the pure interface type directly, it's possible to define a generic typed DTO interface with inheritance. Of course, using alis type or union typed DTO are also possibble, too. Besides, `@nestjs/swagger` never can construct such generic typed DTO and constructing union typed DTO is possible but extremely difficult.
|
|
113
123
|
|
|
114
|
-
|
|
124
|
+
- Simple [`ISaleArticleComment`](https://github.com/samchon/nestia/tree/master/demo/simple/src/api/structures/ISaleArticleComment.ts)
|
|
125
|
+
- Generic interfaces
|
|
126
|
+
- grandparent interface, [`ISaleArticle<Content>`](https://github.com/samchon/nestia/tree/master/demo/generic/src/api/structures/ISaleArticle.ts)
|
|
127
|
+
- parent interface, [`ISaleInquiry<Content>`](https://github.com/samchon/nestia/tree/master/demo/generic/src/api/structures/ISaleInquiry.ts)
|
|
128
|
+
- 1st sub-type interface, [`ISaleQuestion`](https://github.com/samchon/nestia/tree/master/demo/generic/src/api/structures/ISaleQuestion.ts)
|
|
129
|
+
- 2nd sub-type interface, [`ISaleReview`](https://github.com/samchon/nestia/tree/master/demo/generic/src/api/structures/ISaleReview.ts)
|
|
130
|
+
- Union alias type [`ISaleEntireArticle`](https://github.com/samchon/nestia/tree/master/demo/generic/src/api/structures/ISaleArticle.ts)
|
|
115
131
|
|
|
116
|
-
|
|
117
|
-
{
|
|
118
|
-
"name": "payments-server-api",
|
|
119
|
-
"dependencies": {
|
|
120
|
-
"nestia-fetcher": "^2.0.1",
|
|
121
|
-
"typescript-is": "^0.19.0",
|
|
122
|
-
"typescript-json": "^2.0.9"
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
```
|
|
132
|
+
Looking at the below example code, then you may understand which differences between the `nestia` and `@nestjs/swagger` and what the pure interface DTO type means. Writing the legacy DTO class of the `@nestjs/swagger` after a very long time, I felt the feeling again, "this is insane".
|
|
126
133
|
|
|
134
|
+
> The below example code would be shown by clicking the arrow button or text.
|
|
127
135
|
|
|
136
|
+
<details>
|
|
137
|
+
<summary>
|
|
138
|
+
Pure DTO interface, of the <code>nestia</code>
|
|
139
|
+
</summary>
|
|
128
140
|
|
|
129
|
-
## Advanced
|
|
130
|
-
### `nestia.config.ts`
|
|
131
141
|
```typescript
|
|
132
142
|
/**
|
|
133
|
-
*
|
|
143
|
+
* Comment wrote on a sale related article.
|
|
144
|
+
*
|
|
145
|
+
* When an article of a sale has been enrolled, all of the participants like consumers and
|
|
146
|
+
* sellers can write a comment on that article. However, when the writer is a consumer, the
|
|
147
|
+
* consumer can hide its name through the annoymous option.
|
|
148
|
+
*
|
|
149
|
+
* Also, writing a reply comment for a specific comment is possible and in that case, the
|
|
150
|
+
* {@link ISaleArticleComment.parent_id} property would be activated.
|
|
134
151
|
*
|
|
135
152
|
* @author Jeongho Nam - https://github.com/samchon
|
|
136
153
|
*/
|
|
137
|
-
export interface
|
|
154
|
+
export interface ISaleArticleComment
|
|
138
155
|
{
|
|
139
156
|
/**
|
|
140
|
-
*
|
|
157
|
+
* Primary Key.
|
|
141
158
|
*/
|
|
142
|
-
|
|
159
|
+
id: number;
|
|
143
160
|
|
|
144
161
|
/**
|
|
145
|
-
*
|
|
162
|
+
* Parent comment ID.
|
|
163
|
+
*
|
|
164
|
+
* Only When this comment has been written as a reply.
|
|
146
165
|
*/
|
|
147
|
-
|
|
166
|
+
parent_id: number | null;
|
|
148
167
|
|
|
149
168
|
/**
|
|
150
|
-
*
|
|
151
|
-
*
|
|
152
|
-
* If omitted, the configuration would follow the `tsconfig.json`.
|
|
169
|
+
* Type of the writer.
|
|
153
170
|
*/
|
|
154
|
-
|
|
171
|
+
writer_type: "seller" | "consumer";
|
|
155
172
|
|
|
156
173
|
/**
|
|
157
|
-
*
|
|
174
|
+
* Name of the writer.
|
|
158
175
|
*
|
|
159
|
-
*
|
|
160
|
-
* checked through the [typescript-is](https://github.com/woutervh-/typescript-is).
|
|
176
|
+
* When this is a type of anonymous comment, writer name would be hidden.
|
|
161
177
|
*/
|
|
162
|
-
|
|
178
|
+
writer_name: string | null;
|
|
163
179
|
|
|
164
180
|
/**
|
|
165
|
-
*
|
|
181
|
+
* Contents of the comments.
|
|
166
182
|
*
|
|
167
|
-
*
|
|
168
|
-
*
|
|
169
|
-
*
|
|
183
|
+
* When the comment writer tries to modify content, it would not modify the comment
|
|
184
|
+
* content but would be accumulated. Therefore, all of the people can read how
|
|
185
|
+
* the content has been changed.
|
|
170
186
|
*/
|
|
171
|
-
|
|
187
|
+
contents: ISaleArticleComment.IContent[];
|
|
172
188
|
|
|
173
189
|
/**
|
|
174
|
-
*
|
|
190
|
+
* Creation time.
|
|
175
191
|
*/
|
|
176
|
-
|
|
192
|
+
created_at: string;
|
|
177
193
|
}
|
|
178
|
-
export namespace
|
|
194
|
+
export namespace ISaleArticleComment
|
|
179
195
|
{
|
|
180
196
|
/**
|
|
181
|
-
*
|
|
182
|
-
* controllers.
|
|
197
|
+
* Store info.
|
|
183
198
|
*/
|
|
184
|
-
export interface
|
|
199
|
+
export interface IStore
|
|
185
200
|
{
|
|
186
201
|
/**
|
|
187
|
-
*
|
|
202
|
+
* Body of the content.
|
|
188
203
|
*/
|
|
189
|
-
|
|
204
|
+
body: string;
|
|
190
205
|
|
|
191
206
|
/**
|
|
192
|
-
*
|
|
207
|
+
* Whether to hide the writer name or not.
|
|
193
208
|
*/
|
|
194
|
-
|
|
209
|
+
annonymous: boolean;
|
|
195
210
|
}
|
|
196
211
|
|
|
197
212
|
/**
|
|
198
|
-
*
|
|
213
|
+
* Content info.
|
|
199
214
|
*/
|
|
200
|
-
export interface
|
|
215
|
+
export interface IContent
|
|
201
216
|
{
|
|
202
217
|
/**
|
|
203
|
-
*
|
|
204
|
-
*
|
|
205
|
-
* If you've configure only directory, the file name would be `swagger.json`.
|
|
206
|
-
* Otherwise you configure file name and extension, the `swagger.json` file would
|
|
207
|
-
* be renamed to what you've configured.
|
|
218
|
+
* Primary Key.
|
|
208
219
|
*/
|
|
209
|
-
|
|
220
|
+
id: string;
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Body of the content.
|
|
224
|
+
*/
|
|
225
|
+
body: string;
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Creation time.
|
|
229
|
+
*/
|
|
230
|
+
created_at: string;
|
|
210
231
|
}
|
|
211
232
|
}
|
|
212
233
|
```
|
|
234
|
+
</details>
|
|
213
235
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
236
|
+
<details>
|
|
237
|
+
<summary>
|
|
238
|
+
Legacy DTO class, of the <code>@nestjs/swagger</code>
|
|
239
|
+
</summary>
|
|
217
240
|
|
|
218
241
|
```typescript
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
assert: false
|
|
225
|
-
};
|
|
226
|
-
export default config;
|
|
227
|
-
```
|
|
228
|
-
|
|
229
|
-
> Alternative options for the regular NestJS project:
|
|
230
|
-
>
|
|
231
|
-
> ```typescript
|
|
232
|
-
> export = {
|
|
233
|
-
> input: "src/**/*.controller.ts",
|
|
234
|
-
> /* input: {
|
|
235
|
-
> include: ["src/controllers/**\/*.controller.ts"],
|
|
236
|
-
> exclude: ["src/controllers/**\/fake_*.controller.ts"]
|
|
237
|
-
> },*/
|
|
238
|
-
> output: "src/api",
|
|
239
|
-
> assert: true
|
|
240
|
-
> }
|
|
241
|
-
> ```
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
### Recommended Structures
|
|
246
|
-
When developing a NestJS backend server with this [nestia](https://github.com/samchon/nestia), I recommend you to follow below directory structure. The key princinple of below structure is to gathering all of the DTO interface structures into the `src/api/structures` directory and gather all of the controller classes into the `src/controllers` directory.
|
|
242
|
+
export class SaleArticleComment
|
|
243
|
+
{
|
|
244
|
+
@ApiProperty({
|
|
245
|
+
description:
|
|
246
|
+
`Comment wrote on a sale related article.
|
|
247
247
|
|
|
248
|
-
|
|
248
|
+
When an article of a sale has been enrolled, all of the participants like consumers and sellers can write a comment on that article. However, when the writer is a consumer, the consumer can hide its name through the annoymous option.
|
|
249
249
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
- **structures**: DTO structures
|
|
254
|
-
- controllers
|
|
255
|
-
- providers
|
|
256
|
-
- models
|
|
257
|
-
- **test**: Test automation program using SDK functions
|
|
258
|
-
- package.json
|
|
259
|
-
- tsconfig.json
|
|
260
|
-
- nestia.config.ts
|
|
250
|
+
Also, writing a reply comment for a specific comment is possible and in that case, the ISaleArticleComment.parent_id property would be activated.`
|
|
251
|
+
})
|
|
252
|
+
id: number;
|
|
261
253
|
|
|
262
|
-
|
|
254
|
+
@ApiProperty({
|
|
255
|
+
type: "number",
|
|
256
|
+
nullable: true,
|
|
257
|
+
description:
|
|
258
|
+
`Parent comment ID.
|
|
263
259
|
|
|
264
|
-
|
|
260
|
+
Only When this comment has been written as a reply.`
|
|
261
|
+
})
|
|
262
|
+
parent_id: number | null;
|
|
265
263
|
|
|
264
|
+
@ApiProperty({
|
|
265
|
+
type: "string",
|
|
266
|
+
description: "Type of the writer."
|
|
267
|
+
})
|
|
268
|
+
writer_type: "seller" | "consumer";
|
|
266
269
|
|
|
270
|
+
@ApiProperty({
|
|
271
|
+
type: "string",
|
|
272
|
+
nullable: true,
|
|
273
|
+
description:
|
|
274
|
+
`Name of the writer.
|
|
267
275
|
|
|
276
|
+
When this is a type of anonymous comment, writer name would be hidden.`
|
|
277
|
+
})
|
|
278
|
+
writer_name: string | null;
|
|
268
279
|
|
|
269
|
-
|
|
270
|
-
|
|
280
|
+
@ApiProperty({
|
|
281
|
+
type: "array",
|
|
282
|
+
items: {
|
|
283
|
+
schema: { $ref: getSchemaPath(SaleArticleComment.Content) }
|
|
284
|
+
},
|
|
285
|
+
description:
|
|
286
|
+
`Contents of the comments.
|
|
271
287
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
- [SDK generated by this **nestia**](https://github.com/samchon/nestia/tree/master/demo/simple/src/api/functional/consumers/sales/articles/comments/index.ts)
|
|
276
|
-
- [`swagger.json` generated by this **nestia**](https://github.com/samchon/nestia/tree/master/demo/simple/swagger.json)
|
|
277
|
-
- Demonstration Projects
|
|
278
|
-
- [encrypt](https://github.com/samchon/nestia/tree/master/demo/encrypt): Request and response body are fully encrypted
|
|
279
|
-
- [generic](https://github.com/samchon/nestia/tree/master/demo/generic): Generic typed controller classes
|
|
280
|
-
- [recursive](https://github.com/samchon/nestia/tree/master/demo/recursive): Recursive DTO interface, [swagger editor](https://editor.swagger.io) can't expresss it
|
|
281
|
-
- [simple](https://github.com/samchon/nestia/tree/master/demo/simple): Simple DTO interface and controller class
|
|
282
|
-
- [union](https://github.com/samchon/nestia/tree/master/demo/union): Only [nestia](https://github.com/samchon/nestia) can handle the union typed DTO interface
|
|
288
|
+
When the comment writer tries to modify content, it would not modify the comment content but would be accumulated Therefore, all of the people can read how the content has been changed.`
|
|
289
|
+
})
|
|
290
|
+
contents: SaleArticleComment.Content[];
|
|
283
291
|
|
|
284
|
-
|
|
285
|
-
|
|
292
|
+
@ApiProperty({
|
|
293
|
+
description: "Creation time."
|
|
294
|
+
})
|
|
295
|
+
created_at: string;
|
|
296
|
+
}
|
|
297
|
+
```
|
|
298
|
+
</details>
|
|
286
299
|
|
|
287
|
-
You dont' need to define any extra comment or decorator function to make the DTO (Data Transfer Object). Just define the DTO as a pure interface structure, then [nestia](https://github.com/samchon/nestia) will do everything instead of you.
|
|
288
300
|
|
|
289
|
-
If you're afraiding because your type is union or intersection, I can say that it does not matter. Even when generic or conditional type comes, it does not matter. Just enjoy the pure TypeScript type.
|
|
290
301
|
|
|
291
|
-
```typescript
|
|
292
|
-
/**
|
|
293
|
-
* Comment wrote on a sale related article.
|
|
294
|
-
*
|
|
295
|
-
* @author Jeongho Nam - https://github.com/samchon
|
|
296
|
-
*/
|
|
297
|
-
export interface ISaleComment
|
|
298
|
-
{
|
|
299
|
-
/**
|
|
300
|
-
* Primary Key.
|
|
301
|
-
*/
|
|
302
|
-
id: number;
|
|
303
302
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
*/
|
|
307
|
-
writer_type: "seller" | "consumer";
|
|
303
|
+
### Advanced Controller Class
|
|
304
|
+
Controller also can use the generic arguments.
|
|
308
305
|
|
|
309
|
-
|
|
310
|
-
* Name of the writer.
|
|
311
|
-
*/
|
|
312
|
-
writer_name: string;
|
|
306
|
+
In the previous [Pure DTO Interface](#pure-dto-interface) corner, we've learned that the `nestia` can use the pure interface type as the DTO. Also, we've learned that using the pure interface type as DTO means that making generic typed interface or union typed interface as the DTO are also possible, too.
|
|
313
307
|
|
|
314
|
-
|
|
315
|
-
* Contents of the comments.
|
|
316
|
-
*
|
|
317
|
-
* When the comment writer tries to modify content, it would not modify the comment
|
|
318
|
-
* content but would be accumulated. Therefore, all of the people can read how
|
|
319
|
-
* the content has been changed.
|
|
320
|
-
*/
|
|
321
|
-
contents: ISaleComment.IContent[];
|
|
308
|
+
In the Controller case, it's the same with the upper interface story. In the `nestia`, as using generic typed interface as DTO was possible, defining generic typed controller class is also possible, too. By defining the generic typed controller class as the super type class, you can extremely reduce both duplicated code and duplicated description comments.
|
|
322
309
|
|
|
323
|
-
|
|
324
|
-
* Creation time.
|
|
325
|
-
*/
|
|
326
|
-
created_at: string;
|
|
327
|
-
}
|
|
310
|
+
Look at the below code and feel how the `nestia` is powerful. I repeat that, `@nestjs/swagger` never can construct such generic or union typed controller classes, either.
|
|
328
311
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
{
|
|
336
|
-
/**
|
|
337
|
-
* Body of the content.
|
|
338
|
-
*/
|
|
339
|
-
body: string;
|
|
340
|
-
}
|
|
312
|
+
- Simple [`CustomerSaleArticleCommentsController`](https://github.com/samchon/nestia/blob/master/demo/simple/src/controllers/ConsumerSaleArticleCommentsController.ts)
|
|
313
|
+
- Generic controllers
|
|
314
|
+
- abstract controller, [`SaleInquiriesController<Content, Store, Json>`](https://github.com/samchon/nestia/tree/master/demo/generic/src/controllers/SaleInquiriesController.ts)
|
|
315
|
+
- 1st sub-type controller, [`ConsumerSaleQuestionsController`](https://github.com/samchon/nestia/tree/master/demo/generic/src/controllers/ConsumerSaleQuestionsController.ts)
|
|
316
|
+
- 2nd sub-type controller, [`ConsumerSaleQuestionsController`](https://github.com/samchon/nestia/tree/master/demo/generic/src/controllers/ConsumerSaleQuestionsController.ts)
|
|
317
|
+
- Union controller, [`ConsumerSaleEntireArticlesController`](https://github.com/samchon/nestia/tree/master/demo/union/src/controllers/ConsumerSaleEntireArticlesController.ts)
|
|
341
318
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
{
|
|
347
|
-
/**
|
|
348
|
-
* Creation time.
|
|
349
|
-
*/
|
|
350
|
-
created_at: string;
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
```
|
|
319
|
+
```typescript
|
|
320
|
+
import * as express from "express";
|
|
321
|
+
import * as nest from "@nestjs/common";
|
|
322
|
+
import helper from "nestia-helper";
|
|
354
323
|
|
|
355
|
-
|
|
356
|
-
If you've decided to adapt this [nestia](https://github.com/samchon/nestia) and you want to generate the SDK directly, you don't need any extra work. Just keep you controller class down and do noting. The only one exceptional case that you need an extra dedication is, when you want to explain about the API function to the client developers through the comments.
|
|
324
|
+
import { ISaleInquiry } from "@api/structures/ISaleInquiry";
|
|
357
325
|
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
326
|
+
export abstract class SaleInquiriesController<
|
|
327
|
+
Content extends ISaleInquiry.IContent,
|
|
328
|
+
Store extends ISaleInquiry.IStore,
|
|
329
|
+
Json extends ISaleInquiry<Content>>
|
|
361
330
|
{
|
|
362
331
|
/**
|
|
363
|
-
* Store a new
|
|
332
|
+
* Store a new inquiry.
|
|
333
|
+
*
|
|
334
|
+
* Write a new article inquirying about a sale.
|
|
364
335
|
*
|
|
365
336
|
* @param request Instance of the Express.Request
|
|
366
337
|
* @param section Code of the target section
|
|
367
338
|
* @param saleId ID of the target sale
|
|
368
339
|
* @param input Content to archive
|
|
340
|
+
* @return Newly archived inquiry
|
|
369
341
|
*
|
|
370
|
-
* @return Newly archived question
|
|
371
342
|
* @throw 400 bad request error when type of the input data is not valid
|
|
372
343
|
* @throw 401 unauthorized error when you've not logged in yet
|
|
373
344
|
*/
|
|
@@ -375,40 +346,101 @@ export class ConsumerSaleQuestionsController
|
|
|
375
346
|
public store
|
|
376
347
|
(
|
|
377
348
|
@nest.Request() request: express.Request,
|
|
378
|
-
@
|
|
379
|
-
@
|
|
380
|
-
@nest.Body() input:
|
|
381
|
-
): Promise<
|
|
349
|
+
@helper.TypedParam("section", "string") section: string,
|
|
350
|
+
@helper.TypedParam("saleId", "string") saleId: string,
|
|
351
|
+
@nest.Body() input: Store
|
|
352
|
+
): Promise<Json>;
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Update an inquiry.
|
|
356
|
+
*
|
|
357
|
+
* Update ordinary inquiry article. However, it would not modify the content reocrd
|
|
358
|
+
* {@link ISaleInquiry.IContent}, but be accumulated into the {@link ISaleInquiry.contents}.
|
|
359
|
+
* Therefore, all of the poeple can read how the content has been changed.
|
|
360
|
+
*
|
|
361
|
+
* @param request Instance of the Express.Request
|
|
362
|
+
* @param section Code of the target section
|
|
363
|
+
* @param saleId ID of the target sale
|
|
364
|
+
* @param id ID of the target article to be updated
|
|
365
|
+
* @param input New content to be overwritten
|
|
366
|
+
* @return The newly created content record
|
|
367
|
+
*
|
|
368
|
+
* @throw 400 bad request error when type of the input data is not valid
|
|
369
|
+
* @throw 401 unauthorized error when you've not logged in yet
|
|
370
|
+
* @throw 403 forbidden error when the article is not yours
|
|
371
|
+
*/
|
|
372
|
+
@nest.Put(":id")
|
|
373
|
+
public update
|
|
374
|
+
(
|
|
375
|
+
@nest.Request() request: express.Request,
|
|
376
|
+
@helper.TypedParam("section", "string") section: string,
|
|
377
|
+
@helper.TypedParam("saleId", "string") saleId: string,
|
|
378
|
+
@helper.TypedParam("id", "number") id: number,
|
|
379
|
+
@nest.Body() input: Store
|
|
380
|
+
): Promise<Json>;
|
|
382
381
|
}
|
|
383
382
|
```
|
|
384
383
|
|
|
385
|
-
### SDK
|
|
386
|
-
When you run the [nestia](https://github.com/samchon/nestia) up using the upper controller class `ConsumerSaleQuestionsController`, the [nestia](https://github.com/samchon/nestia) would generate below function for the client developers, by analyzing the `ConsumerSaleQuestionsController` class in the compilation and runtime level.
|
|
387
384
|
|
|
388
|
-
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
### Software Development Kit
|
|
388
|
+
> `Swagger` is torturing the client developers.
|
|
389
|
+
>
|
|
390
|
+
> If you're a backend developer and you deliver a `Swagger` to your companion client developers, the client developers should analyze the `Swagger` and implement duplicated router functions with DTO interfaces by themselves. During those jobs, if the client developers take a mistake by mis-reading the `Swagger`, it becomes the critical runtime error directly.
|
|
391
|
+
>
|
|
392
|
+
> Why do you torture the client developers such like that? If you deliver an SDK (Software Development Kit) instead of the `Swagger`, the client developers don't need to read the `Swagger` file and don't need to implement the duplicated DTO interfaces and router functions, either.
|
|
393
|
+
>
|
|
394
|
+
> Therefore, just build the SDK through this `nestia` and delivers the SDK. Your client developers would be anticipated from the long time torturing and become happy. Your solution would be much more reliable and efficient, too.
|
|
395
|
+
|
|
396
|
+
Looking at the SDK library file, generated by the `nestia`, it seems perfect.
|
|
397
|
+
|
|
398
|
+
Exact route method, path and parameters are constructed and DTO structures are perfectly imported. Also, descriptive comments written on the controller class methods, DTO interfaces and their properties are exactly revied in the SDK library.
|
|
399
|
+
|
|
400
|
+
Furthermore, there's not any problem even when the generic typed abstract controller classes with generic typed DTO comes. The `nestia` will specialize the generic arguments exactly, by analyzing your `NestJS` developed backend server code, in the compilation level.
|
|
401
|
+
|
|
402
|
+
- [simple/.../comments/index.ts](https://github.com/samchon/nestia/blob/master/demo/simple/src/api/functional/consumers/sales/articles/comments/index.ts)
|
|
403
|
+
- [generic/.../questions/index.ts](https://github.com/samchon/nestia/tree/master/demo/generic/src/api/functional/consumers/sales/questions/index.ts)
|
|
404
|
+
- [generic/.../reviews/index.ts](https://github.com/samchon/nestia/tree/master/demo/generic/src/api/functional/consumers/sales/reviews/index.ts)
|
|
405
|
+
- [union/.../entire_articles/index.ts](https://github.com/samchon/nestia/tree/master/demo/union/src/api/functional/consumers/sales/entire_articles/index.ts)
|
|
389
406
|
|
|
390
407
|
```typescript
|
|
391
408
|
/**
|
|
392
|
-
*
|
|
409
|
+
* @packageDocumentation
|
|
410
|
+
* @module api.functional.consumers.sales.reviews
|
|
411
|
+
* @nestia Generated by Nestia - https://github.com/samchon/nestia
|
|
412
|
+
*/
|
|
413
|
+
//================================================================
|
|
414
|
+
import { Fetcher, Primitive } from "nestia-fetcher";
|
|
415
|
+
import type { IConnection } from "nestia-fetcher";
|
|
416
|
+
import { createStringifier } from "typescript-json";
|
|
417
|
+
|
|
418
|
+
import type { ISaleReview } from "./../../../../structures/ISaleReview";
|
|
419
|
+
import type { ISaleInquiry } from "./../../../../structures/ISaleInquiry";
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* Store a new inquiry.
|
|
423
|
+
*
|
|
424
|
+
* Write a new article inquirying about a sale.
|
|
393
425
|
*
|
|
394
426
|
* @param connection connection Information of the remote HTTP(s) server with headers (+encryption password)
|
|
395
427
|
* @param request Instance of the Express.Request
|
|
396
428
|
* @param section Code of the target section
|
|
397
429
|
* @param saleId ID of the target sale
|
|
398
430
|
* @param input Content to archive
|
|
399
|
-
* @return Newly archived
|
|
431
|
+
* @return Newly archived inquiry
|
|
400
432
|
* @throw 400 bad request error when type of the input data is not valid
|
|
401
433
|
* @throw 401 unauthorized error when you've not logged in yet
|
|
402
434
|
*
|
|
435
|
+
* @controller ConsumerSaleReviewsController.store()
|
|
436
|
+
* @path POST /consumers/:section/sales/:saleId/reviews
|
|
403
437
|
* @nestia Generated by Nestia - https://github.com/samchon/nestia
|
|
404
|
-
* @controller ConsumerSaleQuestionsController.store()
|
|
405
|
-
* @path POST /consumers/:section/sales/:saleId/questions/
|
|
406
438
|
*/
|
|
407
439
|
export function store
|
|
408
440
|
(
|
|
409
441
|
connection: IConnection,
|
|
410
442
|
section: string,
|
|
411
|
-
saleId:
|
|
443
|
+
saleId: string,
|
|
412
444
|
input: Primitive<store.Input>
|
|
413
445
|
): Promise<store.Output>
|
|
414
446
|
{
|
|
@@ -418,249 +450,316 @@ export function store
|
|
|
418
450
|
store.ENCRYPTED,
|
|
419
451
|
store.METHOD,
|
|
420
452
|
store.path(section, saleId),
|
|
421
|
-
input
|
|
453
|
+
input,
|
|
454
|
+
store.stringify
|
|
422
455
|
);
|
|
423
456
|
}
|
|
424
457
|
export namespace store
|
|
425
458
|
{
|
|
426
|
-
export type Input = Primitive<
|
|
427
|
-
export type Output = Primitive<ISaleInquiry<
|
|
459
|
+
export type Input = Primitive<ISaleReview.IStore>;
|
|
460
|
+
export type Output = Primitive<ISaleInquiry<ISaleReview.IContent>>;
|
|
428
461
|
|
|
429
462
|
export const METHOD = "POST" as const;
|
|
430
|
-
export const PATH: string = "/consumers/:section/sales/:saleId/
|
|
463
|
+
export const PATH: string = "/consumers/:section/sales/:saleId/reviews";
|
|
431
464
|
export const ENCRYPTED: Fetcher.IEncrypted = {
|
|
432
|
-
request:
|
|
433
|
-
response:
|
|
465
|
+
request: false,
|
|
466
|
+
response: false,
|
|
434
467
|
};
|
|
435
468
|
|
|
436
|
-
export function path(section: string, saleId:
|
|
469
|
+
export function path(section: string, saleId: string): string
|
|
437
470
|
{
|
|
438
|
-
return `/consumers/${section}/sales/${saleId}/
|
|
471
|
+
return `/consumers/${section}/sales/${saleId}/reviews`;
|
|
439
472
|
}
|
|
473
|
+
export const stringify = createStringifier<Input>();
|
|
440
474
|
}
|
|
441
|
-
```
|
|
442
475
|
|
|
443
|
-
|
|
444
|
-
|
|
476
|
+
/**
|
|
477
|
+
* Update an inquiry.
|
|
478
|
+
*
|
|
479
|
+
* Update ordinary inquiry article. However, it would not modify the content reocrd
|
|
480
|
+
* {@link ISaleInquiry.IContent}, but be accumulated into the {@link ISaleInquiry.contents}.
|
|
481
|
+
* Therefore, all of the poeple can read how the content has been changed.
|
|
482
|
+
*
|
|
483
|
+
* @param connection connection Information of the remote HTTP(s) server with headers (+encryption password)
|
|
484
|
+
* @param request Instance of the Express.Request
|
|
485
|
+
* @param section Code of the target section
|
|
486
|
+
* @param saleId ID of the target sale
|
|
487
|
+
* @param id ID of the target article to be updated
|
|
488
|
+
* @param input New content to be overwritten
|
|
489
|
+
* @return The newly created content record
|
|
490
|
+
* @throw 400 bad request error when type of the input data is not valid
|
|
491
|
+
* @throw 401 unauthorized error when you've not logged in yet
|
|
492
|
+
* @throw 403 forbidden error when the article is not yours
|
|
493
|
+
*
|
|
494
|
+
* @controller ConsumerSaleReviewsController.update()
|
|
495
|
+
* @path PUT /consumers/:section/sales/:saleId/reviews/:id
|
|
496
|
+
* @nestia Generated by Nestia - https://github.com/samchon/nestia
|
|
497
|
+
*/
|
|
498
|
+
export function update
|
|
499
|
+
(
|
|
500
|
+
connection: IConnection,
|
|
501
|
+
section: string,
|
|
502
|
+
saleId: string,
|
|
503
|
+
id: number,
|
|
504
|
+
input: Primitive<update.Input>
|
|
505
|
+
): Promise<update.Output>
|
|
506
|
+
{
|
|
507
|
+
return Fetcher.fetch
|
|
508
|
+
(
|
|
509
|
+
connection,
|
|
510
|
+
update.ENCRYPTED,
|
|
511
|
+
update.METHOD,
|
|
512
|
+
update.path(section, saleId, id),
|
|
513
|
+
input,
|
|
514
|
+
update.stringify
|
|
515
|
+
);
|
|
516
|
+
}
|
|
517
|
+
export namespace update
|
|
518
|
+
{
|
|
519
|
+
export type Input = Primitive<ISaleReview.IStore>;
|
|
520
|
+
export type Output = Primitive<ISaleInquiry<ISaleReview.IContent>>;
|
|
445
521
|
|
|
446
|
-
|
|
522
|
+
export const METHOD = "PUT" as const;
|
|
523
|
+
export const PATH: string = "/consumers/:section/sales/:saleId/reviews/:id";
|
|
524
|
+
export const ENCRYPTED: Fetcher.IEncrypted = {
|
|
525
|
+
request: false,
|
|
526
|
+
response: false,
|
|
527
|
+
};
|
|
447
528
|
|
|
448
|
-
|
|
449
|
-
{
|
|
450
|
-
|
|
451
|
-
"/consumers/{section}/sales/{saleId}/comments/{articleId}": {
|
|
452
|
-
"post": {
|
|
453
|
-
"tags": [],
|
|
454
|
-
"parameters": [
|
|
455
|
-
{
|
|
456
|
-
"name": "section",
|
|
457
|
-
"in": "path",
|
|
458
|
-
"description": "Code of the target section",
|
|
459
|
-
"schema": {
|
|
460
|
-
"type": "string",
|
|
461
|
-
"nullable": false
|
|
462
|
-
},
|
|
463
|
-
"required": true
|
|
464
|
-
},
|
|
465
|
-
{
|
|
466
|
-
"name": "saleId",
|
|
467
|
-
"in": "path",
|
|
468
|
-
"description": "ID of the target sale",
|
|
469
|
-
"schema": {
|
|
470
|
-
"type": "number",
|
|
471
|
-
"nullable": false
|
|
472
|
-
},
|
|
473
|
-
"required": true
|
|
474
|
-
},
|
|
475
|
-
{
|
|
476
|
-
"name": "articleId",
|
|
477
|
-
"in": "path",
|
|
478
|
-
"description": "ID of the target article",
|
|
479
|
-
"schema": {
|
|
480
|
-
"type": "number",
|
|
481
|
-
"nullable": false
|
|
482
|
-
},
|
|
483
|
-
"required": true
|
|
484
|
-
}
|
|
485
|
-
],
|
|
486
|
-
"requestBody": {
|
|
487
|
-
"description": "Content to write",
|
|
488
|
-
"content": {
|
|
489
|
-
"application/json": {
|
|
490
|
-
"schema": {
|
|
491
|
-
"$ref": "#/components/schemas/ISaleComment.IStore"
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
},
|
|
495
|
-
"required": true
|
|
496
|
-
},
|
|
497
|
-
"responses": {
|
|
498
|
-
"201": {
|
|
499
|
-
"description": "Newly archived comment",
|
|
500
|
-
"content": {
|
|
501
|
-
"application/json": {
|
|
502
|
-
"schema": {
|
|
503
|
-
"$ref": "#/components/schemas/ISaleComment"
|
|
504
|
-
}
|
|
505
|
-
}
|
|
506
|
-
}
|
|
507
|
-
},
|
|
508
|
-
"400": {
|
|
509
|
-
"description": "bad request error when type of the input data is not valid"
|
|
510
|
-
},
|
|
511
|
-
"401": {
|
|
512
|
-
"description": "unauthorized error when you've not logged in yet"
|
|
513
|
-
},
|
|
514
|
-
"403": {
|
|
515
|
-
"description": "forbidden error when you're a seller and the sale is not yours"
|
|
516
|
-
},
|
|
517
|
-
"404": {
|
|
518
|
-
"description": "not found error when unable to find the matched record"
|
|
519
|
-
}
|
|
520
|
-
},
|
|
521
|
-
"description": "Store a new comment."
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
},
|
|
525
|
-
"components": {
|
|
526
|
-
"schemas": {
|
|
527
|
-
"ISaleComment": {
|
|
528
|
-
"type": "object",
|
|
529
|
-
"properties": {
|
|
530
|
-
"id": {
|
|
531
|
-
"description": "Primary Key.",
|
|
532
|
-
"type": "number",
|
|
533
|
-
"nullable": false
|
|
534
|
-
},
|
|
535
|
-
"writer_type": {
|
|
536
|
-
"description": "Type of the writer.",
|
|
537
|
-
"type": "string",
|
|
538
|
-
"nullable": false
|
|
539
|
-
},
|
|
540
|
-
"writer_name": {
|
|
541
|
-
"description": "Name of the writer.",
|
|
542
|
-
"type": "string",
|
|
543
|
-
"nullable": false
|
|
544
|
-
},
|
|
545
|
-
"contents": {
|
|
546
|
-
"description": "Contents of the comments.\n\nWhen the comment writer tries to modify content, it would not modify the comment\ncontent but would be accumulated. Therefore, all of the people can read how\nthe content has been changed.",
|
|
547
|
-
"type": "array",
|
|
548
|
-
"items": {
|
|
549
|
-
"$ref": "#/components/schemas/ISaleComment.IContent"
|
|
550
|
-
},
|
|
551
|
-
"nullable": false
|
|
552
|
-
},
|
|
553
|
-
"created_at": {
|
|
554
|
-
"description": "Creation time.",
|
|
555
|
-
"type": "string",
|
|
556
|
-
"nullable": false
|
|
557
|
-
}
|
|
558
|
-
},
|
|
559
|
-
"nullable": false,
|
|
560
|
-
"required": [
|
|
561
|
-
"id",
|
|
562
|
-
"writer_type",
|
|
563
|
-
"writer_name",
|
|
564
|
-
"contents",
|
|
565
|
-
"created_at"
|
|
566
|
-
],
|
|
567
|
-
"description": "Comment wrote on an article."
|
|
568
|
-
},
|
|
569
|
-
"ISaleComment.IContent": {
|
|
570
|
-
"type": "object",
|
|
571
|
-
"properties": {
|
|
572
|
-
"created_at": {
|
|
573
|
-
"description": "Creation time.",
|
|
574
|
-
"type": "string",
|
|
575
|
-
"nullable": false
|
|
576
|
-
},
|
|
577
|
-
"body": {
|
|
578
|
-
"description": "Body of the content.",
|
|
579
|
-
"type": "string",
|
|
580
|
-
"nullable": false
|
|
581
|
-
}
|
|
582
|
-
},
|
|
583
|
-
"nullable": false,
|
|
584
|
-
"required": [
|
|
585
|
-
"created_at",
|
|
586
|
-
"body"
|
|
587
|
-
],
|
|
588
|
-
"description": "Content info."
|
|
589
|
-
},
|
|
590
|
-
"ISaleComment.IStore": {
|
|
591
|
-
"type": "object",
|
|
592
|
-
"properties": {
|
|
593
|
-
"body": {
|
|
594
|
-
"description": "Body of the content.",
|
|
595
|
-
"type": "string",
|
|
596
|
-
"nullable": false
|
|
597
|
-
}
|
|
598
|
-
},
|
|
599
|
-
"nullable": false,
|
|
600
|
-
"required": [
|
|
601
|
-
"body"
|
|
602
|
-
],
|
|
603
|
-
"description": "Store info."
|
|
604
|
-
}
|
|
529
|
+
export function path(section: string, saleId: string, id: number): string
|
|
530
|
+
{
|
|
531
|
+
return `/consumers/${section}/sales/${saleId}/reviews/${id}`;
|
|
605
532
|
}
|
|
606
|
-
|
|
533
|
+
export const stringify = createStringifier<Input>();
|
|
607
534
|
}
|
|
608
535
|
```
|
|
609
536
|
|
|
610
537
|
|
|
611
538
|
|
|
612
539
|
|
|
540
|
+
### Swagger
|
|
541
|
+
Building `Swagger` is also possible and even much powerful.
|
|
613
542
|
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
https://
|
|
543
|
+
Although I think the `Swagger` is a typical tool that torturing the client developers, the `nestia` also can build the `swagger.json` file. Even `Swagger` generator of the `nestia` is much powerful and convenient than the `@nestjs/swagger`. It doesn't require any type of the swagger comment or DTO decorator function and just using the pure interface type as DTO is possible.
|
|
544
|
+
|
|
545
|
+
Looking at the [simple/swagger.json](https://editor.swagger.io/?url=https%3A%2F%2Fraw.githubusercontent.com%2Fsamchon%2Fnestia%2Fmaster%2Fdemo%2Fsimple%2Fswagger.json) file, generated by the `nestia`, it seems perfect. Exact route method, path and parameters are constructed and DTO structures are exactly same with the pure interace type `ISaleArticleComment`. Also, comments written on the controller class method, DTO interface `ISaleArticleComment` and its properties are exactly revied on the `description` fields in the `swagger.json`.
|
|
546
|
+
|
|
547
|
+
Looking at the another file [generic/swagger.json](https://editor.swagger.io/?url=https%3A%2F%2Fraw.githubusercontent.com%2Fsamchon%2Fnestia%2Fmaster%2Fdemo%2Fgeneric%2Fswagger.json), you can find that there isn't any problem even when the generic typed DTO interfaces or controller classes come. Furthermore, traveling the `description` properties of the [generic/swagger.json](https://editor.swagger.io/?url=https%3A%2F%2Fraw.githubusercontent.com%2Fsamchon%2Fnestia%2Fmaster%2Fdemo%2Funion%2Fswagger.json), comments written on the generic interfaces and controllers are exactly revived in each route functions and schema definitions.
|
|
548
|
+
|
|
549
|
+
- View in the `Swagger Editor`
|
|
550
|
+
- [simple/swagger.json](https://editor.swagger.io/?url=https%3A%2F%2Fraw.githubusercontent.com%2Fsamchon%2Fnestia%2Fmaster%2Fdemo%2Fsimple%2Fswagger.json)
|
|
551
|
+
- [generic/swagger.json](https://editor.swagger.io/?url=https%3A%2F%2Fraw.githubusercontent.com%2Fsamchon%2Fnestia%2Fmaster%2Fdemo%2Fgeneric%2Fswagger.json)
|
|
552
|
+
- [union/swagger.json](https://editor.swagger.io/?url=https%3A%2F%2Fraw.githubusercontent.com%2Fsamchon%2Fnestia%2Fmaster%2Fdemo%2Funion%2Fswagger.json)
|
|
553
|
+
|
|
554
|
+

|
|
555
|
+
|
|
556
|
+
|
|
557
|
+
|
|
558
|
+
|
|
559
|
+
## Configuration
|
|
560
|
+
Components | `nestia.config.ts` | `CLI` | `@nestjs/swagger`
|
|
561
|
+
-----------------------------|--------------------|-------|------------------
|
|
562
|
+
Swagger Generation | ✔ | ✔ | ✔
|
|
563
|
+
SDK Generation | ✔ | ✔ | ❌
|
|
564
|
+
2x faster `JSON.stringify()` | ✔ | ❌ | ❌
|
|
565
|
+
Type check in runtime | ✔ | ❌ | ❌
|
|
566
|
+
Custom compiler options | ✔ | ❌ | ❌
|
|
567
|
+
|
|
568
|
+
You can configure the nestia builder options by two ways: `CLI` or `configuration file`.
|
|
569
|
+
|
|
570
|
+
The CLI (Command Line Interface) is easy to use, because it does not require any type of configuration file wrinting. However, it's not safe thing that repeating the same command whenever generating the SDK or `swagger.json` through the CLI, because command miswriting can be happened.
|
|
571
|
+
|
|
572
|
+
```bash
|
|
573
|
+
# BASIC COMMAND
|
|
574
|
+
npx nestia <nestia|swagger> <source_directories_or_patterns> \
|
|
575
|
+
--exclude <exclude_directory_or_pattern> \
|
|
576
|
+
--out <output_directory_or_file>
|
|
577
|
+
|
|
578
|
+
# EXAMPLES
|
|
579
|
+
npx nestia nestia "src/controllers" --out "src/api"
|
|
580
|
+
npx nestia swagger "src/**/*.controller.ts" -- out "swagger.json"
|
|
581
|
+
npx nestia swagger "src/main/controllers" "src/sub/controllers" \
|
|
582
|
+
--exclude "src/main/test" \
|
|
583
|
+
--out "composite.swagger.json"
|
|
584
|
+
|
|
585
|
+
# ONLY WHEN NESTIA.CONFIG.TS EXISTS
|
|
586
|
+
npx nestia nestia
|
|
587
|
+
npx nestia swagger
|
|
588
|
+
```
|
|
589
|
+
|
|
590
|
+
Otherwise, configuration file `nestia.config.ts` may hard to construct and the construction would be annoying and inconvenient, such configuration would be useful within framework of the reusability. Also, the configuration file `nestia.config.ts` use much detailed options than the `CLI` and its content can be ensured its safety by TypeScript compiler through the `IConfigruation` interface type.
|
|
591
|
+
|
|
592
|
+
```typescript
|
|
593
|
+
/**
|
|
594
|
+
* Definition for the `nestia.config.ts` file.
|
|
595
|
+
*
|
|
596
|
+
* @author Jeongho Nam - https://github.com/samchon
|
|
597
|
+
*/
|
|
598
|
+
export interface IConfiguration
|
|
599
|
+
{
|
|
600
|
+
/**
|
|
601
|
+
* List of files or directories containing the NestJS controller classes.
|
|
602
|
+
*/
|
|
603
|
+
input: string | string[] | IConfiguration.IInput;
|
|
617
604
|
|
|
618
|
-
|
|
605
|
+
/**
|
|
606
|
+
* Output directory that SDK would be placed in.
|
|
607
|
+
*
|
|
608
|
+
* If not configured, you can't build the SDK library.
|
|
609
|
+
*/
|
|
610
|
+
output?: string;
|
|
611
|
+
|
|
612
|
+
/**
|
|
613
|
+
* Compiler options for the TypeScript.
|
|
614
|
+
*
|
|
615
|
+
* If you've omitted this property or the assigned property cannot fully cover the
|
|
616
|
+
* `tsconfig.json`, the properties from the `tsconfig.json` would be assigned to here.
|
|
617
|
+
* Otherwise, this property has been configured and it's detailed values are different
|
|
618
|
+
* with the `tsconfig.json`, this property values would be overwritten.
|
|
619
|
+
*
|
|
620
|
+
* ```typescript
|
|
621
|
+
* import ts from "typescript";
|
|
622
|
+
*
|
|
623
|
+
* const tsconfig: ts.TsConfig;
|
|
624
|
+
* const nestiaConfig: IConfiguration;
|
|
625
|
+
*
|
|
626
|
+
* const compilerOptions: ts.CompilerOptions = {
|
|
627
|
+
* ...tsconfig.compilerOptions,
|
|
628
|
+
* ...(nestiaConfig.compilerOptions || {})
|
|
629
|
+
* }
|
|
630
|
+
* ```
|
|
631
|
+
*/
|
|
632
|
+
compilerOptions?: ts.CompilerOptions;
|
|
633
|
+
|
|
634
|
+
/**
|
|
635
|
+
* Whether to assert parameter types or not.
|
|
636
|
+
*
|
|
637
|
+
* If you configure this property to be `true`, all of the function parameters would be
|
|
638
|
+
* checked through the [typescript-is](https://github.com/woutervh-/typescript-is). This
|
|
639
|
+
* option would make your SDK library slower, but would be much safer in the type level
|
|
640
|
+
* even in the runtime environment.
|
|
641
|
+
*/
|
|
642
|
+
assert?: boolean;
|
|
643
|
+
|
|
644
|
+
/**
|
|
645
|
+
* Whether to optimize JSON string conversion 2x faster or not.
|
|
646
|
+
*
|
|
647
|
+
* If you configure this property to be `true`, the SDK library would utilize the
|
|
648
|
+
* [typescript-json](https://github.com/samchon/typescript-json) and the JSON string
|
|
649
|
+
* conversion speed really be 2x faster.
|
|
650
|
+
*/
|
|
651
|
+
json?: boolean;
|
|
652
|
+
|
|
653
|
+
/**
|
|
654
|
+
* Building `swagger.json` is also possible.
|
|
655
|
+
*
|
|
656
|
+
* If not specified, you can't build the `swagger.json`.
|
|
657
|
+
*/
|
|
658
|
+
swagger?: IConfiguration.ISwagger;
|
|
659
|
+
}
|
|
660
|
+
export namespace IConfiguration
|
|
661
|
+
{
|
|
662
|
+
/**
|
|
663
|
+
* List of files or directories to include or exclude to specifying the NestJS
|
|
664
|
+
* controllers.
|
|
665
|
+
*/
|
|
666
|
+
export interface IInput
|
|
667
|
+
{
|
|
668
|
+
/**
|
|
669
|
+
* List of files or directories containing the NestJS controller classes.
|
|
670
|
+
*/
|
|
671
|
+
include: string[];
|
|
672
|
+
|
|
673
|
+
/**
|
|
674
|
+
* List of files or directories to be excluded.
|
|
675
|
+
*/
|
|
676
|
+
exclude?: string[];
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
/**
|
|
680
|
+
* Building `swagger.json` is also possible.
|
|
681
|
+
*/
|
|
682
|
+
export interface ISwagger
|
|
683
|
+
{
|
|
684
|
+
/**
|
|
685
|
+
* Output path of the `swagger.json`.
|
|
686
|
+
*
|
|
687
|
+
* If you've configured only directory, the file name would be the `swagger.json`.
|
|
688
|
+
* Otherwise you've configured the full path with file name and extension, the
|
|
689
|
+
* `swagger.json` file would be renamed to it.
|
|
690
|
+
*/
|
|
691
|
+
output: string;
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
```
|
|
695
|
+
|
|
696
|
+
When you've completed to reading the `IConfiguration` interface, let's make the `nestia.config.ts` file directly. At first, move to your project directory. Note that the project directory means the top level directory of your project, where configuration files like `package.json` or `tsconfig.json` are placed in. After the movement, make the new `nestia` configuration file and write some script like below:
|
|
619
697
|
|
|
620
|
-
|
|
698
|
+
```typescript
|
|
699
|
+
import type { IConfiguration } from "nestia";
|
|
700
|
+
|
|
701
|
+
export const NESTIA_CONFIG: IConfiguration = {
|
|
702
|
+
input: "./src/controllers",
|
|
703
|
+
output: "./src/api",
|
|
704
|
+
json: true,
|
|
705
|
+
swagger: {
|
|
706
|
+
output: "./public/swagger.json"
|
|
707
|
+
}
|
|
708
|
+
};
|
|
709
|
+
export default NESTIA_CONFIG;
|
|
710
|
+
```
|
|
621
711
|
|
|
622
|
-
Therefore, if you're planning to compose your own backend project using this [nestia](https://github.com/samchon/nestia), I recommend you to create the repository and learn from the [backend](https://github.com/samchon/backend) template project.
|
|
623
712
|
|
|
624
|
-
### Nestia-Helper
|
|
625
|
-
https://github.com/samchon/nestia-helper
|
|
626
713
|
|
|
627
|
-
Helper library of the `NestJS` with [nestia](https://github.com/samchon/nestia).
|
|
628
714
|
|
|
629
|
-
|
|
715
|
+
## Appendix
|
|
716
|
+
### Dependencies of the SDK
|
|
717
|
+
An SDK library generated by the `nestia` requires the [nestia-fetcher](https://github.com/samchon/nestia-fetcher) module. Also, the [typescript-is](https://github.com/woutervh-/typescript-is) and [typescript-json](https://github.com/samchon/typescript-json) modules can be required following your nestia.config.ts options.
|
|
718
|
+
|
|
719
|
+
```json
|
|
720
|
+
{
|
|
721
|
+
"name": "payments-server-api",
|
|
722
|
+
"dependencies": {
|
|
723
|
+
"nestia-fetcher": "^2.0.1",
|
|
724
|
+
"typescript-is": "^0.19.0",
|
|
725
|
+
"typescript-json": "^2.0.9"
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
```
|
|
630
729
|
|
|
631
|
-
|
|
730
|
+
The `npx nestia install` command installs those dependencies with the `package.json` configuration.
|
|
632
731
|
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
- [TypedParam](https://github.com/samchon/nestia-helper#typedparam), [EncryptedBody](https://github.com/samchon/nestia-helper#encryptedbody), [PlainBody](https://github.com/samchon/nestia-helper#plainbody)
|
|
637
|
-
- [ExceptionManager](https://github.com/samchon/nestia-helper#exceptionmanager)
|
|
732
|
+
```bash
|
|
733
|
+
# MOVE TO THE DISTRIBUTION DIRECTORY
|
|
734
|
+
cd packages/api
|
|
638
735
|
|
|
639
|
-
|
|
640
|
-
|
|
736
|
+
# INSTALL DEPENDENCIES OF THE SDK
|
|
737
|
+
npx nestia install
|
|
738
|
+
```
|
|
641
739
|
|
|
642
|
-
|
|
740
|
+
### Template Repository
|
|
741
|
+
https://github.com/samchon/backend
|
|
643
742
|
|
|
644
|
-
|
|
743
|
+
I support template backend project using this `nestia` library, `samchon/backend`.
|
|
645
744
|
|
|
646
|
-
|
|
647
|
-
- Errors would be detected in the **compilation** level
|
|
648
|
-
- **Auto Completion** would be provided
|
|
649
|
-
- **Type Hint** would be supported
|
|
650
|
-
- You can implement [**App-join**](https://github.com/samchon/safe-typeorm#app-join-builder) very conveniently
|
|
651
|
-
- When [**SELECT**ing for **JSON** conversion](https://github.com/samchon/safe-typeorm#json-select-builder)
|
|
652
|
-
- [**App-Join**](https://github.com/samchon/safe-typeorm#app-join-builder) with the related entities would be automatically done
|
|
653
|
-
- Exact JSON **type** would be automatically **deduced**
|
|
654
|
-
- The **performance** would be **automatically tuned**
|
|
655
|
-
- When [**INSERT**](https://github.com/samchon/safe-typeorm#insert-collection)ing records
|
|
656
|
-
- Sequence of tables would be automatically sorted by analyzing dependencies
|
|
657
|
-
- The **performance** would be **automatically tuned**
|
|
745
|
+
Reading the README content of the backend template repository, you can find lots of example backend projects who've been generated from the backend. Furthermore, those example projects guide how to generate SDK library from the `nestia` and how to distribute the SDK library thorugh the NPM module.
|
|
658
746
|
|
|
659
|
-
|
|
747
|
+
Therefore, if you're planning to compose your own backend project using this `nestia`, I recommend you to create the repository and learn from the `samchon/backend` template project.
|
|
660
748
|
|
|
661
749
|
### Archidraw
|
|
662
750
|
https://www.archisketch.com/
|
|
663
751
|
|
|
664
752
|
I have special thanks to the Archidraw, where I'm working for.
|
|
665
753
|
|
|
666
|
-
The Archidraw is a great IT company developing 3D interior editor and lots of solutions based on the 3D assets. Also, the Archidraw is the first company who had adopted this
|
|
754
|
+
The Archidraw is a great IT company developing 3D interior editor and lots of solutions based on the 3D assets. Also, the Archidraw is the first company who had adopted this nestia on their commercial backend project, even this nestia was in the alpha level.
|
|
755
|
+
|
|
756
|
+
> 저희 회사 "아키드로우" 에서, 삼촌과 함께 일할 프론트 개발자 분들을, 최고의 대우로 모십니다.
|
|
757
|
+
>
|
|
758
|
+
> "아키드로우" 는 3D (인테리어) 에디터 및 이에 관한 파생 솔루션들을 만드는 회사입니다. 다만 저희 회사의 주력 제품이 3D 에디터라 하여, 반드시 3D 내지 랜더링에 능숙해야 하는 것은 아니니, 일반적인 프론트 개발자 분들도 망설임없이 지원해주십시오.
|
|
759
|
+
>
|
|
760
|
+
> 그리고 저희 회사는 분위기가 다들 친하고 즐겁게 지내는 분위기입니다. 더하여 위 `nestia` 나 [typescript-json](https://github.com/samchon/typescript-json) 및 [payments](https://github.com/archidraw/payments) 등, 제법 합리적(?)이고 재미난 프로젝트들을 다양하게 체험해보실 수 있습니다.
|
|
761
|
+
>
|
|
762
|
+
> - 회사소개서: [archidraw.pdf](https://github.com/archidraw/payments/files/7696710/archidraw.pdf)
|
|
763
|
+
> - 기술 스택: React + TypeScript
|
|
764
|
+
> - 이력서: 자유 양식
|
|
765
|
+
> - 지원처: samchon@archisketch.com
|