@antelopejs/interface-data-api 0.5.0 → 0.6.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 +26 -22
- package/dist/antelope.test.js +1 -5
- package/dist/antelope.test.js.map +1 -1
- package/dist/components.d.ts +57 -37
- package/dist/components.js +36 -68
- package/dist/components.js.map +1 -1
- package/dist/index.d.ts +44 -60
- package/dist/index.js +22 -10
- package/dist/index.js.map +1 -1
- package/dist/internal/computed.js +12 -4
- package/dist/internal/computed.js.map +1 -1
- package/dist/internal/controller-types.d.ts +19 -4
- package/dist/internal/default-filter.js +11 -1
- package/dist/internal/default-filter.js.map +1 -1
- package/dist/internal/deferred-joined-stream.js +40 -27
- package/dist/internal/deferred-joined-stream.js.map +1 -1
- package/dist/internal/extractors.d.ts +25 -0
- package/dist/internal/extractors.js +31 -0
- package/dist/internal/extractors.js.map +1 -0
- package/dist/internal/field-types.d.ts +22 -6
- package/dist/internal/filters.d.ts +3 -1
- package/dist/internal/filters.js +9 -2
- package/dist/internal/filters.js.map +1 -1
- package/dist/internal/foreign.js +1 -1
- package/dist/internal/foreign.js.map +1 -1
- package/dist/internal/guards.d.ts +20 -1
- package/dist/internal/guards.js +42 -0
- package/dist/internal/guards.js.map +1 -1
- package/dist/internal/joined.js +1 -1
- package/dist/internal/joined.js.map +1 -1
- package/dist/internal/list.d.ts +2 -2
- package/dist/internal/list.js +10 -6
- package/dist/internal/list.js.map +1 -1
- package/dist/internal/meta-records.d.ts +2 -0
- package/dist/internal/meta-records.js +10 -5
- package/dist/internal/meta-records.js.map +1 -1
- package/dist/internal/properties.d.ts +2 -2
- package/dist/internal/properties.js +6 -4
- package/dist/internal/properties.js.map +1 -1
- package/dist/internal/prototypes.d.ts +14 -0
- package/dist/internal/prototypes.js +38 -0
- package/dist/internal/prototypes.js.map +1 -0
- package/dist/internal/read-properties.d.ts +3 -0
- package/dist/internal/read-properties.js +29 -0
- package/dist/internal/read-properties.js.map +1 -0
- package/dist/internal/tables.d.ts +12 -2
- package/dist/internal/tables.js +11 -0
- package/dist/internal/tables.js.map +1 -1
- package/dist/internal/validation.d.ts +4 -3
- package/dist/internal/validation.js +18 -6
- package/dist/internal/validation.js.map +1 -1
- package/dist/metadata.d.ts +13 -13
- package/dist/metadata.js +29 -31
- package/dist/metadata.js.map +1 -1
- package/dist/tests/components/access_control.test.js +11 -3
- package/dist/tests/components/access_control.test.js.map +1 -1
- package/dist/tests/components/computed.test.js +14 -3
- package/dist/tests/components/computed.test.js.map +1 -1
- package/dist/tests/components/cross_instance.test.js +2 -2
- package/dist/tests/components/cross_instance.test.js.map +1 -1
- package/dist/tests/components/deferred_joined.test.js +1 -1
- package/dist/tests/components/deferred_joined.test.js.map +1 -1
- package/dist/tests/components/extract_generic.test.d.ts +1 -0
- package/dist/tests/components/extract_generic.test.js +64 -0
- package/dist/tests/components/extract_generic.test.js.map +1 -0
- package/dist/tests/components/filter_comparisons.test.d.ts +1 -0
- package/dist/tests/components/filter_comparisons.test.js +34 -0
- package/dist/tests/components/filter_comparisons.test.js.map +1 -0
- package/dist/tests/components/filter_list_route.test.d.ts +1 -0
- package/dist/tests/components/filter_list_route.test.js +182 -0
- package/dist/tests/components/filter_list_route.test.js.map +1 -0
- package/dist/tests/components/filter_options.test.js +3 -3
- package/dist/tests/components/filter_options.test.js.map +1 -1
- package/dist/tests/components/foreign_computed.test.js +15 -4
- package/dist/tests/components/foreign_computed.test.js.map +1 -1
- package/dist/tests/components/foreign_joined.test.js +16 -4
- package/dist/tests/components/foreign_joined.test.js.map +1 -1
- package/dist/tests/components/joined.test.js +26 -8
- package/dist/tests/components/joined.test.js.map +1 -1
- package/dist/tests/components/listable.test.js +31 -12
- package/dist/tests/components/listable.test.js.map +1 -1
- package/dist/tests/components/localized_sort.test.js +5 -1
- package/dist/tests/components/localized_sort.test.js.map +1 -1
- package/dist/tests/components/mandatory.test.js +1 -1
- package/dist/tests/components/mandatory.test.js.map +1 -1
- package/dist/tests/components/modifier_key.test.js +12 -4
- package/dist/tests/components/modifier_key.test.js.map +1 -1
- package/dist/tests/components/parse_body.test.js +17 -16
- package/dist/tests/components/parse_body.test.js.map +1 -1
- package/dist/tests/components/parsed_value.test.js +2 -2
- package/dist/tests/components/parsed_value.test.js.map +1 -1
- package/dist/tests/components/partial_edit.test.js +8 -3
- package/dist/tests/components/partial_edit.test.js.map +1 -1
- package/dist/tests/components/per_action_access.test.js +11 -3
- package/dist/tests/components/per_action_access.test.js.map +1 -1
- package/dist/tests/components/prototype_named_filter.test.d.ts +1 -0
- package/dist/tests/components/prototype_named_filter.test.js +87 -0
- package/dist/tests/components/prototype_named_filter.test.js.map +1 -0
- package/dist/tests/components/validator.test.js +1 -1
- package/dist/tests/components/validator.test.js.map +1 -1
- package/dist/tests/components/written_row.test.d.ts +1 -0
- package/dist/tests/components/written_row.test.js +99 -0
- package/dist/tests/components/written_row.test.js.map +1 -0
- package/dist/tests/helpers/other-instance.js +8 -4
- package/dist/tests/helpers/other-instance.js.map +1 -1
- package/dist/tests/index/field_decorator_target.test.d.ts +1 -0
- package/dist/tests/index/field_decorator_target.test.js +20 -0
- package/dist/tests/index/field_decorator_target.test.js.map +1 -0
- package/dist/tests/index/inheritance.test.js +6 -2
- package/dist/tests/index/inheritance.test.js.map +1 -1
- package/dist/tests/index/metadata_names.test.d.ts +1 -0
- package/dist/tests/index/metadata_names.test.js +91 -0
- package/dist/tests/index/metadata_names.test.js.map +1 -0
- package/dist/tests/index/routes.test.js +14 -5
- package/dist/tests/index/routes.test.js.map +1 -1
- package/dist/tests/integration.test.js +24 -11
- package/dist/tests/integration.test.js.map +1 -1
- package/dist/tests/internal/controller-fields.test.d.ts +1 -0
- package/dist/tests/internal/controller-fields.test.js +25 -0
- package/dist/tests/internal/controller-fields.test.js.map +1 -0
- package/dist/tests/internal/http.d.ts +21 -9
- package/dist/tests/internal/http.js +24 -3
- package/dist/tests/internal/http.js.map +1 -1
- package/dist/tests/internal/shop-schema.d.ts +28 -20
- package/dist/tests/utils.d.ts +5 -7
- package/dist/tests/utils.js.map +1 -1
- package/docs/1.introduction.md +38 -35
- package/docs/10.joined.md +51 -40
- package/docs/11.computed.md +91 -47
- package/docs/2.data-controllers.md +50 -42
- package/docs/3.routes.md +150 -133
- package/docs/4.access-rights.md +48 -44
- package/docs/5.validators.md +54 -45
- package/docs/6.listable.md +53 -49
- package/docs/7.foreign-keys.md +55 -48
- package/docs/8.filters.md +64 -57
- package/docs/9.modifiers.md +29 -25
- package/package.json +24 -14
- package/skills/data-api-interface/SKILL.md +9 -3
package/docs/3.routes.md
CHANGED
|
@@ -1,18 +1,22 @@
|
|
|
1
|
-
|
|
1
|
+
---
|
|
2
|
+
title: Routes
|
|
3
|
+
description: The default CRUD routes, their parameters and responses, the WithOptions and WithGetGuard helpers, custom routes, and route errors.
|
|
4
|
+
mode: reference
|
|
5
|
+
---
|
|
2
6
|
|
|
3
7
|
## Overview
|
|
4
8
|
|
|
5
|
-
Routes define which operations
|
|
9
|
+
Routes define which operations a data controller serves. The `DefaultRoutes` namespace holds ready-made routes. Each one sets its HTTP method, the parameters it reads from the request and the response it sends.
|
|
6
10
|
|
|
7
|
-
## Default
|
|
11
|
+
## Default routes
|
|
8
12
|
|
|
9
|
-
The `DefaultRoutes` namespace
|
|
13
|
+
The `DefaultRoutes` namespace holds five routes for the CRUD operations.
|
|
10
14
|
|
|
11
15
|
```typescript
|
|
12
16
|
import { DefaultRoutes } from "@antelopejs/interface-data-api";
|
|
13
17
|
```
|
|
14
18
|
|
|
15
|
-
| Route | Object | HTTP
|
|
19
|
+
| Route | Object | HTTP method | Endpoint pattern |
|
|
16
20
|
| ------ | ---------------------- | ----------- | -------------------------- |
|
|
17
21
|
| Get | `DefaultRoutes.Get` | GET | `/resource/get?id=<id>` |
|
|
18
22
|
| List | `DefaultRoutes.List` | GET | `/resource/list` |
|
|
@@ -20,13 +24,13 @@ import { DefaultRoutes } from "@antelopejs/interface-data-api";
|
|
|
20
24
|
| Edit | `DefaultRoutes.Edit` | PUT | `/resource/edit?id=<id>` |
|
|
21
25
|
| Delete | `DefaultRoutes.Delete` | DELETE | `/resource/delete?id=<id>` |
|
|
22
26
|
|
|
23
|
-
|
|
27
|
+
`DefaultRoutes.All` holds all five routes under the keys `get`, `list`, `new`, `edit` and `delete`.
|
|
24
28
|
|
|
25
|
-
## Route
|
|
29
|
+
## Route configuration
|
|
26
30
|
|
|
27
|
-
|
|
31
|
+
The route definition object you pass to `DataController` decides which endpoints exist. Each key is an endpoint name.
|
|
28
32
|
|
|
29
|
-
```typescript
|
|
33
|
+
```typescript fragment
|
|
30
34
|
// All CRUD routes
|
|
31
35
|
@RegisterDataController()
|
|
32
36
|
class UserAPI extends DataController(
|
|
@@ -48,31 +52,27 @@ class UserAPI extends DataController(
|
|
|
48
52
|
},
|
|
49
53
|
Controller("/users"),
|
|
50
54
|
) {
|
|
51
|
-
// edit
|
|
55
|
+
// no edit or delete endpoint
|
|
52
56
|
}
|
|
53
57
|
```
|
|
54
58
|
|
|
55
|
-
## Route
|
|
59
|
+
## Route details
|
|
56
60
|
|
|
57
61
|
### Get
|
|
58
62
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
**Endpoint:** `GET /resource/get`
|
|
63
|
+
Reads one record by its identifier. The endpoint is `GET /resource/get`.
|
|
62
64
|
|
|
63
|
-
|
|
65
|
+
| Query parameter | Required | Description |
|
|
66
|
+
| --------------- | -------- | ----------------- |
|
|
67
|
+
| `id` | Yes | Record identifier |
|
|
64
68
|
|
|
65
|
-
|
|
66
|
-
| --------- | -------- | ----------------- |
|
|
67
|
-
| `id` | Yes | Record identifier |
|
|
68
|
-
|
|
69
|
-
**Example:**
|
|
69
|
+
A request without `id` answers `400` with `Missing id.`. A missing record answers `404 Not Found`.
|
|
70
70
|
|
|
71
71
|
```http
|
|
72
72
|
GET /users/get?id=user-123
|
|
73
73
|
```
|
|
74
74
|
|
|
75
|
-
|
|
75
|
+
The response holds the readable fields of the record.
|
|
76
76
|
|
|
77
77
|
```json
|
|
78
78
|
{
|
|
@@ -84,26 +84,21 @@ GET /users/get?id=user-123
|
|
|
84
84
|
|
|
85
85
|
### List
|
|
86
86
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
**Endpoint:** `GET /resource/list`
|
|
87
|
+
Reads one page of records, with sorting and filtering. The endpoint is `GET /resource/list`.
|
|
90
88
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
|
94
|
-
|
|
|
95
|
-
| `
|
|
96
|
-
| `
|
|
97
|
-
| `
|
|
98
|
-
| `sortDirection` | No | `asc` | Sort direction: `asc` or `desc` |
|
|
99
|
-
|
|
100
|
-
**Example:**
|
|
89
|
+
| Query parameter | Required | Default | Description |
|
|
90
|
+
| ---------------- | -------- | -------------------- | ------------------------------------------------------------------------------------------------ |
|
|
91
|
+
| `limit` | No | `maxPage`, else `10` | Maximum number of records to return. The route caps it at `maxPage`, or at `100` without one. |
|
|
92
|
+
| `offset` | No | `0` | Number of records to skip |
|
|
93
|
+
| `sortKey` | No | none | Field to sort by. The field needs `@Sortable`, or the route answers `400 Field is not sortable.` |
|
|
94
|
+
| `sortDirection` | No | `asc` | `asc` or `desc`. Any other value sorts ascending. |
|
|
95
|
+
| `filter_<field>` | No | none | A filter on a field declared with `@Filter`. See [filters](./8.filters.md). |
|
|
101
96
|
|
|
102
97
|
```http
|
|
103
98
|
GET /users/list?limit=10&offset=0&sortKey=name&sortDirection=asc
|
|
104
99
|
```
|
|
105
100
|
|
|
106
|
-
|
|
101
|
+
The response holds the page, the total count of matching records, and the `offset` and `limit` the route used.
|
|
107
102
|
|
|
108
103
|
```json
|
|
109
104
|
{
|
|
@@ -119,13 +114,7 @@ GET /users/list?limit=10&offset=0&sortKey=name&sortDirection=asc
|
|
|
119
114
|
|
|
120
115
|
### New
|
|
121
116
|
|
|
122
|
-
Creates a
|
|
123
|
-
|
|
124
|
-
**Endpoint:** `POST /resource/new`
|
|
125
|
-
|
|
126
|
-
**Body:** JSON object with field values.
|
|
127
|
-
|
|
128
|
-
**Example:**
|
|
117
|
+
Creates a record. The endpoint is `POST /resource/new`, and the body is a JSON object with the field values.
|
|
129
118
|
|
|
130
119
|
```http
|
|
131
120
|
POST /users/new
|
|
@@ -138,7 +127,7 @@ Content-Type: application/json
|
|
|
138
127
|
}
|
|
139
128
|
```
|
|
140
129
|
|
|
141
|
-
|
|
130
|
+
The response is the created record, as [Get](#get) answers it, with its `_id` even when `get` does not read `_id`.
|
|
142
131
|
|
|
143
132
|
```json
|
|
144
133
|
{
|
|
@@ -148,23 +137,19 @@ Content-Type: application/json
|
|
|
148
137
|
}
|
|
149
138
|
```
|
|
150
139
|
|
|
151
|
-
Every writable field is written
|
|
140
|
+
The row starts from the defaults the controller class declares. Every writable field is then written. A field absent from the body is written as `undefined`, and every writable setter of the controller runs, with `undefined` for an absent field. The route answers `400 Bad Request` for a body that is not valid JSON or not a JSON object.
|
|
152
141
|
|
|
153
|
-
Before
|
|
142
|
+
Before the insert, the route triggers the `insert` event on the modifiers of the table.
|
|
154
143
|
|
|
155
144
|
### Edit
|
|
156
145
|
|
|
157
|
-
Updates
|
|
158
|
-
|
|
159
|
-
**Endpoint:** `PUT /resource/edit`
|
|
146
|
+
Updates a record by its identifier. The endpoint is `PUT /resource/edit`.
|
|
160
147
|
|
|
161
|
-
|
|
148
|
+
| Query parameter | Required | Description |
|
|
149
|
+
| --------------- | -------- | ----------------- |
|
|
150
|
+
| `id` | Yes | Record identifier |
|
|
162
151
|
|
|
163
|
-
|
|
164
|
-
| --------- | -------- | ----------------- |
|
|
165
|
-
| `id` | Yes | Record identifier |
|
|
166
|
-
|
|
167
|
-
**Body:** JSON object with the fields to update. Only the fields present in the body are written:
|
|
152
|
+
The body is a JSON object with the fields to update. The route writes only the fields present in the body.
|
|
168
153
|
|
|
169
154
|
| In the body | Effect on the field |
|
|
170
155
|
| ----------- | ------------------- |
|
|
@@ -172,11 +157,9 @@ Updates an existing record by its identifier.
|
|
|
172
157
|
| `null` | cleared |
|
|
173
158
|
| a value | set |
|
|
174
159
|
|
|
175
|
-
Writable setters only
|
|
176
|
-
|
|
177
|
-
A missing record answers `404 Not Found`. A body that is not valid JSON or not a JSON object is rejected with `400 Bad Request`.
|
|
160
|
+
Writable setters run only for the fields present in the body. A modifier field (localized or encrypted, for example) absent from the body keeps its stored value in every language.
|
|
178
161
|
|
|
179
|
-
|
|
162
|
+
A request without `id` answers `400` with `Missing id.`. A missing record answers `404 Not Found`. A body that is not valid JSON or not a JSON object answers `400 Bad Request`.
|
|
180
163
|
|
|
181
164
|
```http
|
|
182
165
|
PUT /users/edit?id=user-123
|
|
@@ -188,7 +171,7 @@ Content-Type: application/json
|
|
|
188
171
|
}
|
|
189
172
|
```
|
|
190
173
|
|
|
191
|
-
|
|
174
|
+
The response is the stored record after the update, as [Get](#get) answers it, with joined, computed and foreign fields. The caller needs no second read.
|
|
192
175
|
|
|
193
176
|
```json
|
|
194
177
|
{
|
|
@@ -198,49 +181,45 @@ Content-Type: application/json
|
|
|
198
181
|
}
|
|
199
182
|
```
|
|
200
183
|
|
|
201
|
-
Before
|
|
184
|
+
Before the update, the route triggers the `update` event on the modifiers of the table.
|
|
202
185
|
|
|
203
186
|
### Delete
|
|
204
187
|
|
|
205
|
-
Deletes one or more records by their identifiers.
|
|
188
|
+
Deletes one or more records by their identifiers. The endpoint is `DELETE /resource/delete`.
|
|
206
189
|
|
|
207
|
-
|
|
190
|
+
| Query parameter | Required | Description |
|
|
191
|
+
| --------------- | -------- | ----------------------------------------------------------------------------------- |
|
|
192
|
+
| `id` | Yes | Record identifier. Repeat the `id` parameter to delete several records in one call. |
|
|
208
193
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
| Parameter | Required | Description |
|
|
212
|
-
| --------- | -------- | -------------------------------------------------------------------------------------- |
|
|
213
|
-
| `id` | Yes | Record identifier(s). Pass multiple `id` parameters to delete several records at once. |
|
|
214
|
-
|
|
215
|
-
**Example:**
|
|
194
|
+
A request without `id` answers `400` with `Missing id.`.
|
|
216
195
|
|
|
217
196
|
```http
|
|
218
197
|
DELETE /users/delete?id=user-123
|
|
219
198
|
```
|
|
220
199
|
|
|
221
|
-
|
|
200
|
+
The route answers `200 OK`. The body is the number of records the database deleted. An identifier that matches no record is not an error.
|
|
222
201
|
|
|
223
202
|
```http
|
|
224
203
|
200 OK
|
|
225
204
|
```
|
|
226
205
|
|
|
227
|
-
## Route
|
|
206
|
+
## Route options with `WithOptions`
|
|
228
207
|
|
|
229
|
-
|
|
208
|
+
`DefaultRoutes.WithOptions` customizes a route. It sets route options, which take precedence over the query parameters of the same name, and it can rename the endpoint.
|
|
230
209
|
|
|
231
|
-
```typescript
|
|
210
|
+
```typescript fragment
|
|
232
211
|
DefaultRoutes.WithOptions(callback, options?, endpoint?)
|
|
233
212
|
```
|
|
234
213
|
|
|
235
|
-
| Parameter | Description
|
|
236
|
-
| ---------- |
|
|
237
|
-
| `callback` | The route
|
|
238
|
-
| `options` | An object
|
|
239
|
-
| `endpoint` |
|
|
214
|
+
| Parameter | Description |
|
|
215
|
+
| ---------- | -------------------------------------------------------------------------------------------------------------------------------- |
|
|
216
|
+
| `callback` | The route to customize, such as `DefaultRoutes.Edit`, or a route `WithOptions` already returned. In that case the options merge. |
|
|
217
|
+
| `options` | An object of option values. A query parameter with the same name is ignored. Route `filters` combine with the request's filters. |
|
|
218
|
+
| `endpoint` | A custom endpoint path. It defaults to the route key in the definition object. |
|
|
240
219
|
|
|
241
220
|
### Example
|
|
242
221
|
|
|
243
|
-
```typescript
|
|
222
|
+
```typescript fragment
|
|
244
223
|
const routes = {
|
|
245
224
|
edit: DefaultRoutes.Edit,
|
|
246
225
|
quickEdit: DefaultRoutes.WithOptions(DefaultRoutes.Edit, {
|
|
@@ -250,48 +229,52 @@ const routes = {
|
|
|
250
229
|
|
|
251
230
|
@RegisterDataController()
|
|
252
231
|
class UserAPI extends DataController(User, routes, Controller("/users")) {
|
|
253
|
-
// /users/edit
|
|
254
|
-
// /users/quickEdit skips mandatory field
|
|
232
|
+
// /users/edit checks mandatory fields
|
|
233
|
+
// /users/quickEdit skips the mandatory field check
|
|
255
234
|
}
|
|
256
235
|
```
|
|
257
236
|
|
|
258
|
-
### Available
|
|
237
|
+
### Available options per route
|
|
238
|
+
|
|
239
|
+
The string options `noForeign` and `noMandatory` take effect for any non-empty string, `"false"` included.
|
|
259
240
|
|
|
260
|
-
|
|
241
|
+
The Get route takes these options.
|
|
261
242
|
|
|
262
|
-
| Option | Type | Description
|
|
263
|
-
| ----------- | -------- |
|
|
264
|
-
| `index` | `string` |
|
|
265
|
-
| `noForeign` | `string` | Skip foreign key resolution
|
|
243
|
+
| Option | Type | Description |
|
|
244
|
+
| ----------- | -------- | -------------------------------------------------------------------- |
|
|
245
|
+
| `index` | `string` | Read the record through a secondary index instead of the primary key |
|
|
246
|
+
| `noForeign` | `string` | Skip foreign key resolution |
|
|
266
247
|
|
|
267
|
-
|
|
248
|
+
The List route takes these options.
|
|
268
249
|
|
|
269
250
|
| Option | Type | Description |
|
|
270
251
|
| ----------- | ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
|
|
271
|
-
| `maxPage` | `number` | Maximum
|
|
252
|
+
| `maxPage` | `number` | Maximum value of `limit`, and the page size when the request sends no `limit`. Without it, the cap is `100`. |
|
|
272
253
|
| `noForeign` | `boolean` | Skip foreign key resolution |
|
|
273
|
-
| `noPluck` | `boolean` |
|
|
274
|
-
| `pluckMode` | `string` | Select a named pluck mode (see `@Listable`)
|
|
275
|
-
| `filters` | `Record<string, FieldFilter>` | Mandatory filters merged with request filters
|
|
254
|
+
| `noPluck` | `boolean` | Skip the database pluck. The response still holds only the fields readable in `list` |
|
|
255
|
+
| `pluckMode` | `string` | Select a named pluck mode (see `@Listable`). A mode that no field declares answers `400`. |
|
|
256
|
+
| `filters` | `Record<string, FieldFilter>` | Mandatory filters merged with request filters. Route field keys win (see [filters](./8.filters.md#mandatory-route-filters)). |
|
|
276
257
|
|
|
277
|
-
|
|
258
|
+
The New route takes this option.
|
|
278
259
|
|
|
279
|
-
| Option | Type | Description
|
|
280
|
-
| ------------- | -------- |
|
|
281
|
-
| `noMandatory` | `string` | Skip mandatory field
|
|
260
|
+
| Option | Type | Description |
|
|
261
|
+
| ------------- | -------- | ------------------------------ |
|
|
262
|
+
| `noMandatory` | `string` | Skip the mandatory field check |
|
|
282
263
|
|
|
283
|
-
|
|
264
|
+
The Edit route takes these options.
|
|
284
265
|
|
|
285
|
-
| Option | Type | Description
|
|
286
|
-
| ------------- | -------- |
|
|
287
|
-
| `index` | `string` |
|
|
288
|
-
| `noMandatory` | `string` | Skip mandatory field
|
|
266
|
+
| Option | Type | Description |
|
|
267
|
+
| ------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------ |
|
|
268
|
+
| `index` | `string` | Find the record and read the response through a secondary index. The update itself targets the record whose primary key equals `id`. |
|
|
269
|
+
| `noMandatory` | `string` | Skip the mandatory field check |
|
|
289
270
|
|
|
290
|
-
## Amending the
|
|
271
|
+
## Amending the body of `new` and `edit`
|
|
291
272
|
|
|
292
|
-
The `new` and `edit` routes take the raw request body (`Buffer`) or a body already parsed (`Validation.WrittenBody`). A route
|
|
273
|
+
The `new` and `edit` routes take the raw request body (`Buffer`) or a body already parsed (`Validation.WrittenBody`). A route that wraps them can parse the body, amend it (stamp a field or drop one) and pass the object on. The route validates and writes the object as it receives it, with no second serialization.
|
|
293
274
|
|
|
294
|
-
A parsed body is `FieldValues
|
|
275
|
+
A parsed body is `FieldValues`, which holds fields by name. Each field holds a `FieldValue`: a string, a number, a boolean, `null`, a `Date`, or an array or plain object of those. `Validation.ParseBody` answers `400` for anything else, whether it comes from a `Buffer` or from a body already parsed. That includes a function, a class instance, a `bigint`, a cycle and an `undefined` array element.
|
|
276
|
+
|
|
277
|
+
The default routes run with the controller as `this`. A wrapping function declares `this: DataControllerInstance` and calls `route.func.call(this, ...)`.
|
|
295
278
|
|
|
296
279
|
```ts docs-check
|
|
297
280
|
import {
|
|
@@ -319,9 +302,9 @@ const stampedNew = {
|
|
|
319
302
|
};
|
|
320
303
|
```
|
|
321
304
|
|
|
322
|
-
## Loaded-
|
|
305
|
+
## Loaded-model guards with `WithGetGuard`
|
|
323
306
|
|
|
324
|
-
`DefaultRoutes.WithGetGuard(guard)` returns a Get route that awaits a guard after
|
|
307
|
+
`DefaultRoutes.WithGetGuard(guard)` returns a Get route that awaits a guard after it loads and unlocks the model. The guard runs before the route selects the readable fields and applies the response getters. A guard can check a stored discriminator or ownership field that never appears in the response.
|
|
325
308
|
|
|
326
309
|
```ts docs-check
|
|
327
310
|
import { DefaultRoutes } from "@antelopejs/interface-data-api";
|
|
@@ -329,7 +312,7 @@ import { assert } from "@antelopejs/interface-api-util";
|
|
|
329
312
|
|
|
330
313
|
const invoiceGet = DefaultRoutes.WithGetGuard(
|
|
331
314
|
async function (ctx, current, params) {
|
|
332
|
-
//
|
|
315
|
+
// The table stores documentType, and no @Access exposes it.
|
|
333
316
|
assert(current.documentType === "invoice", 404, "Not Found");
|
|
334
317
|
},
|
|
335
318
|
);
|
|
@@ -340,28 +323,28 @@ const routes = {
|
|
|
340
323
|
};
|
|
341
324
|
```
|
|
342
325
|
|
|
343
|
-
The `GetGuard` callback receives `(ctx, current, params)`. Declare it as a regular function to
|
|
326
|
+
The `GetGuard` callback receives `(ctx, current, params)`. Declare it as a regular function to reach the controller through `this`, typed `DataControllerInstance`. That type is `object`, with no string index, so narrow `this` (with `instanceof` your controller class, for example) before you read a controller field. `current` is `RowFields`, whose values are `unknown`.
|
|
344
327
|
|
|
345
|
-
| Argument | Contents
|
|
346
|
-
| --------- |
|
|
347
|
-
| `ctx` | Request context
|
|
348
|
-
| `current` | Loaded model,
|
|
349
|
-
| `params` | `Parameters.GetParameters`
|
|
328
|
+
| Argument | Contents | Use it for |
|
|
329
|
+
| --------- | ------------------------------------------------------------------------------------------------- | ------------------------------------------- |
|
|
330
|
+
| `ctx` | Request context | Policy that depends on the request |
|
|
331
|
+
| `current` | Loaded model, with the fields no `@Access` reads and the joined, computed and foreign data of Get | Checking stored ownership or discriminators |
|
|
332
|
+
| `params` | `Parameters.GetParameters` | The parsed Get parameters |
|
|
350
333
|
|
|
351
|
-
Treat `current` as read-only
|
|
334
|
+
Treat `current` as read-only. It is not the response object, and the API does not freeze it.
|
|
352
335
|
|
|
353
|
-
|
|
336
|
+
A guard follows these rules.
|
|
354
337
|
|
|
355
|
-
- **Deny by throwing.** Throw an `HTTPResult` or
|
|
356
|
-
- **
|
|
357
|
-
- **
|
|
358
|
-
- **
|
|
338
|
+
- **Deny by throwing.** Throw an `HTTPResult` or call `assert`. A normal return allows the read, even a return value of `false`.
|
|
339
|
+
- **A missing record skips the guard.** The route answers `404` without calling it.
|
|
340
|
+
- **The guard reads the loaded model.** It issues no second read and does not change field access rules.
|
|
341
|
+
- **Each route needs its own guard.** The guard applies only to the route that uses it, not to the list, edit or delete routes.
|
|
359
342
|
|
|
360
|
-
## Custom
|
|
343
|
+
## Custom routes
|
|
361
344
|
|
|
362
|
-
For operations beyond CRUD, define a
|
|
345
|
+
For operations beyond CRUD, define a route callback with `func`, `args` and `method` properties.
|
|
363
346
|
|
|
364
|
-
```typescript
|
|
347
|
+
```typescript fragment
|
|
365
348
|
import { Context } from "@antelopejs/interface-api";
|
|
366
349
|
import {
|
|
367
350
|
DataController,
|
|
@@ -392,20 +375,54 @@ class UserAPI extends DataController(
|
|
|
392
375
|
}
|
|
393
376
|
```
|
|
394
377
|
|
|
395
|
-
The `args` array
|
|
378
|
+
The `args` array holds parameter decorators that read values from the request context. Entry `i` of `args` supplies parameter `i` of `func`.
|
|
379
|
+
|
|
380
|
+
`DataControllerCallback<O, R, A>` states that pairing. `A` is the tuple of what `func` receives, and `args` holds one decorator per element of `A`. The default routes declare their tuple. `DefaultRoutes.Get` takes `[RequestContext, Parameters.GetParameters]`, and `DefaultRoutes.New` and `DefaultRoutes.Edit` add the `Validation.WrittenBody`. A route that wraps one of them can be generic over the tuple and call `route.func.call(this, ...)` with typed arguments. Without `A`, the parameters are unknown and unchecked. A `DataControllerDef` holds routes of that type.
|
|
381
|
+
|
|
382
|
+
```ts docs-check
|
|
383
|
+
import type { RequestContext } from "@antelopejs/interface-api";
|
|
384
|
+
import {
|
|
385
|
+
type DataControllerCallback,
|
|
386
|
+
type DataControllerInstance,
|
|
387
|
+
DefaultRoutes,
|
|
388
|
+
} from "@antelopejs/interface-data-api";
|
|
389
|
+
import type { Parameters } from "@antelopejs/interface-data-api/components";
|
|
390
|
+
|
|
391
|
+
function withLoggedId<O, R, Rest extends unknown[]>(
|
|
392
|
+
route: DataControllerCallback<
|
|
393
|
+
O,
|
|
394
|
+
R,
|
|
395
|
+
[RequestContext, Parameters.GetParameters, ...Rest]
|
|
396
|
+
>,
|
|
397
|
+
): DataControllerCallback<
|
|
398
|
+
O,
|
|
399
|
+
R,
|
|
400
|
+
[RequestContext, Parameters.GetParameters, ...Rest]
|
|
401
|
+
> {
|
|
402
|
+
return {
|
|
403
|
+
...route,
|
|
404
|
+
func: function (this: DataControllerInstance, ctx, params, ...rest) {
|
|
405
|
+
console.log(params.id);
|
|
406
|
+
return route.func.call(this, ctx, params, ...rest);
|
|
407
|
+
},
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
const loggedGet = withLoggedId(DefaultRoutes.Get);
|
|
412
|
+
```
|
|
396
413
|
|
|
397
|
-
## Error
|
|
414
|
+
## Error handling
|
|
398
415
|
|
|
399
|
-
|
|
416
|
+
The default routes answer with these HTTP errors.
|
|
400
417
|
|
|
401
|
-
| Status | Condition
|
|
402
|
-
| --------------------------- |
|
|
403
|
-
| `400 Bad Request` |
|
|
404
|
-
| `404 Not Found` |
|
|
405
|
-
| `500 Internal Server Error` |
|
|
418
|
+
| Status | Condition |
|
|
419
|
+
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
|
|
420
|
+
| `400 Bad Request` | A field fails its validator, a mandatory field is missing, the body is not a JSON object, or a parameter is missing or invalid |
|
|
421
|
+
| `404 Not Found` | The record does not exist |
|
|
422
|
+
| `500 Internal Server Error` | The database operation fails, or the controller holds no model |
|
|
406
423
|
|
|
407
|
-
|
|
424
|
+
A failed mandatory or validator check answers a JSON body with a `code` and the `fields` concerned (see [validators](./5.validators.md#error-handling)). Every other error the routes raise answers its message as text, such as `Missing id.` or `Not Found`.
|
|
408
425
|
|
|
409
|
-
## Next
|
|
426
|
+
## Next steps
|
|
410
427
|
|
|
411
|
-
|
|
428
|
+
The [access rights page](./4.access-rights.md) covers which fields each action reads and writes.
|