nestia 2.1.2-dev.20220509 → 2.1.4
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 +666 -448
- package/lib/analyses/ControllerAnalyzer.d.ts.map +1 -1
- package/lib/analyses/ControllerAnalyzer.js +34 -7
- package/lib/analyses/ReflectAnalyzer.d.ts.map +1 -1
- package/lib/analyses/ReflectAnalyzer.js +51 -18
- package/lib/factories/SchemaFactory.js +1 -1
- package/lib/structures/IController.d.ts +2 -2
- package/lib/structures/IController.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,372 +1,474 @@
|
|
|
1
1
|
# Nestia
|
|
2
|
-
Automatic `SDK` and `
|
|
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)
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
You just run this [nestia](https://github.com/samchon/nestia) up, then [nestia](https://github.com/samchon/nestia) would generate the SDK automatically, by analyzing your controller classes in the compliation and runtime level. With the automatically generated SDK through this [nestia](https://github.com/samchon/nestia), client developer also does not need any extra work, like reading `swagger` and writing the duplicated interaction code. Client developer only needs to import the SDK and calls matched function with the `await` symbol.
|
|
32
|
-
|
|
33
|
-
> Even generating the `swagger.json` without any swagger comment and DTO decorator is also possible. When generating the `swagger.json`, no DTO comment and decorator is required, either. Use only the pure interface definitions.
|
|
2
|
+
Automatic `SDK` and `Swagger` generator for the `NestJS`, evolved than ever.
|
|
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)
|
|
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
|
|
13
|
+
|
|
14
|
+
`nestia` is an evolved `SDK` and `Swagger` generator, which analyzes your `NestJS` server code in the compilation level. With `nestia` and compilation level analyzer, you don't need to write any swagger or class-validator decorators. All you need to do is use the `nestia` CLI as shown below.
|
|
15
|
+
|
|
16
|
+
Reading below contents, feel how the "compilation level" makes `nestia` stronger.
|
|
17
|
+
|
|
18
|
+
Components | `nestia`::SDK | `nestia`::swagger | `@nestjs/swagger`
|
|
19
|
+
-----------|---|---|---
|
|
20
|
+
Pure DTO interface | ✔ | ✔ | ❌
|
|
21
|
+
Description comments | ✔ | ✔ | ❌
|
|
22
|
+
Simple structure | ✔ | ✔ | ✔
|
|
23
|
+
Generic type | ✔ | ✔ | ❌
|
|
24
|
+
Union type | ✔ | ✔ | ▲
|
|
25
|
+
Intersection type | ✔ | ✔ | ▲
|
|
26
|
+
Conditional type | ✔ | ▲ | ❌
|
|
27
|
+
Auto completion | ✔ | ❌ | ❌
|
|
28
|
+
Type hints | ✔ | ❌ | ❌
|
|
29
|
+
2x faster `JSON.stringify()` | ✔ | ❌ | ❌
|
|
30
|
+
Ensure type safety | ✅ | ❌ | ❌
|
|
34
31
|
|
|
35
32
|
```typescript
|
|
36
|
-
|
|
37
|
-
import
|
|
38
|
-
import { IPage } from "@samchon/
|
|
39
|
-
|
|
40
|
-
|
|
33
|
+
// IMPORT SDK LIBRARY GENERATED BY NESTIA
|
|
34
|
+
import api from "@samchon/shopping-api";
|
|
35
|
+
import { IPage } from "@samchon/shopping-api/lib/structures/IPage";
|
|
36
|
+
import { ISale } from "@samchon/shopping-api/lib/structures/ISale";
|
|
37
|
+
import { ISaleArticleComment } from "@samchon/shopping-api/lib/structures/ISaleArticleComment";
|
|
38
|
+
import { ISaleQuestion } from "@samchon/shopping-api/lib/structures/ISaleQuestion";
|
|
39
|
+
|
|
40
|
+
export async function trace_sale_question_and_comment
|
|
41
|
+
(connection: api.IConnection): Promise<void>
|
|
41
42
|
{
|
|
42
|
-
// LIST UP
|
|
43
|
-
const index: IPage<
|
|
43
|
+
// LIST UP SALE SUMMARIES
|
|
44
|
+
const index: IPage<ISale.ISummary> = await api.functional.shoppings.sales.index
|
|
44
45
|
(
|
|
45
46
|
connection,
|
|
46
|
-
"
|
|
47
|
+
"general",
|
|
47
48
|
{ limit: 100, page: 1 }
|
|
48
49
|
);
|
|
49
50
|
|
|
50
|
-
//
|
|
51
|
-
const
|
|
51
|
+
// PICK A SALE
|
|
52
|
+
const sale: ISale = await api.functional.shoppings.sales.at
|
|
52
53
|
(
|
|
53
|
-
connection,
|
|
54
|
-
"free",
|
|
54
|
+
connection,
|
|
55
55
|
index.data[0].id
|
|
56
56
|
);
|
|
57
|
-
console.log(
|
|
57
|
+
console.log("sale", sale);
|
|
58
|
+
|
|
59
|
+
// WRITE A QUESTION
|
|
60
|
+
const question: ISaleQuestion = await api.functional.shoppings.sales.questions.store
|
|
61
|
+
(
|
|
62
|
+
connection,
|
|
63
|
+
"general",
|
|
64
|
+
sale.id,
|
|
65
|
+
{
|
|
66
|
+
title: "How to use this product?",
|
|
67
|
+
body: "The description is not fully enough. Can you introduce me more?",
|
|
68
|
+
files: []
|
|
69
|
+
}
|
|
70
|
+
);
|
|
71
|
+
console.log("question", question);
|
|
72
|
+
|
|
73
|
+
// WRITE A COMMENT
|
|
74
|
+
const comment: ISaleArticleComment = await api.functional.shoppings.sales.comments.store
|
|
75
|
+
(
|
|
76
|
+
connection,
|
|
77
|
+
"general",
|
|
78
|
+
sale.id,
|
|
79
|
+
question.id,
|
|
80
|
+
{
|
|
81
|
+
body: "p.s) Can you send me a detailed catalogue?",
|
|
82
|
+
anonymous: false
|
|
83
|
+
}
|
|
84
|
+
);
|
|
85
|
+
console.log("comment", comment);
|
|
58
86
|
}
|
|
59
87
|
```
|
|
60
88
|
|
|
61
89
|
|
|
62
90
|
|
|
91
|
+
## Setup
|
|
92
|
+
Just like any other package, you've got to install it before you can use it.
|
|
63
93
|
|
|
64
|
-
|
|
65
|
-
### Installation
|
|
66
|
-
```bash
|
|
94
|
+
```sh
|
|
67
95
|
npm install --save-dev nestia
|
|
68
96
|
```
|
|
69
97
|
|
|
70
|
-
|
|
98
|
+
After the installation, you can generate the `SDK` or `Swagger`, directly.
|
|
71
99
|
|
|
72
|
-
|
|
100
|
+
```sh
|
|
101
|
+
npx nestia sdk "src/**/*.controller" --out "src/api"
|
|
102
|
+
npx nestia swagger "src/**/*.controller" --out "swagger.json"
|
|
103
|
+
```
|
|
73
104
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
105
|
+
> If all of your controller files are gathered into one directory:
|
|
106
|
+
>
|
|
107
|
+
> ```sh
|
|
108
|
+
> npx nestia sdk "src/controllers" --out "src/api"
|
|
109
|
+
> npx nestia swagger "src/controllers" --out "swagger.json"
|
|
110
|
+
> ```
|
|
111
|
+
>
|
|
112
|
+
> You can omit all of the parameters if you've configured the [nestia.config.ts](#configuration) file.
|
|
113
|
+
>
|
|
114
|
+
> ```sh
|
|
115
|
+
> npx nestia sdk
|
|
116
|
+
> npx nestia swagger
|
|
117
|
+
> ```
|
|
77
118
|
|
|
78
|
-
npx nestia sdk "src/**/*.controller.ts" --out "src/api"
|
|
79
|
-
npx nestia sdk "src/controllers" --out "src/api"
|
|
80
|
-
npx nestia sdk "src/controllers/consumers" "src/controllers/sellers" --out "src/api"
|
|
81
|
-
npx nestia sdk "src/controllers" --exclude "src/**/Fake*.ts" --out "src/api"
|
|
82
|
-
```
|
|
83
119
|
|
|
84
|
-
To generate a SDK library through the [nestia](https://github.com/samchon/nestia) is very easy.
|
|
85
120
|
|
|
86
|
-
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.
|
|
87
121
|
|
|
88
|
-
|
|
122
|
+
## Demonstrations
|
|
123
|
+
### Pure DTO Interface
|
|
124
|
+
`nestia` can utilize pure interface type as DTO.
|
|
89
125
|
|
|
90
|
-
|
|
91
|
-
```bash
|
|
92
|
-
npx nestia <source_controller_of_directory> --out <output_path>
|
|
126
|
+
Unlike `@nestjs/swagger` which requires the DTO class with decorators, `nestia` can use the pure interface type directly. Also, `nestia` can utilize the pure descriptive comments, instead of using the `description` property of the decorators. Furthermore, `nestia` can even support generic types, union/intersection types and even conditional types.
|
|
93
127
|
|
|
94
|
-
|
|
95
|
-
npx nestia swagger "src/controllers" --out "./swagger.json"
|
|
96
|
-
npx nestia swagger "src/consumers" "src/sellers" --out "actors.json"
|
|
97
|
-
npx nestia swagger "src/controllers" --exclude "src/**/Fake*.ts" -out "./"
|
|
98
|
-
```
|
|
128
|
+
Look at the code below, you may see the difference between `nestia` and `@nestjs/swagger`, and thereby catch the meaning of the pure DTO interface.
|
|
99
129
|
|
|
100
|
-
|
|
130
|
+
- Simple [`ISaleArticleComment`](https://github.com/samchon/nestia/tree/master/demo/simple/src/api/structures/ISaleArticleComment.ts)
|
|
131
|
+
- Generic interfaces
|
|
132
|
+
- grandparent interface, [`ISaleArticle<Content>`](https://github.com/samchon/nestia/tree/master/demo/generic/src/api/structures/ISaleArticle.ts)
|
|
133
|
+
- parent interface, [`ISaleInquiry<Content>`](https://github.com/samchon/nestia/tree/master/demo/generic/src/api/structures/ISaleInquiry.ts)
|
|
134
|
+
- 1st sub-type interface, [`ISaleQuestion`](https://github.com/samchon/nestia/tree/master/demo/generic/src/api/structures/ISaleQuestion.ts)
|
|
135
|
+
- 2nd sub-type interface, [`ISaleReview`](https://github.com/samchon/nestia/tree/master/demo/generic/src/api/structures/ISaleReview.ts)
|
|
136
|
+
- Union alias type [`ISaleEntireArticle`](https://github.com/samchon/nestia/tree/master/demo/union/src/api/structures/ISaleEntireArticle.ts)
|
|
101
137
|
|
|
102
|
-
|
|
138
|
+
> The below example code would be shown by clicking the arrow button or text.
|
|
103
139
|
|
|
104
|
-
|
|
140
|
+
<details>
|
|
141
|
+
<summary>
|
|
142
|
+
Traditional DTO class using <code>@nestjs/swagger</code>
|
|
143
|
+
</summary>
|
|
105
144
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
145
|
+
```typescript
|
|
146
|
+
export class SaleArticleComment
|
|
147
|
+
{
|
|
148
|
+
@ApiProperty({
|
|
149
|
+
description:
|
|
150
|
+
`Comment wrote on a sale related article.
|
|
110
151
|
|
|
111
|
-
|
|
152
|
+
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.
|
|
112
153
|
|
|
113
|
-
|
|
154
|
+
Also, writing a reply comment for a specific comment is possible and in that case, the ISaleArticleComment.parent_id property would be activated.`
|
|
155
|
+
})
|
|
156
|
+
id: number;
|
|
114
157
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
158
|
+
@ApiProperty({
|
|
159
|
+
type: "number",
|
|
160
|
+
nullable: true,
|
|
161
|
+
description:
|
|
162
|
+
`Parent comment ID.
|
|
163
|
+
|
|
164
|
+
Only When this comment has been written as a reply.`
|
|
165
|
+
})
|
|
166
|
+
parent_id: number | null;
|
|
167
|
+
|
|
168
|
+
@ApiProperty({
|
|
169
|
+
type: "string",
|
|
170
|
+
description: "Type of the writer."
|
|
171
|
+
})
|
|
172
|
+
writer_type: "seller" | "consumer";
|
|
173
|
+
|
|
174
|
+
@ApiProperty({
|
|
175
|
+
type: "string",
|
|
176
|
+
nullable: true,
|
|
177
|
+
description:
|
|
178
|
+
`Name of the writer.
|
|
179
|
+
|
|
180
|
+
When this is a type of anonymous comment, writer name would be hidden.`
|
|
181
|
+
})
|
|
182
|
+
writer_name: string | null;
|
|
183
|
+
|
|
184
|
+
@ApiProperty({
|
|
185
|
+
type: "array",
|
|
186
|
+
items: {
|
|
187
|
+
schema: { $ref: getSchemaPath(SaleArticleComment.Content) }
|
|
188
|
+
},
|
|
189
|
+
description:
|
|
190
|
+
`Contents of the comments.
|
|
191
|
+
|
|
192
|
+
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.`
|
|
193
|
+
})
|
|
194
|
+
contents: SaleArticleComment.Content[];
|
|
195
|
+
|
|
196
|
+
@ApiProperty({
|
|
197
|
+
description: "Creation time."
|
|
198
|
+
})
|
|
199
|
+
created_at: string;
|
|
123
200
|
}
|
|
124
201
|
```
|
|
202
|
+
</details>
|
|
125
203
|
|
|
204
|
+
<details>
|
|
205
|
+
<summary>
|
|
206
|
+
Pure DTO interface using <code>nestia</code>
|
|
207
|
+
</summary>
|
|
126
208
|
|
|
127
|
-
|
|
128
|
-
## Advanced
|
|
129
|
-
### `nestia.config.ts`
|
|
130
209
|
```typescript
|
|
131
210
|
/**
|
|
132
|
-
*
|
|
211
|
+
* Comment wrote on a sale related article.
|
|
212
|
+
*
|
|
213
|
+
* When an article of a sale has been enrolled, all of the participants like consumers and
|
|
214
|
+
* sellers can write a comment on that article. However, when the writer is a consumer, the
|
|
215
|
+
* consumer can hide its name through the annoymous option.
|
|
216
|
+
*
|
|
217
|
+
* Also, writing a reply comment for a specific comment is possible and in that case, the
|
|
218
|
+
* {@link ISaleArticleComment.parent_id} property would be activated.
|
|
133
219
|
*
|
|
134
220
|
* @author Jeongho Nam - https://github.com/samchon
|
|
135
221
|
*/
|
|
136
|
-
export interface
|
|
222
|
+
export interface ISaleArticleComment
|
|
137
223
|
{
|
|
138
224
|
/**
|
|
139
|
-
*
|
|
225
|
+
* Primary Key.
|
|
140
226
|
*/
|
|
141
|
-
|
|
227
|
+
id: number;
|
|
142
228
|
|
|
143
229
|
/**
|
|
144
|
-
*
|
|
230
|
+
* Parent comment ID.
|
|
231
|
+
*
|
|
232
|
+
* Only When this comment has been written as a reply.
|
|
145
233
|
*/
|
|
146
|
-
|
|
234
|
+
parent_id: number | null;
|
|
147
235
|
|
|
148
236
|
/**
|
|
149
|
-
*
|
|
150
|
-
*
|
|
151
|
-
* If omitted, the configuration would follow the `tsconfig.json`.
|
|
237
|
+
* Type of the writer.
|
|
152
238
|
*/
|
|
153
|
-
|
|
239
|
+
writer_type: "seller" | "consumer";
|
|
154
240
|
|
|
155
241
|
/**
|
|
156
|
-
*
|
|
242
|
+
* Name of the writer.
|
|
157
243
|
*
|
|
158
|
-
*
|
|
159
|
-
* checked through the [typescript-is](https://github.com/woutervh-/typescript-is).
|
|
244
|
+
* When this is a type of anonymous comment, writer name would be hidden.
|
|
160
245
|
*/
|
|
161
|
-
|
|
246
|
+
writer_name: string | null;
|
|
162
247
|
|
|
163
248
|
/**
|
|
164
|
-
*
|
|
249
|
+
* Contents of the comments.
|
|
165
250
|
*
|
|
166
|
-
*
|
|
167
|
-
*
|
|
168
|
-
*
|
|
251
|
+
* When the comment writer tries to modify content, it would not modify the comment
|
|
252
|
+
* content but would be accumulated. Therefore, all of the people can read how
|
|
253
|
+
* the content has been changed.
|
|
169
254
|
*/
|
|
170
|
-
|
|
255
|
+
contents: ISaleArticleComment.IContent[];
|
|
171
256
|
|
|
172
257
|
/**
|
|
173
|
-
*
|
|
258
|
+
* Creation time.
|
|
174
259
|
*/
|
|
175
|
-
|
|
260
|
+
created_at: string;
|
|
176
261
|
}
|
|
177
|
-
export namespace
|
|
262
|
+
export namespace ISaleArticleComment
|
|
178
263
|
{
|
|
179
264
|
/**
|
|
180
|
-
*
|
|
181
|
-
* controllers.
|
|
265
|
+
* Store info.
|
|
182
266
|
*/
|
|
183
|
-
export interface
|
|
267
|
+
export interface IStore
|
|
184
268
|
{
|
|
185
269
|
/**
|
|
186
|
-
*
|
|
270
|
+
* Body of the content.
|
|
187
271
|
*/
|
|
188
|
-
|
|
272
|
+
body: string;
|
|
189
273
|
|
|
190
274
|
/**
|
|
191
|
-
*
|
|
275
|
+
* Whether to hide the writer name or not.
|
|
192
276
|
*/
|
|
193
|
-
|
|
277
|
+
annonymous: boolean;
|
|
194
278
|
}
|
|
195
279
|
|
|
196
280
|
/**
|
|
197
|
-
*
|
|
281
|
+
* Content info.
|
|
198
282
|
*/
|
|
199
|
-
export interface
|
|
283
|
+
export interface IContent
|
|
200
284
|
{
|
|
201
285
|
/**
|
|
202
|
-
*
|
|
203
|
-
*
|
|
204
|
-
* If you've configure only directory, the file name would be `swagger.json`.
|
|
205
|
-
* Otherwise you configure file name and extension, the `swagger.json` file would
|
|
206
|
-
* be renamed to what you've configured.
|
|
286
|
+
* Primary Key.
|
|
207
287
|
*/
|
|
208
|
-
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
```
|
|
212
|
-
|
|
213
|
-
Instead of specifying `input` and `output` directories using the cli options, you can specify those directories as an independent configuration file. It's the `nestia.config.ts` and with the `nestia.config.ts` file, you also configure independent TypeScript compiler option from the `tsconfig.json`.
|
|
214
|
-
|
|
215
|
-
Write below content as the `nestia.config.ts` file and place it onto the root directory of your backend project. After the configuration, you can generate the SDK only with the `npx nestia sdk` command, without any directory specification.
|
|
288
|
+
id: string;
|
|
216
289
|
|
|
217
|
-
|
|
218
|
-
|
|
290
|
+
/**
|
|
291
|
+
* Body of the content.
|
|
292
|
+
*/
|
|
293
|
+
body: string;
|
|
219
294
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
}
|
|
225
|
-
|
|
295
|
+
/**
|
|
296
|
+
* Creation time.
|
|
297
|
+
*/
|
|
298
|
+
created_at: string;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
226
301
|
```
|
|
302
|
+
</details>
|
|
227
303
|
|
|
228
|
-
>
|
|
229
|
-
>
|
|
230
|
-
>
|
|
231
|
-
>
|
|
232
|
-
> input: "src/**/*.controller.ts",
|
|
233
|
-
> /* input: {
|
|
234
|
-
> include: ["src/controllers/**\/*.controller.ts"],
|
|
235
|
-
> exclude: ["src/controllers/**\/fake_*.controller.ts"]
|
|
236
|
-
> },*/
|
|
237
|
-
> output: "src/api",
|
|
238
|
-
> assert: true
|
|
239
|
-
> }
|
|
240
|
-
> ```
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
### Recommended Structures
|
|
245
|
-
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.
|
|
246
|
-
|
|
247
|
-
If you place the SDK onto the `src/api` directory and gather all of the DTO interface structures into the `src/api/structures` directory, you can publish the SDK library very easily without any special configuration. Also when you're develop the test automation program, you can implement the API testing features very convenienty through the automatically generated SDK through this [nestia](https://github.com/samchon/nestia).
|
|
248
|
-
|
|
249
|
-
- src
|
|
250
|
-
- api
|
|
251
|
-
- **functional**: automatically generated SDK functions
|
|
252
|
-
- **structures**: DTO structures
|
|
253
|
-
- controllers
|
|
254
|
-
- providers
|
|
255
|
-
- models
|
|
256
|
-
- **test**: Test automation program using SDK functions
|
|
257
|
-
- package.json
|
|
258
|
-
- tsconfig.json
|
|
259
|
-
- nestia.config.ts
|
|
260
|
-
|
|
261
|
-
For your deep understanding about this directory structure with this [nestia](https://github.com/samchon/nestia), I've prepared an example backend project. Looking around the example repository and reading the [README.md](https://github.com/samchon/backend#13-directories) of it, you can feel that such directory structure is how convenient for SDK publishing and test automation program implementation.
|
|
262
|
-
|
|
263
|
-
- https://github.com/samchon/backend
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
## Demonstration
|
|
269
|
-
To demonstrate which SDK codes would be generated by this [nestia](https://github.com/samchon/nestia):
|
|
270
|
-
|
|
271
|
-
- Representative files
|
|
272
|
-
- [DTO interface used in the RestAPI](https://github.com/samchon/nestia/tree/master/demo/simple/src/api/structures/ISaleArticleComment.ts)
|
|
273
|
-
- [Controllers of the NestJS](https://github.com/samchon/nestia/tree/master/demo/simple/src/controllers/ConsumerSaleArticleCommentsController.ts)
|
|
274
|
-
- [SDK generated by this [nestia](https://github.com/samchon/nestia)](https://github.com/samchon/nestia/tree/master/demo/simple/src/api/functional/consumers/sales/articles/comments/index.ts)
|
|
275
|
-
- [`swagger.json` generated by this [nestia](https://github.com/samchon/nestia)](https://github.com/samchon/nestia/tree/master/demo/simple/swagger.json)
|
|
276
|
-
- Demonstration Projects
|
|
277
|
-
- [encrypt](https://github.com/samchon/nestia/tree/master/demo/encrypt): Request and response body are fully encrypted
|
|
278
|
-
- [generic](https://github.com/samchon/nestia/tree/master/demo/generic): Generic typed controller classes
|
|
279
|
-
- [recursive](https://github.com/samchon/nestia/tree/master/demo/recursive): Recursive DTO interface, [swagger editor](https://editor.swagger.io) can't expresss it
|
|
280
|
-
- [simple](https://github.com/samchon/nestia/tree/master/demo/simple): Simple DTO interface and controller class
|
|
281
|
-
- [union](https://github.com/samchon/nestia/tree/master/demo/union): Only [nestia](https://github.com/samchon/nestia) can handle the union typed DTO interface
|
|
282
|
-
|
|
283
|
-
### DTO
|
|
284
|
-
Using pure interface type as DTO is possible.
|
|
285
|
-
|
|
286
|
-
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.
|
|
287
|
-
|
|
288
|
-
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.
|
|
304
|
+
<details>
|
|
305
|
+
<summary>
|
|
306
|
+
Generic typed DTO using <code>nestia</code>
|
|
307
|
+
</summary>
|
|
289
308
|
|
|
290
309
|
```typescript
|
|
291
310
|
/**
|
|
292
|
-
*
|
|
311
|
+
* Inquiry article.
|
|
293
312
|
*
|
|
313
|
+
* Sub-type of article and super-type of question and answer.
|
|
314
|
+
*
|
|
315
|
+
* - List of the sub-types
|
|
316
|
+
* - {@link ISaleQuestion}
|
|
317
|
+
* - {@link ISaleReview}
|
|
318
|
+
*
|
|
319
|
+
* @template Content Content type
|
|
294
320
|
* @author Jeongho Nam - https://github.com/samchon
|
|
295
321
|
*/
|
|
296
|
-
export interface
|
|
322
|
+
export interface ISaleInquiry<Content extends ISaleInquiry.IContent>
|
|
323
|
+
extends ISaleArticle<Content>
|
|
297
324
|
{
|
|
298
325
|
/**
|
|
299
326
|
* Primary Key.
|
|
300
327
|
*/
|
|
301
328
|
id: number;
|
|
302
329
|
|
|
303
|
-
/**
|
|
304
|
-
* Type of the writer.
|
|
305
|
-
*/
|
|
306
|
-
writer_type: "seller" | "consumer";
|
|
307
|
-
|
|
308
330
|
/**
|
|
309
331
|
* Name of the writer.
|
|
310
332
|
*/
|
|
311
|
-
|
|
333
|
+
writer: string;
|
|
312
334
|
|
|
313
335
|
/**
|
|
314
|
-
*
|
|
336
|
+
* List of contents.
|
|
315
337
|
*
|
|
316
|
-
* When the
|
|
317
|
-
* content but would be accumulated. Therefore, all
|
|
338
|
+
* When the article writer tries to modify content, it would not modify the article
|
|
339
|
+
* content but would be accumulated. Therefore, all the people can read how
|
|
318
340
|
* the content has been changed.
|
|
319
341
|
*/
|
|
320
|
-
contents:
|
|
342
|
+
contents: Content[];
|
|
321
343
|
|
|
322
344
|
/**
|
|
323
345
|
* Creation time.
|
|
324
346
|
*/
|
|
325
|
-
|
|
347
|
+
createdAat: string;
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* Formal answer from the seller.
|
|
351
|
+
*/
|
|
352
|
+
answer: ISaleInquiryAnswer | null;
|
|
326
353
|
}
|
|
327
|
-
|
|
328
|
-
export namespace ISaleComment
|
|
354
|
+
export namespace ISaleInquiry
|
|
329
355
|
{
|
|
330
356
|
/**
|
|
331
|
-
*
|
|
357
|
+
* Content info.
|
|
332
358
|
*/
|
|
333
|
-
export interface
|
|
359
|
+
export interface IContent
|
|
334
360
|
{
|
|
361
|
+
/**
|
|
362
|
+
* Primary Key
|
|
363
|
+
*/
|
|
364
|
+
id: string;
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Title of the content.
|
|
368
|
+
*/
|
|
369
|
+
title: string;
|
|
370
|
+
|
|
335
371
|
/**
|
|
336
372
|
* Body of the content.
|
|
337
373
|
*/
|
|
338
374
|
body: string;
|
|
339
|
-
}
|
|
340
375
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
376
|
+
/**
|
|
377
|
+
* Attached files.
|
|
378
|
+
*/
|
|
379
|
+
files: IAttachmentFile[];
|
|
380
|
+
|
|
346
381
|
/**
|
|
347
382
|
* Creation time.
|
|
348
383
|
*/
|
|
349
|
-
|
|
384
|
+
createdAt: string;
|
|
350
385
|
}
|
|
351
386
|
}
|
|
352
387
|
```
|
|
388
|
+
</details>
|
|
389
|
+
|
|
390
|
+
<details>
|
|
391
|
+
<summary>
|
|
392
|
+
Union typed DTO using <code>nestia</code>
|
|
393
|
+
</summary>
|
|
394
|
+
|
|
395
|
+
```typescript
|
|
396
|
+
/**
|
|
397
|
+
* Union type of the entire sub-type articles.
|
|
398
|
+
*
|
|
399
|
+
* @author Jeongho Nam - https://github.com/samchon
|
|
400
|
+
*/
|
|
401
|
+
export type ISaleEntireArtcle = ISaleQuestion | ISaleReview;
|
|
402
|
+
```
|
|
403
|
+
</details>
|
|
353
404
|
|
|
354
|
-
|
|
355
|
-
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
|
|
408
|
+
### Advanced Controller Class
|
|
409
|
+
Controller also can use the generic arguments.
|
|
410
|
+
|
|
411
|
+
In the previous [Pure DTO Interface](#pure-dto-interface) corner, we've learned that `nestia` can use the pure interface type as DTO. Also, we've learned that utilizing generic, union/intersection and even conditional typed interfaces are also possible.
|
|
412
|
+
|
|
413
|
+
In the Controller case, it's same with the upper DTO story. With `nestia`, defining a generic typed controller class is also possible, too. By defining a generic typed controller class as a super-type class, you can reduce both duplicated code and description comments.
|
|
414
|
+
|
|
415
|
+
Look at the below code and feel how powerful `nestia` is. It should be stated that, `@nestjs/swagger` cannot construct such generic or union typed controller class.
|
|
416
|
+
|
|
417
|
+
- Simple [`CustomerSaleArticleCommentsController`](https://github.com/samchon/nestia/blob/master/demo/simple/src/controllers/ConsumerSaleArticleCommentsController.ts)
|
|
418
|
+
- Generic controllers
|
|
419
|
+
- abstract controller, [`SaleInquiriesController<Content, Store, Json>`](https://github.com/samchon/nestia/tree/master/demo/generic/src/controllers/SaleInquiriesController.ts)
|
|
420
|
+
- 1st sub-type controller, [`ConsumerSaleQuestionsController`](https://github.com/samchon/nestia/tree/master/demo/generic/src/controllers/ConsumerSaleQuestionsController.ts)
|
|
421
|
+
- 2nd sub-type controller, [`ConsumerSaleQuestionsController`](https://github.com/samchon/nestia/tree/master/demo/generic/src/controllers/ConsumerSaleQuestionsController.ts)
|
|
422
|
+
- Union controller, [`ConsumerSaleEntireArticlesController`](https://github.com/samchon/nestia/tree/master/demo/union/src/controllers/ConsumerSaleEntireArticlesController.ts)
|
|
423
|
+
|
|
424
|
+
> [typescript-is](https://github.com/woutervh-/typescript-is) can replace the class-validator with only one line.
|
|
425
|
+
>
|
|
426
|
+
> ```typescript
|
|
427
|
+
> import * as nest from "@nestjs/common";
|
|
428
|
+
> import { assertType } from "typescript-is";
|
|
429
|
+
>
|
|
430
|
+
> @nest.Controller("consumers/:section/sales/:saleId/questions")
|
|
431
|
+
> export class SaleQuestionsController
|
|
432
|
+
> extends SaleInquiriesController<
|
|
433
|
+
> ISaleQuestion,
|
|
434
|
+
> ISaleQuestion.IContent,
|
|
435
|
+
> ISaleQuestion.IStore>
|
|
436
|
+
> {
|
|
437
|
+
> public constructor()
|
|
438
|
+
> {
|
|
439
|
+
> super(input => assertType<ISaleQuestion.IStore>(input));
|
|
440
|
+
> }
|
|
441
|
+
> }
|
|
442
|
+
> ```
|
|
356
443
|
|
|
357
444
|
```typescript
|
|
358
|
-
|
|
359
|
-
|
|
445
|
+
import * as express from "express";
|
|
446
|
+
import * as nest from "@nestjs/common";
|
|
447
|
+
import helper from "nestia-helper";
|
|
448
|
+
|
|
449
|
+
import { ISaleInquiry } from "@api/structures/ISaleInquiry";
|
|
450
|
+
|
|
451
|
+
export abstract class SaleInquiriesController<
|
|
452
|
+
Content extends ISaleInquiry.IContent,
|
|
453
|
+
Store extends ISaleInquiry.IStore,
|
|
454
|
+
Json extends ISaleInquiry<Content>>
|
|
360
455
|
{
|
|
361
456
|
/**
|
|
362
|
-
*
|
|
457
|
+
* Constructor with type assert function.
|
|
458
|
+
*/
|
|
459
|
+
protected constructor(private readonly assert: (input: Store) => void);
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* Store a new inquiry.
|
|
463
|
+
*
|
|
464
|
+
* Write a new article inquirying about a sale.
|
|
363
465
|
*
|
|
364
466
|
* @param request Instance of the Express.Request
|
|
365
467
|
* @param section Code of the target section
|
|
366
468
|
* @param saleId ID of the target sale
|
|
367
469
|
* @param input Content to archive
|
|
470
|
+
* @return Newly archived inquiry
|
|
368
471
|
*
|
|
369
|
-
* @return Newly archived question
|
|
370
472
|
* @throw 400 bad request error when type of the input data is not valid
|
|
371
473
|
* @throw 401 unauthorized error when you've not logged in yet
|
|
372
474
|
*/
|
|
@@ -374,40 +476,101 @@ export class ConsumerSaleQuestionsController
|
|
|
374
476
|
public store
|
|
375
477
|
(
|
|
376
478
|
@nest.Request() request: express.Request,
|
|
377
|
-
@
|
|
378
|
-
@
|
|
379
|
-
@nest.Body() input:
|
|
380
|
-
): Promise<
|
|
479
|
+
@helper.TypedParam("section", "string") section: string,
|
|
480
|
+
@helper.TypedParam("saleId", "string") saleId: string,
|
|
481
|
+
@nest.Body() input: Store
|
|
482
|
+
): Promise<Json>;
|
|
483
|
+
|
|
484
|
+
/**
|
|
485
|
+
* Update an inquiry.
|
|
486
|
+
*
|
|
487
|
+
* Update ordinary inquiry article. However, it would not modify the content reocrd
|
|
488
|
+
* {@link ISaleInquiry.IContent}, but be accumulated into the {@link ISaleInquiry.contents}.
|
|
489
|
+
* Therefore, all of the poeple can read how the content has been changed.
|
|
490
|
+
*
|
|
491
|
+
* @param request Instance of the Express.Request
|
|
492
|
+
* @param section Code of the target section
|
|
493
|
+
* @param saleId ID of the target sale
|
|
494
|
+
* @param id ID of the target article to be updated
|
|
495
|
+
* @param input New content to be overwritten
|
|
496
|
+
* @return The newly created content record
|
|
497
|
+
*
|
|
498
|
+
* @throw 400 bad request error when type of the input data is not valid
|
|
499
|
+
* @throw 401 unauthorized error when you've not logged in yet
|
|
500
|
+
* @throw 403 forbidden error when the article is not yours
|
|
501
|
+
*/
|
|
502
|
+
@nest.Put(":id")
|
|
503
|
+
public update
|
|
504
|
+
(
|
|
505
|
+
@nest.Request() request: express.Request,
|
|
506
|
+
@helper.TypedParam("section", "string") section: string,
|
|
507
|
+
@helper.TypedParam("saleId", "string") saleId: string,
|
|
508
|
+
@helper.TypedParam("id", "number") id: number,
|
|
509
|
+
@nest.Body() input: Store
|
|
510
|
+
): Promise<Json>;
|
|
381
511
|
}
|
|
382
512
|
```
|
|
383
513
|
|
|
384
|
-
### SDK
|
|
385
|
-
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.
|
|
386
514
|
|
|
387
|
-
|
|
515
|
+
|
|
516
|
+
|
|
517
|
+
### Software Development Kit
|
|
518
|
+
> `Swagger` is torturing client developers.
|
|
519
|
+
>
|
|
520
|
+
> If you're a backend developer and you deliver a `Swagger` to your companion client developers, they should analyze the `Swagger` and implement duplicated router functions with DTO interfaces by themselves. During those jobs, if a client developer takes a mistake by mis-reading the `Swagger`, it becomes a critical runtime error directly.
|
|
521
|
+
>
|
|
522
|
+
> Why are you torturing 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. They never need to implement the duplicated DTO interfaces with router functions, either.
|
|
523
|
+
>
|
|
524
|
+
> Therefore, just build the SDK through this `nestia` and deliver 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.
|
|
525
|
+
|
|
526
|
+
Looking at the SDK library file, generated by `nestia`, it is perfect.
|
|
527
|
+
|
|
528
|
+
Route method, path and parameters are well-formed and DTO structures are correctly imported. Also, descriptive comments are fully revived in the SDK library, regardless of where they are written.
|
|
529
|
+
|
|
530
|
+
Furthermore, there's not any problem even when a generic typed controller class comes. `nestia` will specialize the generic arguments exactly, by analyzing your `NestJS` server code, in the compilation level.
|
|
531
|
+
|
|
532
|
+
- [simple/.../comments/index.ts](https://github.com/samchon/nestia/blob/master/demo/simple/src/api/functional/consumers/sales/articles/comments/index.ts)
|
|
533
|
+
- [generic/.../questions/index.ts](https://github.com/samchon/nestia/tree/master/demo/generic/src/api/functional/consumers/sales/questions/index.ts)
|
|
534
|
+
- [generic/.../reviews/index.ts](https://github.com/samchon/nestia/tree/master/demo/generic/src/api/functional/consumers/sales/reviews/index.ts)
|
|
535
|
+
- [union/.../entire_articles/index.ts](https://github.com/samchon/nestia/tree/master/demo/union/src/api/functional/consumers/sales/entire_articles/index.ts)
|
|
388
536
|
|
|
389
537
|
```typescript
|
|
390
538
|
/**
|
|
391
|
-
*
|
|
539
|
+
* @packageDocumentation
|
|
540
|
+
* @module api.functional.consumers.sales.reviews
|
|
541
|
+
* @nestia Generated by Nestia - https://github.com/samchon/nestia
|
|
542
|
+
*/
|
|
543
|
+
//================================================================
|
|
544
|
+
import { Fetcher, Primitive } from "nestia-fetcher";
|
|
545
|
+
import type { IConnection } from "nestia-fetcher";
|
|
546
|
+
import { createStringifier } from "typescript-json";
|
|
547
|
+
|
|
548
|
+
import type { ISaleReview } from "./../../../../structures/ISaleReview";
|
|
549
|
+
import type { ISaleInquiry } from "./../../../../structures/ISaleInquiry";
|
|
550
|
+
|
|
551
|
+
/**
|
|
552
|
+
* Store a new inquiry.
|
|
553
|
+
*
|
|
554
|
+
* Write a new article inquirying about a sale.
|
|
392
555
|
*
|
|
393
556
|
* @param connection connection Information of the remote HTTP(s) server with headers (+encryption password)
|
|
394
557
|
* @param request Instance of the Express.Request
|
|
395
558
|
* @param section Code of the target section
|
|
396
559
|
* @param saleId ID of the target sale
|
|
397
560
|
* @param input Content to archive
|
|
398
|
-
* @return Newly archived
|
|
561
|
+
* @return Newly archived inquiry
|
|
399
562
|
* @throw 400 bad request error when type of the input data is not valid
|
|
400
563
|
* @throw 401 unauthorized error when you've not logged in yet
|
|
401
564
|
*
|
|
565
|
+
* @controller ConsumerSaleReviewsController.store()
|
|
566
|
+
* @path POST /consumers/:section/sales/:saleId/reviews
|
|
402
567
|
* @nestia Generated by Nestia - https://github.com/samchon/nestia
|
|
403
|
-
* @controller ConsumerSaleQuestionsController.store()
|
|
404
|
-
* @path POST /consumers/:section/sales/:saleId/questions/
|
|
405
568
|
*/
|
|
406
569
|
export function store
|
|
407
570
|
(
|
|
408
571
|
connection: IConnection,
|
|
409
572
|
section: string,
|
|
410
|
-
saleId:
|
|
573
|
+
saleId: string,
|
|
411
574
|
input: Primitive<store.Input>
|
|
412
575
|
): Promise<store.Output>
|
|
413
576
|
{
|
|
@@ -417,249 +580,304 @@ export function store
|
|
|
417
580
|
store.ENCRYPTED,
|
|
418
581
|
store.METHOD,
|
|
419
582
|
store.path(section, saleId),
|
|
420
|
-
input
|
|
583
|
+
input,
|
|
584
|
+
store.stringify
|
|
421
585
|
);
|
|
422
586
|
}
|
|
423
587
|
export namespace store
|
|
424
588
|
{
|
|
425
|
-
export type Input = Primitive<
|
|
426
|
-
export type Output = Primitive<ISaleInquiry<
|
|
589
|
+
export type Input = Primitive<ISaleReview.IStore>;
|
|
590
|
+
export type Output = Primitive<ISaleInquiry<ISaleReview.IContent>>;
|
|
427
591
|
|
|
428
592
|
export const METHOD = "POST" as const;
|
|
429
|
-
export const PATH: string = "/consumers/:section/sales/:saleId/
|
|
593
|
+
export const PATH: string = "/consumers/:section/sales/:saleId/reviews";
|
|
430
594
|
export const ENCRYPTED: Fetcher.IEncrypted = {
|
|
431
|
-
request:
|
|
432
|
-
response:
|
|
595
|
+
request: false,
|
|
596
|
+
response: false,
|
|
433
597
|
};
|
|
434
598
|
|
|
435
|
-
export function path(section: string, saleId:
|
|
599
|
+
export function path(section: string, saleId: string): string
|
|
436
600
|
{
|
|
437
|
-
return `/consumers/${section}/sales/${saleId}/
|
|
601
|
+
return `/consumers/${section}/sales/${saleId}/reviews`;
|
|
438
602
|
}
|
|
603
|
+
export const stringify = createStringifier<Input>();
|
|
439
604
|
}
|
|
440
|
-
```
|
|
441
605
|
|
|
442
|
-
|
|
443
|
-
|
|
606
|
+
/**
|
|
607
|
+
* Update an inquiry.
|
|
608
|
+
*
|
|
609
|
+
* Update ordinary inquiry article. However, it would not modify the content reocrd
|
|
610
|
+
* {@link ISaleInquiry.IContent}, but be accumulated into the {@link ISaleInquiry.contents}.
|
|
611
|
+
* Therefore, all of the poeple can read how the content has been changed.
|
|
612
|
+
*
|
|
613
|
+
* @param connection connection Information of the remote HTTP(s) server with headers (+encryption password)
|
|
614
|
+
* @param request Instance of the Express.Request
|
|
615
|
+
* @param section Code of the target section
|
|
616
|
+
* @param saleId ID of the target sale
|
|
617
|
+
* @param id ID of the target article to be updated
|
|
618
|
+
* @param input New content to be overwritten
|
|
619
|
+
* @return The newly created content record
|
|
620
|
+
* @throw 400 bad request error when type of the input data is not valid
|
|
621
|
+
* @throw 401 unauthorized error when you've not logged in yet
|
|
622
|
+
* @throw 403 forbidden error when the article is not yours
|
|
623
|
+
*
|
|
624
|
+
* @controller ConsumerSaleReviewsController.update()
|
|
625
|
+
* @path PUT /consumers/:section/sales/:saleId/reviews/:id
|
|
626
|
+
* @nestia Generated by Nestia - https://github.com/samchon/nestia
|
|
627
|
+
*/
|
|
628
|
+
export function update
|
|
629
|
+
(
|
|
630
|
+
connection: IConnection,
|
|
631
|
+
section: string,
|
|
632
|
+
saleId: string,
|
|
633
|
+
id: number,
|
|
634
|
+
input: Primitive<update.Input>
|
|
635
|
+
): Promise<update.Output>
|
|
636
|
+
{
|
|
637
|
+
return Fetcher.fetch
|
|
638
|
+
(
|
|
639
|
+
connection,
|
|
640
|
+
update.ENCRYPTED,
|
|
641
|
+
update.METHOD,
|
|
642
|
+
update.path(section, saleId, id),
|
|
643
|
+
input,
|
|
644
|
+
update.stringify
|
|
645
|
+
);
|
|
646
|
+
}
|
|
647
|
+
export namespace update
|
|
648
|
+
{
|
|
649
|
+
export type Input = Primitive<ISaleReview.IStore>;
|
|
650
|
+
export type Output = Primitive<ISaleInquiry<ISaleReview.IContent>>;
|
|
444
651
|
|
|
445
|
-
|
|
652
|
+
export const METHOD = "PUT" as const;
|
|
653
|
+
export const PATH: string = "/consumers/:section/sales/:saleId/reviews/:id";
|
|
654
|
+
export const ENCRYPTED: Fetcher.IEncrypted = {
|
|
655
|
+
request: false,
|
|
656
|
+
response: false,
|
|
657
|
+
};
|
|
446
658
|
|
|
447
|
-
|
|
448
|
-
{
|
|
449
|
-
|
|
450
|
-
"/consumers/{section}/sales/{saleId}/comments/{articleId}": {
|
|
451
|
-
"post": {
|
|
452
|
-
"tags": [],
|
|
453
|
-
"parameters": [
|
|
454
|
-
{
|
|
455
|
-
"name": "section",
|
|
456
|
-
"in": "path",
|
|
457
|
-
"description": "Code of the target section",
|
|
458
|
-
"schema": {
|
|
459
|
-
"type": "string",
|
|
460
|
-
"nullable": false
|
|
461
|
-
},
|
|
462
|
-
"required": true
|
|
463
|
-
},
|
|
464
|
-
{
|
|
465
|
-
"name": "saleId",
|
|
466
|
-
"in": "path",
|
|
467
|
-
"description": "ID of the target sale",
|
|
468
|
-
"schema": {
|
|
469
|
-
"type": "number",
|
|
470
|
-
"nullable": false
|
|
471
|
-
},
|
|
472
|
-
"required": true
|
|
473
|
-
},
|
|
474
|
-
{
|
|
475
|
-
"name": "articleId",
|
|
476
|
-
"in": "path",
|
|
477
|
-
"description": "ID of the target article",
|
|
478
|
-
"schema": {
|
|
479
|
-
"type": "number",
|
|
480
|
-
"nullable": false
|
|
481
|
-
},
|
|
482
|
-
"required": true
|
|
483
|
-
}
|
|
484
|
-
],
|
|
485
|
-
"requestBody": {
|
|
486
|
-
"description": "Content to write",
|
|
487
|
-
"content": {
|
|
488
|
-
"application/json": {
|
|
489
|
-
"schema": {
|
|
490
|
-
"$ref": "#/components/schemas/ISaleComment.IStore"
|
|
491
|
-
}
|
|
492
|
-
}
|
|
493
|
-
},
|
|
494
|
-
"required": true
|
|
495
|
-
},
|
|
496
|
-
"responses": {
|
|
497
|
-
"201": {
|
|
498
|
-
"description": "Newly archived comment",
|
|
499
|
-
"content": {
|
|
500
|
-
"application/json": {
|
|
501
|
-
"schema": {
|
|
502
|
-
"$ref": "#/components/schemas/ISaleComment"
|
|
503
|
-
}
|
|
504
|
-
}
|
|
505
|
-
}
|
|
506
|
-
},
|
|
507
|
-
"400": {
|
|
508
|
-
"description": "bad request error when type of the input data is not valid"
|
|
509
|
-
},
|
|
510
|
-
"401": {
|
|
511
|
-
"description": "unauthorized error when you've not logged in yet"
|
|
512
|
-
},
|
|
513
|
-
"403": {
|
|
514
|
-
"description": "forbidden error when you're a seller and the sale is not yours"
|
|
515
|
-
},
|
|
516
|
-
"404": {
|
|
517
|
-
"description": "not found error when unable to find the matched record"
|
|
518
|
-
}
|
|
519
|
-
},
|
|
520
|
-
"description": "Store a new comment."
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
},
|
|
524
|
-
"components": {
|
|
525
|
-
"schemas": {
|
|
526
|
-
"ISaleComment": {
|
|
527
|
-
"type": "object",
|
|
528
|
-
"properties": {
|
|
529
|
-
"id": {
|
|
530
|
-
"description": "Primary Key.",
|
|
531
|
-
"type": "number",
|
|
532
|
-
"nullable": false
|
|
533
|
-
},
|
|
534
|
-
"writer_type": {
|
|
535
|
-
"description": "Type of the writer.",
|
|
536
|
-
"type": "string",
|
|
537
|
-
"nullable": false
|
|
538
|
-
},
|
|
539
|
-
"writer_name": {
|
|
540
|
-
"description": "Name of the writer.",
|
|
541
|
-
"type": "string",
|
|
542
|
-
"nullable": false
|
|
543
|
-
},
|
|
544
|
-
"contents": {
|
|
545
|
-
"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.",
|
|
546
|
-
"type": "array",
|
|
547
|
-
"items": {
|
|
548
|
-
"$ref": "#/components/schemas/ISaleComment.IContent"
|
|
549
|
-
},
|
|
550
|
-
"nullable": false
|
|
551
|
-
},
|
|
552
|
-
"created_at": {
|
|
553
|
-
"description": "Creation time.",
|
|
554
|
-
"type": "string",
|
|
555
|
-
"nullable": false
|
|
556
|
-
}
|
|
557
|
-
},
|
|
558
|
-
"nullable": false,
|
|
559
|
-
"required": [
|
|
560
|
-
"id",
|
|
561
|
-
"writer_type",
|
|
562
|
-
"writer_name",
|
|
563
|
-
"contents",
|
|
564
|
-
"created_at"
|
|
565
|
-
],
|
|
566
|
-
"description": "Comment wrote on an article."
|
|
567
|
-
},
|
|
568
|
-
"ISaleComment.IContent": {
|
|
569
|
-
"type": "object",
|
|
570
|
-
"properties": {
|
|
571
|
-
"created_at": {
|
|
572
|
-
"description": "Creation time.",
|
|
573
|
-
"type": "string",
|
|
574
|
-
"nullable": false
|
|
575
|
-
},
|
|
576
|
-
"body": {
|
|
577
|
-
"description": "Body of the content.",
|
|
578
|
-
"type": "string",
|
|
579
|
-
"nullable": false
|
|
580
|
-
}
|
|
581
|
-
},
|
|
582
|
-
"nullable": false,
|
|
583
|
-
"required": [
|
|
584
|
-
"created_at",
|
|
585
|
-
"body"
|
|
586
|
-
],
|
|
587
|
-
"description": "Content info."
|
|
588
|
-
},
|
|
589
|
-
"ISaleComment.IStore": {
|
|
590
|
-
"type": "object",
|
|
591
|
-
"properties": {
|
|
592
|
-
"body": {
|
|
593
|
-
"description": "Body of the content.",
|
|
594
|
-
"type": "string",
|
|
595
|
-
"nullable": false
|
|
596
|
-
}
|
|
597
|
-
},
|
|
598
|
-
"nullable": false,
|
|
599
|
-
"required": [
|
|
600
|
-
"body"
|
|
601
|
-
],
|
|
602
|
-
"description": "Store info."
|
|
603
|
-
}
|
|
659
|
+
export function path(section: string, saleId: string, id: number): string
|
|
660
|
+
{
|
|
661
|
+
return `/consumers/${section}/sales/${saleId}/reviews/${id}`;
|
|
604
662
|
}
|
|
605
|
-
|
|
663
|
+
export const stringify = createStringifier<Input>();
|
|
606
664
|
}
|
|
607
665
|
```
|
|
608
666
|
|
|
609
667
|
|
|
610
668
|
|
|
611
669
|
|
|
670
|
+
### Swagger
|
|
671
|
+
Building `Swagger` is also possible and even much powerful.
|
|
612
672
|
|
|
613
|
-
|
|
614
|
-
### Template Project
|
|
615
|
-
https://github.com/samchon/backend
|
|
673
|
+
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 `nestia`, everything is perfect. Route method, path and parameters are well-formed. Also, schema definitions are exactly matched with the pure interface type `ISaleArticleComment`. Of course, descriptive comments are perfectly resurrected in the `description` properties of the `swagger.json` file.
|
|
616
674
|
|
|
617
|
-
|
|
675
|
+
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 a generic typed DTO and controller come. The last file [union/swagger.json](https://editor.swagger.io/?url=https%3A%2F%2Fraw.githubusercontent.com%2Fsamchon%2Fnestia%2Fmaster%2Fdemo%2Funion%2Fswagger.json), there's no problem on the union type, either.
|
|
618
676
|
|
|
619
|
-
|
|
677
|
+
- View in the `Swagger Editor`
|
|
678
|
+
- [simple/swagger.json](https://editor.swagger.io/?url=https%3A%2F%2Fraw.githubusercontent.com%2Fsamchon%2Fnestia%2Fmaster%2Fdemo%2Fsimple%2Fswagger.json)
|
|
679
|
+
- [generic/swagger.json](https://editor.swagger.io/?url=https%3A%2F%2Fraw.githubusercontent.com%2Fsamchon%2Fnestia%2Fmaster%2Fdemo%2Fgeneric%2Fswagger.json)
|
|
680
|
+
- [union/swagger.json](https://editor.swagger.io/?url=https%3A%2F%2Fraw.githubusercontent.com%2Fsamchon%2Fnestia%2Fmaster%2Fdemo%2Funion%2Fswagger.json)
|
|
620
681
|
|
|
621
|
-
|
|
682
|
+

|
|
622
683
|
|
|
623
|
-
### Nestia-Helper
|
|
624
|
-
https://github.com/samchon/nestia-helper
|
|
625
684
|
|
|
626
|
-
Helper library of the `NestJS` with [nestia](https://github.com/samchon/nestia).
|
|
627
685
|
|
|
628
|
-
[nestia-helper](https://github.com/samchon/nestia-helper) is a type of helper library for `Nestia` by enhancing decorator functions. Also, all of the decorator functions provided by this [nestia-helper](https://github.com/samchon/nestia-helper) are all fully compatible with the [nestia](https://github.com/samchon/nestia), who can generate SDK library by analyzing NestJS controller classes in the compilation level.
|
|
629
686
|
|
|
630
|
-
|
|
687
|
+
## Configuration
|
|
688
|
+
Components | `nestia.config.ts` | `CLI` | `@nestjs/swagger`
|
|
689
|
+
-----------------------------|--------------------|-------|------------------
|
|
690
|
+
Swagger Generation | ✔ | ✔ | ✔
|
|
691
|
+
SDK Generation | ✔ | ✔ | ❌
|
|
692
|
+
2x faster `JSON.stringify()` | ✔ | ❌ | ❌
|
|
693
|
+
Type check in runtime | ✔ | ❌ | ❌
|
|
694
|
+
Custom compiler options | ✔ | ❌ | ❌
|
|
631
695
|
|
|
632
|
-
|
|
633
|
-
- [EncryptedController](https://github.com/samchon/nestia-helper#encryptedcontroller), [EncryptedModule](https://github.com/samchon/nestia-helper#encryptedmodule)
|
|
634
|
-
- [TypedRoute](https://github.com/samchon/nestia-helper#typedroute), [EncryptedRoute](https://github.com/samchon/nestia-helper#encryptedroute)
|
|
635
|
-
- [TypedParam](https://github.com/samchon/nestia-helper#typedparam), [EncryptedBody](https://github.com/samchon/nestia-helper#encryptedbody), [PlainBody](https://github.com/samchon/nestia-helper#plainbody)
|
|
636
|
-
- [ExceptionManager](https://github.com/samchon/nestia-helper#exceptionmanager)
|
|
696
|
+
`nestia` can configure generator options by two ways: CLI and configuration file.
|
|
637
697
|
|
|
638
|
-
|
|
639
|
-
https://github.com/samchon/safe-typeorm
|
|
698
|
+
At first, the CLI (Command Line Interface) is convenient, but does not support detailed options.
|
|
640
699
|
|
|
641
|
-
|
|
700
|
+
```sh
|
|
701
|
+
# BASIC COMMAND
|
|
702
|
+
npx nestia <sdk|swagger> <source_directories_or_patterns> \
|
|
703
|
+
--exclude <exclude_directory_or_pattern> \
|
|
704
|
+
--out <output_directory_or_file>
|
|
705
|
+
|
|
706
|
+
# EXAMPLES
|
|
707
|
+
npx nestia sdk "src/controllers" --out "src/api"
|
|
708
|
+
npx nestia swagger "src/**/*.controller.ts" --out "swagger.json"
|
|
709
|
+
npx nestia swagger "src/main/controllers" "src/sub/controllers" \
|
|
710
|
+
--exclude "src/main/test" \
|
|
711
|
+
--out "composite.swagger.json"
|
|
712
|
+
|
|
713
|
+
# ONLY WHEN NESTIA.CONFIG.TS EXISTS
|
|
714
|
+
npx nestia sdk
|
|
715
|
+
npx nestia swagger
|
|
716
|
+
```
|
|
717
|
+
|
|
718
|
+
Besides, the configuration file `nestia.config.ts` supports much detailed options.
|
|
719
|
+
|
|
720
|
+
The detailed options are listed up to the `IConfiguration` interface. You can utilize the `IConfiguration` type like below. If you want to know more about those options, please check the [Guide Documents](https://github.com/samchon/nestia/wiki/Configuration).
|
|
721
|
+
|
|
722
|
+
<details>
|
|
723
|
+
<summary> Read <code>IConfiguration</code> </summary>
|
|
724
|
+
|
|
725
|
+
```typescript
|
|
726
|
+
/**
|
|
727
|
+
* Definition for the `nestia.config.ts` file.
|
|
728
|
+
*
|
|
729
|
+
* @author Jeongho Nam - https://github.com/samchon
|
|
730
|
+
*/
|
|
731
|
+
export interface IConfiguration {
|
|
732
|
+
/**
|
|
733
|
+
* List of files or directories containing the `NestJS` controller classes.
|
|
734
|
+
*/
|
|
735
|
+
input: string | string[] | IConfiguration.IInput;
|
|
736
|
+
|
|
737
|
+
/**
|
|
738
|
+
* Output directory that SDK would be placed in.
|
|
739
|
+
*
|
|
740
|
+
* If not configured, you can't build the SDK library.
|
|
741
|
+
*/
|
|
742
|
+
output?: string;
|
|
743
|
+
|
|
744
|
+
/**
|
|
745
|
+
* Compiler options for the TypeScript.
|
|
746
|
+
*
|
|
747
|
+
* If you've omitted this property or the assigned property cannot fully cover the
|
|
748
|
+
* `tsconfig.json`, the properties from the `tsconfig.json` would be assigned to here.
|
|
749
|
+
* Otherwise, this property has been configured and it's detailed values are different
|
|
750
|
+
* with the `tsconfig.json`, this property values would be overwritten.
|
|
751
|
+
*
|
|
752
|
+
* ```typescript
|
|
753
|
+
* import ts from "typescript";
|
|
754
|
+
*
|
|
755
|
+
* const tsconfig: ts.TsConfig;
|
|
756
|
+
* const nestiaConfig: IConfiguration;
|
|
757
|
+
*
|
|
758
|
+
* const compilerOptions: ts.CompilerOptions = {
|
|
759
|
+
* ...tsconfig.compilerOptions,
|
|
760
|
+
* ...(nestiaConfig.compilerOptions || {})
|
|
761
|
+
* }
|
|
762
|
+
* ```
|
|
763
|
+
*/
|
|
764
|
+
compilerOptions?: ts.CompilerOptions;
|
|
765
|
+
|
|
766
|
+
/**
|
|
767
|
+
* Whether to assert parameter types or not.
|
|
768
|
+
*
|
|
769
|
+
* If you configure this property to be `true`, all of the function parameters would be
|
|
770
|
+
* checked through the [typescript-is](https://github.com/woutervh-/typescript-is). This
|
|
771
|
+
* option would make your SDK library slower, but would be much safer in the type level
|
|
772
|
+
* even in the runtime environment.
|
|
773
|
+
*/
|
|
774
|
+
assert?: boolean;
|
|
775
|
+
|
|
776
|
+
/**
|
|
777
|
+
* Whether to optimize JSON string conversion 2x faster or not.
|
|
778
|
+
*
|
|
779
|
+
* If you configure this property to be `true`, the SDK library would utilize the
|
|
780
|
+
* [typescript-json](https://github.com/samchon/typescript-json) and the JSON string
|
|
781
|
+
* conversion speed really be 2x faster.
|
|
782
|
+
*/
|
|
783
|
+
json?: boolean;
|
|
784
|
+
|
|
785
|
+
/**
|
|
786
|
+
* Building `swagger.json` is also possible.
|
|
787
|
+
*
|
|
788
|
+
* If not specified, you can't build the `swagger.json`.
|
|
789
|
+
*/
|
|
790
|
+
swagger?: IConfiguration.ISwagger;
|
|
791
|
+
}
|
|
792
|
+
export namespace IConfiguration
|
|
793
|
+
{
|
|
794
|
+
/**
|
|
795
|
+
* List of files or directories to include or exclude to specifying the `NestJS`
|
|
796
|
+
* controllers.
|
|
797
|
+
*/
|
|
798
|
+
export interface IInput {
|
|
799
|
+
/**
|
|
800
|
+
* List of files or directories containing the `NestJS` controller classes.
|
|
801
|
+
*/
|
|
802
|
+
include: string[];
|
|
803
|
+
|
|
804
|
+
/**
|
|
805
|
+
* List of files or directories to be excluded.
|
|
806
|
+
*/
|
|
807
|
+
exclude?: string[];
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
/**
|
|
811
|
+
* Building `swagger.json` is also possible.
|
|
812
|
+
*/
|
|
813
|
+
export interface ISwagger {
|
|
814
|
+
/**
|
|
815
|
+
* Output path of the `swagger.json`.
|
|
816
|
+
*
|
|
817
|
+
* If you've configured only directory, the file name would be the `swagger.json`.
|
|
818
|
+
* Otherwise you've configured the full path with file name and extension, the
|
|
819
|
+
* `swagger.json` file would be renamed to it.
|
|
820
|
+
*/
|
|
821
|
+
output: string;
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
```
|
|
825
|
+
</details>
|
|
826
|
+
|
|
827
|
+
```typescript
|
|
828
|
+
import type { IConfiguration } from "nestia";
|
|
829
|
+
|
|
830
|
+
export const NESTIA_CONFIG: IConfiguration = {
|
|
831
|
+
input: "./src/controllers",
|
|
832
|
+
output: "./src/api",
|
|
833
|
+
json: true,
|
|
834
|
+
swagger: {
|
|
835
|
+
output: "./public/swagger.json"
|
|
836
|
+
}
|
|
837
|
+
};
|
|
838
|
+
export default NESTIA_CONFIG;
|
|
839
|
+
```
|
|
840
|
+
|
|
841
|
+
|
|
842
|
+
|
|
843
|
+
|
|
844
|
+
## Appendix
|
|
845
|
+
### Dependencies of the SDK
|
|
846
|
+
An SDK library generated by `nestia` requires [nestia-fetcher](https://github.com/samchon/nestia-fetcher) module. Also, [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` configuration file.
|
|
847
|
+
|
|
848
|
+
The `npx nestia install` command installs those dependencies with the `package.json` configuration.
|
|
849
|
+
|
|
850
|
+
```bash
|
|
851
|
+
# MOVE TO THE DISTRIBUTION DIRECTORY
|
|
852
|
+
cd packages/api
|
|
853
|
+
|
|
854
|
+
# INSTALL DEPENDENCIES OF THE SDK
|
|
855
|
+
npx nestia install
|
|
856
|
+
```
|
|
857
|
+
|
|
858
|
+
### Template Repository
|
|
859
|
+
https://github.com/samchon/backend
|
|
642
860
|
|
|
643
|
-
|
|
861
|
+
I support template backend project using this `nestia` library, `samchon/backend`.
|
|
644
862
|
|
|
645
|
-
|
|
646
|
-
- Errors would be detected in the **compilation** level
|
|
647
|
-
- **Auto Completion** would be provided
|
|
648
|
-
- **Type Hint** would be supported
|
|
649
|
-
- You can implement [**App-join**](https://github.com/samchon/safe-typeorm#app-join-builder) very conveniently
|
|
650
|
-
- When [**SELECT**ing for **JSON** conversion](https://github.com/samchon/safe-typeorm#json-select-builder)
|
|
651
|
-
- [**App-Join**](https://github.com/samchon/safe-typeorm#app-join-builder) with the related entities would be automatically done
|
|
652
|
-
- Exact JSON **type** would be automatically **deduced**
|
|
653
|
-
- The **performance** would be **automatically tuned**
|
|
654
|
-
- When [**INSERT**](https://github.com/samchon/safe-typeorm#insert-collection)ing records
|
|
655
|
-
- Sequence of tables would be automatically sorted by analyzing dependencies
|
|
656
|
-
- The **performance** would be **automatically tuned**
|
|
863
|
+
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 `nestia` and how to distribute the SDK library thorugh the NPM module.
|
|
657
864
|
|
|
658
|
-
|
|
865
|
+
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.
|
|
659
866
|
|
|
660
867
|
### Archidraw
|
|
661
868
|
https://www.archisketch.com/
|
|
662
869
|
|
|
663
870
|
I have special thanks to the Archidraw, where I'm working for.
|
|
664
871
|
|
|
665
|
-
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
|
|
872
|
+
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.
|
|
873
|
+
|
|
874
|
+
> 저희 회사 "아키드로우" 에서, 삼촌과 함께 일할 프론트 개발자 분들을, 최고의 대우로 모십니다.
|
|
875
|
+
>
|
|
876
|
+
> "아키드로우" 는 3D (인테리어) 에디터 및 이에 관한 파생 솔루션들을 만드는 회사입니다. 다만 저희 회사의 주력 제품이 3D 에디터라 하여, 반드시 3D 내지 랜더링에 능숙해야 하는 것은 아니니, 일반적인 프론트 개발자 분들도 망설임없이 지원해주십시오.
|
|
877
|
+
>
|
|
878
|
+
> 그리고 저희 회사는 분위기가 다들 친하고 즐겁게 지내는 분위기입니다. 더하여 위 `nestia` 나 [typescript-json](https://github.com/samchon/typescript-json) 및 [payments](https://github.com/archidraw/payments) 등, 제법 합리적(?)이고 재미난 프로젝트들을 다양하게 체험해보실 수 있습니다.
|
|
879
|
+
>
|
|
880
|
+
> - 회사소개서: [archidraw.pdf](https://github.com/archidraw/payments/files/7696710/archidraw.pdf)
|
|
881
|
+
> - 기술 스택: React + TypeScript
|
|
882
|
+
> - 이력서: 자유 양식
|
|
883
|
+
> - 지원처: samchon@archisketch.com
|