@nxgt/typespec 0.10.0 → 0.11.0
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 +32 -6
- package/docs/README.md +1 -0
- package/docs/guide/auth.md +1 -1
- package/docs/guide/columns.md +4 -4
- package/docs/guide/errors.md +1 -1
- package/docs/guide/getting-started.md +16 -7
- package/docs/guide/headers.md +2 -2
- package/docs/guide/operation-ids.md +1 -1
- package/docs/guide/requests.md +145 -0
- package/docs/roadmap.md +8 -0
- package/docs/troubleshooting.md +1 -1
- package/lib/main.tsp +1 -0
- package/lib/requests.tsp +36 -0
- package/package.json +1 -1
- package/templates/nxgt/main.tsp +11 -4
package/README.md
CHANGED
|
@@ -125,7 +125,7 @@ namespace Blog;
|
|
|
125
125
|
@route("/posts")
|
|
126
126
|
interface Posts {
|
|
127
127
|
@get getPost(@path postId: string): Post | NotFound | Unauthorized;
|
|
128
|
-
@post createPost(@body post:
|
|
128
|
+
@post createPost(@body post: CreateRequest<Post>): {
|
|
129
129
|
@statusCode _: 201;
|
|
130
130
|
@body post: Post;
|
|
131
131
|
} | BadRequest | Conflict | ErrorResponse<503>;
|
|
@@ -175,8 +175,9 @@ an alias leaves out:
|
|
|
175
175
|
interface Users {
|
|
176
176
|
@get list(...PageParameters): Page<User> | ListErrors;
|
|
177
177
|
@get get(@path id: uuid): User | GetErrors;
|
|
178
|
-
@post create(@body user: User): User | CreateErrors | AuthErrors | TooManyRequests;
|
|
179
|
-
@
|
|
178
|
+
@post create(@body user: CreateRequest<User>): User | CreateErrors | AuthErrors | TooManyRequests;
|
|
179
|
+
@put update(@path id: uuid, @body user: UpdateRequest<User>): User | UpdateErrors;
|
|
180
|
+
@patch patch(@path id: uuid, @body user: PatchRequest<User>): User | UpdateErrors;
|
|
180
181
|
@delete delete(@path id: uuid): NoContentResponse | DeleteErrors;
|
|
181
182
|
}
|
|
182
183
|
```
|
|
@@ -184,6 +185,26 @@ interface Users {
|
|
|
184
185
|
An alias is exactly its union, so the OpenAPI it emits is the same as
|
|
185
186
|
writing the responses out.
|
|
186
187
|
|
|
188
|
+
### Request bodies
|
|
189
|
+
|
|
190
|
+
`CreateRequest<T>`, `UpdateRequest<T>` and `PatchRequest<T>` are TypeSpec's own
|
|
191
|
+
`Create`, `Update` and `MergePatchUpdate`, named so the generated types and
|
|
192
|
+
schemas read `CreatePostRequest`, `UpdatePostRequest` and `PatchPostRequest`:
|
|
193
|
+
|
|
194
|
+
```tsp
|
|
195
|
+
@post createPost(@body post: CreateRequest<Post>): {
|
|
196
|
+
@statusCode _: 201;
|
|
197
|
+
@body post: Post;
|
|
198
|
+
} | BadRequest;
|
|
199
|
+
@put updatePost(@path postId: uuid, @body post: UpdateRequest<Post>): Post | BadRequest | NotFound;
|
|
200
|
+
@patch patchPost(@path postId: uuid, @body post: PatchRequest<Post>): Post | BadRequest | NotFound;
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
A `PUT` body keeps each property as optional as the model declares it; a
|
|
204
|
+
`PATCH` body is a JSON Merge Patch, `application/merge-patch+json`, with
|
|
205
|
+
every property optional. `Create<Post, "New{name}">` names a body your own
|
|
206
|
+
way. See [Request bodies](docs/guide/requests.md).
|
|
207
|
+
|
|
187
208
|
### Pagination
|
|
188
209
|
|
|
189
210
|
Spread the query of a page into the operation's parameters, and answer the
|
|
@@ -293,9 +314,9 @@ model Post {
|
|
|
293
314
|
| --- | --- | --- | --- |
|
|
294
315
|
| `uuid` | the schema `UUID`, `format: uuid`, a pattern for versions 1–8 | `UUID`, `z.guid().regex(…)` | `id()` |
|
|
295
316
|
| `email` | `emailAddress`, the schema `EmailAddress`, `format: email` | `EmailAddress`, `z.string().regex(…)` | |
|
|
296
|
-
| `...Timestamps` | `createdAt`, `updatedAt`, read-only | left out of `
|
|
317
|
+
| `...Timestamps` | `createdAt`, `updatedAt`, read-only | left out of `CreateRequest<T>` and updates | `timestamps()` |
|
|
297
318
|
| `...SoftDelete` | `deletedAt: utcDateTime \| null`, read-only | | `softDelete()` |
|
|
298
|
-
| `...Versioned` | `version: integer`, read and update | kept in `
|
|
319
|
+
| `...Versioned` | `version: integer`, read and update | kept in `UpdateRequest<T>` and `PatchRequest<T>` | `version()` |
|
|
299
320
|
| `...Actors<Id = uuid>` | `createdBy`, `updatedBy`, `deletedBy`, `Id \| null`, read-only | | `actors()` |
|
|
300
321
|
|
|
301
322
|
Every scalar of [`@nxgt/graphql-scalars`](https://www.npmjs.com/package/@nxgt/graphql-scalars)
|
|
@@ -326,7 +347,7 @@ Spread the headers of an idempotent write and of a rate limit into an
|
|
|
326
347
|
operation and its replies:
|
|
327
348
|
|
|
328
349
|
```tsp
|
|
329
|
-
@post createPost(...IdempotencyKeyHeader, @body post:
|
|
350
|
+
@post createPost(...IdempotencyKeyHeader, @body post: CreateRequest<Post>): {
|
|
330
351
|
@statusCode _: 201;
|
|
331
352
|
...IdempotentReplayedHeader;
|
|
332
353
|
...RateLimitHeaders;
|
|
@@ -507,6 +528,9 @@ In the ruleset `@nxgt/typespec/recommended`; each is a warning, and named
|
|
|
507
528
|
| `Page<Item>` | `{ items: Item[], total, page, pageSize, pageCount }`, the schema `<Item>Page` |
|
|
508
529
|
| `CursorPageParameters` | the query `after` and `limit` |
|
|
509
530
|
| `CursorPage<Item>` | `{ items: Item[], nextCursor: string \| null }`, the schema `<Item>CursorPage` |
|
|
531
|
+
| `CreateRequest<T>` | `Create<T, "Create{name}Request">`: the body of a create, `CreatePostRequest` |
|
|
532
|
+
| `UpdateRequest<T>` | `Update<T, "Update{name}Request">`: the body of a `PUT`, `UpdatePostRequest` |
|
|
533
|
+
| `PatchRequest<T>` | `MergePatchUpdate<T, "Patch{name}Request">`: a merge patch, all optional, `PatchPostRequest` |
|
|
510
534
|
| `ValidationIssue` | `{ target: ValidationTarget, path: (string \| integer)[], code: string, message: string }` |
|
|
511
535
|
| `ValidationTarget` | `"param" \| "query" \| "header" \| "json" \| "form" \| "body" \| "response"` |
|
|
512
536
|
|
|
@@ -653,6 +677,8 @@ envelope and the rate limit's headers.
|
|
|
653
677
|
`Retry-After`;
|
|
654
678
|
- [Sorting](docs/guide/sorting.md): `orderBy` and `direction` beside a
|
|
655
679
|
list's filters, and the `@nxgt/drizzle` call they map to;
|
|
680
|
+
- [Request bodies](docs/guide/requests.md): `CreateRequest`, `UpdateRequest`
|
|
681
|
+
and `PatchRequest`, and naming your own;
|
|
656
682
|
- [Operation ids](docs/guide/operation-ids.md): `@operationIds`, each
|
|
657
683
|
operation named as written or a verb with its resource, the method each
|
|
658
684
|
verb is sent with, the options, and the ids it refuses;
|
package/docs/README.md
CHANGED
|
@@ -5,6 +5,7 @@ The [package README](../README.md) is the short version.
|
|
|
5
5
|
| Page | Read it when |
|
|
6
6
|
| --- | --- |
|
|
7
7
|
| [Getting started](guide/getting-started.md) | starting a new API project from the `tsp init` template, up to a Hono route |
|
|
8
|
+
| [Request bodies](guide/requests.md) | naming the body of a create, a `PUT` and a `PATCH`: `CreatePostRequest`, `UpdatePostRequest`, `PatchPostRequest` |
|
|
8
9
|
| [Error replies](guide/errors.md) | declaring what an operation answers when it fails |
|
|
9
10
|
| [Pagination](guide/pagination.md) | listing a collection a page at a time |
|
|
10
11
|
| [Authentication](guide/auth.md) | protecting an operation with a `@nxgt/janus` session |
|
package/docs/guide/auth.md
CHANGED
|
@@ -91,7 +91,7 @@ operation can declare the guard's 401 and the handler's 403:
|
|
|
91
91
|
|
|
92
92
|
```tsp
|
|
93
93
|
@useAuth(JanusAuth)
|
|
94
|
-
@post createPost(@body post:
|
|
94
|
+
@post createPost(@body post: CreateRequest<Post>): {
|
|
95
95
|
@statusCode _: 201;
|
|
96
96
|
@body post: Post;
|
|
97
97
|
} | BadRequest | AuthenticationRequired | Forbidden;
|
package/docs/guide/columns.md
CHANGED
|
@@ -80,9 +80,9 @@ model Post {
|
|
|
80
80
|
| `Versioned` | `version: integer`, at least 0 | read, update | `version()` |
|
|
81
81
|
| `Actors<Id = uuid>` | `createdBy`, `updatedBy`, `deletedBy: Id \| null` | read | `actors()` |
|
|
82
82
|
|
|
83
|
-
The server sets every read-only column, so `
|
|
84
|
-
`
|
|
85
|
-
`
|
|
83
|
+
The server sets every read-only column, so `CreateRequest<Post>` and
|
|
84
|
+
`PatchRequest<Post>` leave them out, and the generated `CreatePostRequest` and
|
|
85
|
+
`PatchPostRequest` have no `createdAt` a client could send.
|
|
86
86
|
`Actors<string>` or `Actors<integer>` match `actors('text')` or
|
|
87
87
|
`actors('integer')`.
|
|
88
88
|
|
|
@@ -94,7 +94,7 @@ that version, and throws `OptimisticLockError` otherwise. Declare the
|
|
|
94
94
|
`Conflict` it becomes:
|
|
95
95
|
|
|
96
96
|
```tsp
|
|
97
|
-
@patch
|
|
97
|
+
@patch patchPost(@path postId: uuid, @body post: PatchRequest<Post>):
|
|
98
98
|
Post | BadRequest | NotFound | Conflict;
|
|
99
99
|
```
|
|
100
100
|
|
package/docs/guide/errors.md
CHANGED
|
@@ -143,7 +143,7 @@ A handler may send a 400 of its own, without `issues`. `BadRequestBody`
|
|
|
143
143
|
describes both, with `issues` optional:
|
|
144
144
|
|
|
145
145
|
```tsp
|
|
146
|
-
@patch
|
|
146
|
+
@patch patchPost(@path postId: string, @body post: PatchRequest<Post>): Post | BadRequest;
|
|
147
147
|
```
|
|
148
148
|
|
|
149
149
|
The generator declares the validators' 400 itself, as `ValidationErrorBody`,
|
|
@@ -99,13 +99,16 @@ interface Users {
|
|
|
99
99
|
get(@path id: uuid): User | GetErrors;
|
|
100
100
|
|
|
101
101
|
@post
|
|
102
|
-
create(@body user:
|
|
102
|
+
create(@body user: CreateRequest<User>): {
|
|
103
103
|
@statusCode _: 201;
|
|
104
104
|
@body user: User;
|
|
105
105
|
} | CreateErrors;
|
|
106
106
|
|
|
107
|
+
@put
|
|
108
|
+
update(@path id: uuid, @body user: UpdateRequest<User>): User | UpdateErrors;
|
|
109
|
+
|
|
107
110
|
@patch
|
|
108
|
-
|
|
111
|
+
patch(@path id: uuid, @body user: PatchRequest<User>): User | UpdateErrors;
|
|
109
112
|
|
|
110
113
|
@delete
|
|
111
114
|
delete(@path id: uuid): NoContentResponse | DeleteErrors;
|
|
@@ -113,7 +116,10 @@ interface Users {
|
|
|
113
116
|
```
|
|
114
117
|
|
|
115
118
|
`@operationIds` names the operations `listUsers`, `getUser`, `createUser`,
|
|
116
|
-
`updateUser` and `deleteUser` ([Operation ids](operation-ids.md)).
|
|
119
|
+
`updateUser`, `patchUser` and `deleteUser` ([Operation ids](operation-ids.md)).
|
|
120
|
+
`update` replaces the whole user with a `PUT` (`UpdateRequest`), `patch`
|
|
121
|
+
changes some of it with a merge patch (`PatchRequest`); see
|
|
122
|
+
[Request bodies](requests.md).
|
|
117
123
|
|
|
118
124
|
```ts
|
|
119
125
|
// openapi-codegen.config.ts
|
|
@@ -250,13 +256,16 @@ interface Pets {
|
|
|
250
256
|
get(@path id: uuid): Pet | GetErrors;
|
|
251
257
|
|
|
252
258
|
@post
|
|
253
|
-
create(@body pet:
|
|
259
|
+
create(@body pet: CreateRequest<Pet>): {
|
|
254
260
|
@statusCode _: 201;
|
|
255
261
|
@body pet: Pet;
|
|
256
262
|
} | CreateErrors;
|
|
257
263
|
|
|
264
|
+
@put
|
|
265
|
+
update(@path id: uuid, @body pet: UpdateRequest<Pet>): Pet | UpdateErrors;
|
|
266
|
+
|
|
258
267
|
@patch
|
|
259
|
-
|
|
268
|
+
patch(@path id: uuid, @body pet: PatchRequest<Pet>): Pet | UpdateErrors;
|
|
260
269
|
|
|
261
270
|
@delete
|
|
262
271
|
delete(@path id: uuid): NoContentResponse | DeleteErrors;
|
|
@@ -264,8 +273,8 @@ interface Pets {
|
|
|
264
273
|
```
|
|
265
274
|
|
|
266
275
|
The operation names stay; `@operationIds` takes the resource from the
|
|
267
|
-
interface, so the ids become `listPets`, `getPet`, `createPet`, `updatePet
|
|
268
|
-
and `deletePet`. Run `tsp compile .` and `nxgt-openapi generate` again, and
|
|
276
|
+
interface, so the ids become `listPets`, `getPet`, `createPet`, `updatePet`,
|
|
277
|
+
`patchPet` and `deletePet`. Run `tsp compile .` and `nxgt-openapi generate` again, and
|
|
269
278
|
the generated types, `Pet` and `PetPage`, follow. A name the singular rule
|
|
270
279
|
does not know takes `@operationIds(#{ singular: "…" })` on the interface
|
|
271
280
|
([Operation ids](operation-ids.md)).
|
package/docs/guide/headers.md
CHANGED
|
@@ -19,7 +19,7 @@ namespace Blog;
|
|
|
19
19
|
|
|
20
20
|
@route("/posts")
|
|
21
21
|
interface Posts {
|
|
22
|
-
@post createPost(...IdempotencyKeyHeader, @body post:
|
|
22
|
+
@post createPost(...IdempotencyKeyHeader, @body post: CreateRequest<Post>): {
|
|
23
23
|
@statusCode _: 201;
|
|
24
24
|
...IdempotentReplayedHeader;
|
|
25
25
|
@body post: Post;
|
|
@@ -48,7 +48,7 @@ lock, gets one 409 with the description of the first, and the library warns,
|
|
|
48
48
|
## A rate limit
|
|
49
49
|
|
|
50
50
|
```tsp
|
|
51
|
-
@post createPost(@body post:
|
|
51
|
+
@post createPost(@body post: CreateRequest<Post>): {
|
|
52
52
|
@statusCode _: 201;
|
|
53
53
|
...RateLimitHeaders;
|
|
54
54
|
@body post: Post;
|
|
@@ -147,7 +147,7 @@ Give the operation its verb's method, or a name that is not a verb, which
|
|
|
147
147
|
is then its id as written:
|
|
148
148
|
|
|
149
149
|
```tsp
|
|
150
|
-
@
|
|
150
|
+
@put @route("/{id}") update(@path id: uuid, @body user: User): User; // updateUser
|
|
151
151
|
@post @route("/lookup") lookUpUser(@body id: uuid): User; // lookUpUser
|
|
152
152
|
```
|
|
153
153
|
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# Request bodies
|
|
2
|
+
|
|
3
|
+
`CreateRequest<T>`, `UpdateRequest<T>` and `PatchRequest<T>` are TypeSpec's own
|
|
4
|
+
`Create`, `Update` and `MergePatchUpdate` with a name template, so the body of
|
|
5
|
+
each write is a component with a name you can read: `CreatePostRequest`.
|
|
6
|
+
|
|
7
|
+
## Why names
|
|
8
|
+
|
|
9
|
+
TypeSpec's templates name what they emit from the model: `Create<Post>` is
|
|
10
|
+
the component `CreatePost` and `MergePatchUpdate<Post>` is
|
|
11
|
+
`PostMergePatchUpdate`. Two conventions in one spec, and a type in the
|
|
12
|
+
generated client that reads unlike the rest. The aliases give each body the
|
|
13
|
+
name `<Verb><Model>Request`, and `@nxgt/openapi-codegen` generates the types
|
|
14
|
+
and the Zod schemas under it.
|
|
15
|
+
|
|
16
|
+
## The smallest example
|
|
17
|
+
|
|
18
|
+
```tsp
|
|
19
|
+
import "@typespec/http";
|
|
20
|
+
import "@nxgt/typespec";
|
|
21
|
+
|
|
22
|
+
using Http;
|
|
23
|
+
using Nxgt;
|
|
24
|
+
|
|
25
|
+
namespace Blog;
|
|
26
|
+
|
|
27
|
+
model Post {
|
|
28
|
+
@visibility(Lifecycle.Read) id: uuid;
|
|
29
|
+
@minLength(1) title: string;
|
|
30
|
+
body: string;
|
|
31
|
+
tags?: string[];
|
|
32
|
+
...Timestamps;
|
|
33
|
+
...Versioned;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
@route("/posts")
|
|
37
|
+
interface Posts {
|
|
38
|
+
@post createPost(@body post: CreateRequest<Post>): {
|
|
39
|
+
@statusCode _: 201;
|
|
40
|
+
@body post: Post;
|
|
41
|
+
} | BadRequest;
|
|
42
|
+
|
|
43
|
+
@put updatePost(@path postId: uuid, @body post: UpdateRequest<Post>):
|
|
44
|
+
Post | BadRequest | NotFound | Conflict;
|
|
45
|
+
|
|
46
|
+
@patch patchPost(@path postId: uuid, @body post: PatchRequest<Post>):
|
|
47
|
+
Post | BadRequest | NotFound | Conflict;
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
| Alias | Is | Component | Content type | Properties |
|
|
52
|
+
| --- | --- | --- | --- | --- |
|
|
53
|
+
| `CreateRequest<T>` | `Create<T, "Create{name}Request">` | `CreatePostRequest` | `application/json` | those visible on create, as `T` declares them |
|
|
54
|
+
| `UpdateRequest<T>` | `Update<T, "Update{name}Request">` | `UpdatePostRequest` | `application/json` | those visible on update, as `T` declares them |
|
|
55
|
+
| `PatchRequest<T>` | `MergePatchUpdate<T, "Patch{name}Request">` | `PatchPostRequest` | `application/merge-patch+json` | those visible on update, all optional |
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
import type {
|
|
59
|
+
CreatePostRequest,
|
|
60
|
+
PatchPostRequest,
|
|
61
|
+
UpdatePostRequest,
|
|
62
|
+
} from './generated/types';
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
```tsp
|
|
66
|
+
alias CreateRequest<T extends Reflection.Model> = Create<T, "Create{name}Request">;
|
|
67
|
+
alias UpdateRequest<T extends Reflection.Model> = Update<T, "Update{name}Request">;
|
|
68
|
+
alias PatchRequest<T extends Reflection.Model> = MergePatchUpdate<T, "Patch{name}Request">;
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Read-only properties (`id`, `Timestamps`, `Actors`) are in none of the three.
|
|
72
|
+
`version` from `Versioned` is read and update, so it is in
|
|
73
|
+
`UpdatePostRequest` and `PatchPostRequest`, and not in `CreatePostRequest`.
|
|
74
|
+
|
|
75
|
+
## `PUT` replaces, `PATCH` merges
|
|
76
|
+
|
|
77
|
+
`UpdateRequest<Post>` is the whole resource: each property is as optional as
|
|
78
|
+
`Post` declares it, so `title` is required and `tags?` is not. Send it with
|
|
79
|
+
`@put`.
|
|
80
|
+
|
|
81
|
+
`PatchRequest<Post>` is a JSON Merge Patch: every property is optional, and a
|
|
82
|
+
nullable one is sent as `null` to clear it. The body's content type is
|
|
83
|
+
`application/merge-patch+json`, where the others are `application/json`.
|
|
84
|
+
|
|
85
|
+
```yaml
|
|
86
|
+
# openapi.yaml (excerpt)
|
|
87
|
+
PUT:
|
|
88
|
+
requestBody:
|
|
89
|
+
content:
|
|
90
|
+
application/json:
|
|
91
|
+
schema:
|
|
92
|
+
$ref: '#/components/schemas/UpdatePostRequest'
|
|
93
|
+
PATCH:
|
|
94
|
+
requestBody:
|
|
95
|
+
content:
|
|
96
|
+
application/merge-patch+json:
|
|
97
|
+
schema:
|
|
98
|
+
$ref: '#/components/schemas/PatchPostRequest'
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
A client sending a patch sets the header `Content-Type:
|
|
102
|
+
application/merge-patch+json`. The component `UpdatePostRequest` requires
|
|
103
|
+
`title`; `PatchPostRequest` requires nothing.
|
|
104
|
+
|
|
105
|
+
## Nested models
|
|
106
|
+
|
|
107
|
+
A nested model the template filters is named by the same template; one it
|
|
108
|
+
leaves whole keeps its name.
|
|
109
|
+
|
|
110
|
+
```tsp
|
|
111
|
+
model Address {
|
|
112
|
+
@visibility(Lifecycle.Read) id: uuid;
|
|
113
|
+
street: string;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
model Author {
|
|
117
|
+
@visibility(Lifecycle.Read) id: uuid;
|
|
118
|
+
name: string;
|
|
119
|
+
address: Address;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
@post createAuthor(@body author: CreateRequest<Author>): Author;
|
|
123
|
+
// CreateAuthorRequest.address is CreateAddressRequest, without `id`
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
A model with no property the filter changes, such as a `Tag` with only
|
|
127
|
+
`name`, is referenced under its own name, `Tag`.
|
|
128
|
+
|
|
129
|
+
## Your own name template
|
|
130
|
+
|
|
131
|
+
The aliases fix the convention. Another one is the same template, inline, and
|
|
132
|
+
`Create<T>` and `MergePatchUpdate<T>` stay usable:
|
|
133
|
+
|
|
134
|
+
```tsp
|
|
135
|
+
@post createPost(@body post: Create<Post, "New{name}">): Post; // NewPost
|
|
136
|
+
@patch patchPost(@path postId: uuid, @body post: MergePatchUpdate<Post, "{name}Changes">): Post;
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
`{name}` is the model's name. Without a template, TypeSpec names the
|
|
140
|
+
components `CreatePost` and `PostMergePatchUpdate`.
|
|
141
|
+
|
|
142
|
+
## Next
|
|
143
|
+
|
|
144
|
+
The columns a request leaves out are on [Scalars and columns](columns.md); the
|
|
145
|
+
errors a write answers on [Error replies](errors.md).
|
package/docs/roadmap.md
CHANGED
|
@@ -26,6 +26,14 @@ Nothing in progress.
|
|
|
26
26
|
|
|
27
27
|
## Shipped
|
|
28
28
|
|
|
29
|
+
### 0.11.0
|
|
30
|
+
|
|
31
|
+
- **Request body aliases.** `CreateRequest<T>`, `UpdateRequest<T>` and
|
|
32
|
+
`PatchRequest<T>` are TypeSpec's `Create`, `Update` and `MergePatchUpdate`
|
|
33
|
+
named `Create<T>Request`, `Update<T>Request` and `Patch<T>Request`, so the
|
|
34
|
+
generated types and schemas share one convention. `Create<T>` and
|
|
35
|
+
`MergePatchUpdate<T>` stay usable.
|
|
36
|
+
|
|
29
37
|
### 0.10.0
|
|
30
38
|
|
|
31
39
|
- **Every scalar of `@nxgt/graphql-scalars`.** The scalars, under the same
|
package/docs/troubleshooting.md
CHANGED
|
@@ -13,7 +13,7 @@ one name with `name_collision`.
|
|
|
13
13
|
optional, and the generator adds its own 400 beside it.
|
|
14
14
|
|
|
15
15
|
```tsp
|
|
16
|
-
@patch
|
|
16
|
+
@patch patchPost(@path postId: string, @body post: PatchRequest<Post>): Post | BadRequest;
|
|
17
17
|
```
|
|
18
18
|
|
|
19
19
|
## `Couldn't resolve import "@nxgt/typespec"`
|
package/lib/main.tsp
CHANGED
package/lib/requests.tsp
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import "@typespec/http";
|
|
2
|
+
|
|
3
|
+
using Http;
|
|
4
|
+
|
|
5
|
+
namespace Nxgt;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The body of a create: the properties of `T` visible on create, named
|
|
9
|
+
* `Create<T>Request` (`CreatePostRequest`). A nested model filtered the same
|
|
10
|
+
* way is named alike (`CreateAddressRequest`); one left whole keeps its name.
|
|
11
|
+
*/
|
|
12
|
+
alias CreateRequest<T extends Reflection.Model> = Create<
|
|
13
|
+
T,
|
|
14
|
+
"Create{name}Request"
|
|
15
|
+
>;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* The body of a full update, as a `PUT` replaces the resource: the properties
|
|
19
|
+
* of `T` visible on update, each as optional as `T` declares it, named
|
|
20
|
+
* `Update<T>Request` (`UpdatePostRequest`).
|
|
21
|
+
*/
|
|
22
|
+
alias UpdateRequest<T extends Reflection.Model> = Update<
|
|
23
|
+
T,
|
|
24
|
+
"Update{name}Request"
|
|
25
|
+
>;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* The body of a partial update, as a JSON Merge Patch
|
|
29
|
+
* (`application/merge-patch+json`): the properties of `T` visible on update,
|
|
30
|
+
* all optional, a nullable one sent as `null` to clear it, named
|
|
31
|
+
* `Patch<T>Request` (`PatchPostRequest`).
|
|
32
|
+
*/
|
|
33
|
+
alias PatchRequest<T extends Reflection.Model> = MergePatchUpdate<
|
|
34
|
+
T,
|
|
35
|
+
"Patch{name}Request"
|
|
36
|
+
>;
|
package/package.json
CHANGED
package/templates/nxgt/main.tsp
CHANGED
|
@@ -50,16 +50,23 @@ interface Users {
|
|
|
50
50
|
|
|
51
51
|
/** Creates a user. */
|
|
52
52
|
@post
|
|
53
|
-
create(@body user:
|
|
53
|
+
create(@body user: CreateRequest<User>): {
|
|
54
54
|
@statusCode _: 201;
|
|
55
55
|
@body user: User;
|
|
56
56
|
} | CreateErrors;
|
|
57
57
|
|
|
58
|
-
/**
|
|
59
|
-
@
|
|
58
|
+
/** Replaces a user: the whole body, as a `PUT` sends it. */
|
|
59
|
+
@put
|
|
60
60
|
update(
|
|
61
61
|
@path id: uuid,
|
|
62
|
-
@body user:
|
|
62
|
+
@body user: UpdateRequest<User>,
|
|
63
|
+
): User | UpdateErrors;
|
|
64
|
+
|
|
65
|
+
/** Updates some of a user's properties, with a merge patch. */
|
|
66
|
+
@patch
|
|
67
|
+
patch(
|
|
68
|
+
@path id: uuid,
|
|
69
|
+
@body user: PatchRequest<User>,
|
|
63
70
|
): User | UpdateErrors;
|
|
64
71
|
|
|
65
72
|
/** Deletes a user. */
|