json-server 1.0.0-beta.0 → 1.0.0-beta.10
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/LICENSE +21 -44
- package/README.md +145 -84
- package/lib/app.js +82 -52
- package/lib/bin.js +52 -49
- package/lib/matches-where.js +87 -0
- package/lib/paginate.js +24 -0
- package/lib/parse-where.js +56 -0
- package/lib/service.js +18 -161
- package/lib/where-operators.js +15 -0
- package/package.json +59 -45
- package/schema.json +10 -0
- package/views/index.html +73 -75
- package/lib/app.d.ts +0 -8
- package/lib/bin.d.ts +0 -2
- package/lib/observer.d.ts +0 -11
- package/lib/service.d.ts +0 -38
package/LICENSE
CHANGED
|
@@ -1,44 +1,21 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
Copyright (
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
Software
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
control with you. If you exceed the Use Limitation, your use is
|
|
23
|
-
subject to payment of Licensor's then-current list price for licenses.
|
|
24
|
-
|
|
25
|
-
Conditions. Redistribution in source code or other forms must include
|
|
26
|
-
a copy of this license document to be provided in a reasonable
|
|
27
|
-
manner. Any redistribution of the Software is only allowed subject to
|
|
28
|
-
this license.
|
|
29
|
-
|
|
30
|
-
Trademarks. This license does not grant you any right in the
|
|
31
|
-
trademarks, service marks, brand names or logos of Licensor.
|
|
32
|
-
|
|
33
|
-
DISCLAIMER. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OR
|
|
34
|
-
CONDITION, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO WARRANTIES
|
|
35
|
-
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
36
|
-
NONINFRINGEMENT. LICENSORS HEREBY DISCLAIM ALL LIABILITY, WHETHER IN
|
|
37
|
-
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
38
|
-
CONNECTION WITH THE SOFTWARE.
|
|
39
|
-
|
|
40
|
-
Termination. If you violate the terms of this license, your rights
|
|
41
|
-
will terminate automatically and will not be reinstated without the
|
|
42
|
-
prior written consent of Licensor. Any such termination will not
|
|
43
|
-
affect the right of others who may have received copies of the
|
|
44
|
-
Software from you.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 typicode
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
#
|
|
1
|
+
# JSON-Server
|
|
2
2
|
|
|
3
3
|
[](https://github.com/typicode/json-server/actions/workflows/node.js.yml)
|
|
4
4
|
|
|
5
5
|
> [!IMPORTANT]
|
|
6
|
-
> Viewing beta v1 documentation – usable but expect breaking changes. For stable version, see [here](https://github.com/typicode/json-server/tree/v0)
|
|
6
|
+
> Viewing beta v1 documentation – usable but expect breaking changes. For stable version, see [here](https://github.com/typicode/json-server/tree/v0.17.4)
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
> [!NOTE]
|
|
9
|
+
> Using React ⚛️ and tired of CSS-in-JS? See [MistCSS](https://github.com/typicode/mistcss) 👀
|
|
9
10
|
|
|
10
11
|
## Install
|
|
11
12
|
|
|
@@ -19,6 +20,7 @@ Create a `db.json` or `db.json5` file
|
|
|
19
20
|
|
|
20
21
|
```json
|
|
21
22
|
{
|
|
23
|
+
"$schema": "./node_modules/json-server/schema.json",
|
|
22
24
|
"posts": [
|
|
23
25
|
{ "id": "1", "title": "a title", "views": 100 },
|
|
24
26
|
{ "id": "2", "title": "another title", "views": 200 }
|
|
@@ -40,15 +42,15 @@ Create a `db.json` or `db.json5` file
|
|
|
40
42
|
```json5
|
|
41
43
|
{
|
|
42
44
|
posts: [
|
|
43
|
-
{ id:
|
|
44
|
-
{ id:
|
|
45
|
+
{ id: "1", title: "a title", views: 100 },
|
|
46
|
+
{ id: "2", title: "another title", views: 200 },
|
|
45
47
|
],
|
|
46
48
|
comments: [
|
|
47
|
-
{ id:
|
|
48
|
-
{ id:
|
|
49
|
+
{ id: "1", text: "a comment about post 1", postId: "1" },
|
|
50
|
+
{ id: "2", text: "another comment about post 1", postId: "1" },
|
|
49
51
|
],
|
|
50
52
|
profile: {
|
|
51
|
-
name:
|
|
53
|
+
name: "typicode",
|
|
52
54
|
},
|
|
53
55
|
}
|
|
54
56
|
```
|
|
@@ -57,19 +59,30 @@ You can read more about JSON5 format [here](https://github.com/json5/json5).
|
|
|
57
59
|
|
|
58
60
|
</details>
|
|
59
61
|
|
|
60
|
-
|
|
62
|
+
Start JSON Server
|
|
61
63
|
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
+
```bash
|
|
65
|
+
npx json-server db.json
|
|
64
66
|
```
|
|
65
67
|
|
|
66
|
-
|
|
68
|
+
This starts the server at `http://localhost:3000`. You should see:
|
|
69
|
+
```
|
|
70
|
+
JSON Server started on PORT :3000
|
|
71
|
+
http://localhost:3000
|
|
72
|
+
```
|
|
67
73
|
|
|
68
|
-
|
|
69
|
-
|
|
74
|
+
Access your REST API:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
curl http://localhost:3000/posts/1
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**Response:**
|
|
81
|
+
```json
|
|
70
82
|
{
|
|
71
83
|
"id": "1",
|
|
72
|
-
"title": "a title"
|
|
84
|
+
"title": "a title",
|
|
85
|
+
"views": 100
|
|
73
86
|
}
|
|
74
87
|
```
|
|
75
88
|
|
|
@@ -77,126 +90,174 @@ Run `json-server --help` for a list of options
|
|
|
77
90
|
|
|
78
91
|
## Sponsors ✨
|
|
79
92
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
|
83
|
-
|
|
|
84
|
-
|
|
|
85
|
-
| <a href="https://
|
|
86
|
-
|
|
|
93
|
+
### Gold
|
|
94
|
+
|
|
95
|
+
| |
|
|
96
|
+
| :--------------------------------------------------------------------------------------------------------------------------------------------------------: |
|
|
97
|
+
| <a href="https://mockend.com/" target="_blank"><img src="https://jsonplaceholder.typicode.com/mockend.svg" height="100px"></a> |
|
|
98
|
+
| <a href="https://zuplo.link/json-server-gh"><img src="https://github.com/user-attachments/assets/adfee31f-a8b6-4684-9a9b-af4f03ac5b75" height="100px"></a> |
|
|
99
|
+
| <a href="https://www.mintlify.com/"><img src="https://github.com/user-attachments/assets/bcc8cc48-b2d9-4577-8939-1eb4196b7cc5" height="100px"></a> |
|
|
100
|
+
| <a href="http://git-tower.com/?utm_source=husky&utm_medium=referral"><img height="100px" alt="tower-dock-icon-light" src="https://github.com/user-attachments/assets/b6b4ab20-beff-4e5c-9845-bb9d60057196" /></a> |
|
|
101
|
+
| <a href="https://serpapi.com/?utm_source=typicode"><img height="100px" src="https://github.com/user-attachments/assets/52b3039d-1e4c-4c68-951c-93f0f1e73611" /></a>
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
### Silver
|
|
105
|
+
|
|
106
|
+
| |
|
|
107
|
+
| :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
|
|
108
|
+
| <a href="https://requestly.com?utm_source=githubsponsor&utm_medium=jsonserver&utm_campaign=jsonserver"><img src="https://github.com/user-attachments/assets/f7e7b3cf-97e2-46b8-81c8-cb3992662a1c" style="height:70px; width:auto;"></a> |
|
|
87
109
|
|
|
110
|
+
### Bronze
|
|
111
|
+
|
|
112
|
+
| | |
|
|
113
|
+
| :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
|
|
114
|
+
| <a href="https://www.storyblok.com/" target="_blank"><img src="https://github.com/typicode/json-server/assets/5502029/c6b10674-4ada-4616-91b8-59d30046b45a" height="35px"></a> | <a href="https://betterstack.com/" target="_blank"><img src="https://github.com/typicode/json-server/assets/5502029/44679f8f-9671-470d-b77e-26d90b90cbdc" height="35px"></a> |
|
|
88
115
|
|
|
89
116
|
[Become a sponsor and have your company logo here](https://github.com/users/typicode/sponsorship)
|
|
90
117
|
|
|
91
|
-
##
|
|
118
|
+
## Query Capabilities
|
|
92
119
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
120
|
+
JSON Server supports advanced querying out of the box:
|
|
121
|
+
|
|
122
|
+
```http
|
|
123
|
+
GET /posts?views:gt=100 # Filter by condition
|
|
124
|
+
GET /posts?_sort=-views # Sort by field (descending)
|
|
125
|
+
GET /posts?_page=1&_per_page=10 # Pagination
|
|
126
|
+
GET /posts?_embed=comments # Include relations
|
|
127
|
+
GET /posts?_where={"or":[...]} # Complex queries
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
See detailed documentation below for each feature.
|
|
97
131
|
|
|
98
132
|
## Routes
|
|
99
133
|
|
|
100
|
-
|
|
134
|
+
### Array Resources
|
|
101
135
|
|
|
102
|
-
|
|
136
|
+
For array resources like `posts` and `comments`:
|
|
137
|
+
|
|
138
|
+
```http
|
|
103
139
|
GET /posts
|
|
104
140
|
GET /posts/:id
|
|
105
141
|
POST /posts
|
|
106
142
|
PUT /posts/:id
|
|
107
143
|
PATCH /posts/:id
|
|
108
144
|
DELETE /posts/:id
|
|
109
|
-
|
|
110
|
-
# Same for comments
|
|
111
145
|
```
|
|
112
146
|
|
|
113
|
-
|
|
147
|
+
### Object Resources
|
|
148
|
+
|
|
149
|
+
For singular object resources like `profile`:
|
|
150
|
+
|
|
151
|
+
```http
|
|
114
152
|
GET /profile
|
|
115
153
|
PUT /profile
|
|
116
154
|
PATCH /profile
|
|
117
155
|
```
|
|
118
156
|
|
|
119
|
-
##
|
|
157
|
+
## Query params
|
|
120
158
|
|
|
121
159
|
### Conditions
|
|
122
160
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
- `
|
|
128
|
-
- `
|
|
129
|
-
|
|
161
|
+
Use `field:operator=value`.
|
|
162
|
+
|
|
163
|
+
Operators:
|
|
164
|
+
|
|
165
|
+
- no operator -> `eq` (equal)
|
|
166
|
+
- `lt` less than, `lte` less than or equal
|
|
167
|
+
- `gt` greater than, `gte` greater than or equal
|
|
168
|
+
- `eq` equal, `ne` not equal
|
|
169
|
+
- `in` included in comma-separated list
|
|
170
|
+
- `contains` string contains (case-insensitive)
|
|
171
|
+
- `startsWith` string starts with (case-insensitive)
|
|
172
|
+
- `endsWith` string ends with (case-insensitive)
|
|
173
|
+
|
|
174
|
+
Examples:
|
|
175
|
+
|
|
176
|
+
```http
|
|
177
|
+
GET /posts?views:gt=100
|
|
178
|
+
GET /posts?title:eq=Hello
|
|
179
|
+
GET /posts?id:in=1,2,3
|
|
180
|
+
GET /posts?author.name:eq=typicode
|
|
181
|
+
GET /posts?title:contains=hello
|
|
182
|
+
GET /posts?title:startsWith=Hello
|
|
183
|
+
GET /posts?title:endsWith=world
|
|
130
184
|
```
|
|
131
|
-
GET /posts?views_gt=9000
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
### Range
|
|
135
185
|
|
|
136
|
-
|
|
137
|
-
- `end`
|
|
138
|
-
- `limit`
|
|
186
|
+
### Sort
|
|
139
187
|
|
|
188
|
+
```http
|
|
189
|
+
GET /posts?_sort=title
|
|
190
|
+
GET /posts?_sort=-views
|
|
191
|
+
GET /posts?_sort=author.name,-views
|
|
140
192
|
```
|
|
141
|
-
GET /posts?_start=10&_end=20
|
|
142
|
-
GET /posts?_start=10&_limit=10
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
### Paginate
|
|
146
193
|
|
|
147
|
-
|
|
148
|
-
- `per_page` (default = 10)
|
|
194
|
+
### Pagination
|
|
149
195
|
|
|
150
|
-
```
|
|
196
|
+
```http
|
|
151
197
|
GET /posts?_page=1&_per_page=25
|
|
152
198
|
```
|
|
153
199
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
200
|
+
**Response:**
|
|
201
|
+
```json
|
|
202
|
+
{
|
|
203
|
+
"first": 1,
|
|
204
|
+
"prev": null,
|
|
205
|
+
"next": 2,
|
|
206
|
+
"last": 4,
|
|
207
|
+
"pages": 4,
|
|
208
|
+
"items": 100,
|
|
209
|
+
"data": [
|
|
210
|
+
{ "id": "1", "title": "...", "views": 100 },
|
|
211
|
+
{ "id": "2", "title": "...", "views": 200 }
|
|
212
|
+
]
|
|
213
|
+
}
|
|
160
214
|
```
|
|
161
215
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
- `
|
|
165
|
-
- `x.y.z[i]...`
|
|
166
|
-
|
|
167
|
-
```
|
|
168
|
-
GET /foo?a.b=bar
|
|
169
|
-
GET /foo?x.y_lt=100
|
|
170
|
-
GET /foo?arr[0]=bar
|
|
171
|
-
```
|
|
216
|
+
**Notes:**
|
|
217
|
+
- `_per_page` defaults to `10` if not specified
|
|
218
|
+
- Invalid `_page` or `_per_page` values are automatically normalized to valid ranges
|
|
172
219
|
|
|
173
220
|
### Embed
|
|
174
221
|
|
|
175
|
-
```
|
|
222
|
+
```http
|
|
176
223
|
GET /posts?_embed=comments
|
|
177
224
|
GET /comments?_embed=post
|
|
178
225
|
```
|
|
179
226
|
|
|
180
|
-
|
|
227
|
+
### Complex filter with `_where`
|
|
181
228
|
|
|
229
|
+
`_where` accepts a JSON object and overrides normal query params when valid.
|
|
230
|
+
|
|
231
|
+
```http
|
|
232
|
+
GET /posts?_where={"or":[{"views":{"gt":100}},{"author":{"name":{"lt":"m"}}}]}
|
|
182
233
|
```
|
|
183
|
-
|
|
234
|
+
|
|
235
|
+
## Delete dependents
|
|
236
|
+
|
|
237
|
+
```http
|
|
184
238
|
DELETE /posts/1?_dependent=comments
|
|
185
239
|
```
|
|
186
240
|
|
|
187
|
-
##
|
|
241
|
+
## Static Files
|
|
188
242
|
|
|
189
|
-
|
|
243
|
+
JSON Server automatically serves files from the `./public` directory.
|
|
190
244
|
|
|
191
|
-
|
|
245
|
+
To serve additional static directories:
|
|
192
246
|
|
|
193
|
-
```
|
|
194
|
-
json-server -s ./static
|
|
195
|
-
json-server -s ./static -s ./node_modules
|
|
247
|
+
```bash
|
|
248
|
+
json-server db.json -s ./static
|
|
249
|
+
json-server db.json -s ./static -s ./node_modules
|
|
196
250
|
```
|
|
197
251
|
|
|
198
|
-
|
|
252
|
+
Static files are served with standard MIME types and can include HTML, CSS, JavaScript, images, and other assets.
|
|
253
|
+
|
|
254
|
+
## Migration Notes (v0 → v1)
|
|
255
|
+
|
|
256
|
+
If you are upgrading from json-server v0.x, note these behavioral changes:
|
|
257
|
+
|
|
258
|
+
- **ID handling:** `id` is always a string and will be auto-generated if not provided
|
|
259
|
+
- **Pagination:** Use `_per_page` with `_page` instead of the deprecated `_limit` parameter
|
|
260
|
+
- **Relationships:** Use `_embed` instead of `_expand` for including related resources
|
|
261
|
+
- **Request delays:** Use browser DevTools (Network tab > throttling) instead of the removed `--delay` CLI option
|
|
199
262
|
|
|
200
|
-
-
|
|
201
|
-
- use `_per_page` with `_page` instead of `_limit`for pagination
|
|
202
|
-
- use Chrome's `Network tab > throtling` to delay requests instead of `--delay` CLI option
|
|
263
|
+
> **New to json-server?** These notes are for users migrating from v0. If this is your first time using json-server, you can ignore this section.
|
package/lib/app.js
CHANGED
|
@@ -3,15 +3,70 @@ import { fileURLToPath } from 'node:url';
|
|
|
3
3
|
import { App } from '@tinyhttp/app';
|
|
4
4
|
import { cors } from '@tinyhttp/cors';
|
|
5
5
|
import { Eta } from 'eta';
|
|
6
|
+
import { Low } from 'lowdb';
|
|
6
7
|
import { json } from 'milliparsec';
|
|
7
8
|
import sirv from 'sirv';
|
|
8
|
-
import {
|
|
9
|
+
import { parseWhere } from "./parse-where.js";
|
|
10
|
+
import { isItem, Service } from "./service.js";
|
|
9
11
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
10
12
|
const isProduction = process.env['NODE_ENV'] === 'production';
|
|
11
13
|
const eta = new Eta({
|
|
12
14
|
views: join(__dirname, '../views'),
|
|
13
15
|
cache: isProduction,
|
|
14
16
|
});
|
|
17
|
+
const RESERVED_QUERY_KEYS = new Set(['_sort', '_page', '_per_page', '_embed', '_where']);
|
|
18
|
+
function parseListParams(req) {
|
|
19
|
+
const queryString = req.url.split('?')[1] ?? '';
|
|
20
|
+
const params = new URLSearchParams(queryString);
|
|
21
|
+
const filterParams = new URLSearchParams();
|
|
22
|
+
for (const [key, value] of params.entries()) {
|
|
23
|
+
if (!RESERVED_QUERY_KEYS.has(key)) {
|
|
24
|
+
filterParams.append(key, value);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
let where = parseWhere(filterParams.toString());
|
|
28
|
+
const rawWhere = params.get('_where');
|
|
29
|
+
if (typeof rawWhere === 'string') {
|
|
30
|
+
try {
|
|
31
|
+
const parsed = JSON.parse(rawWhere);
|
|
32
|
+
if (typeof parsed === 'object' && parsed !== null) {
|
|
33
|
+
where = parsed;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
// Ignore invalid JSON and fallback to parsed query params
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
const pageRaw = params.get('_page');
|
|
41
|
+
const perPageRaw = params.get('_per_page');
|
|
42
|
+
const page = pageRaw === null ? undefined : Number.parseInt(pageRaw, 10);
|
|
43
|
+
const perPage = perPageRaw === null ? undefined : Number.parseInt(perPageRaw, 10);
|
|
44
|
+
return {
|
|
45
|
+
where,
|
|
46
|
+
sort: params.get('_sort') ?? undefined,
|
|
47
|
+
page: Number.isNaN(page) ? undefined : page,
|
|
48
|
+
perPage: Number.isNaN(perPage) ? undefined : perPage,
|
|
49
|
+
embed: req.query['_embed'],
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
function withBody(action) {
|
|
53
|
+
return async (req, res, next) => {
|
|
54
|
+
const { name = '' } = req.params;
|
|
55
|
+
if (isItem(req.body)) {
|
|
56
|
+
res.locals['data'] = await action(name, req.body);
|
|
57
|
+
}
|
|
58
|
+
next?.();
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
function withIdAndBody(action) {
|
|
62
|
+
return async (req, res, next) => {
|
|
63
|
+
const { name = '', id = '' } = req.params;
|
|
64
|
+
if (isItem(req.body)) {
|
|
65
|
+
res.locals['data'] = await action(name, id, req.body);
|
|
66
|
+
}
|
|
67
|
+
next?.();
|
|
68
|
+
};
|
|
69
|
+
}
|
|
15
70
|
export function createApp(db, options = {}) {
|
|
16
71
|
// Create service
|
|
17
72
|
const service = new Service(db);
|
|
@@ -23,69 +78,44 @@ export function createApp(db, options = {}) {
|
|
|
23
78
|
?.map((path) => (isAbsolute(path) ? path : join(process.cwd(), path)))
|
|
24
79
|
.forEach((dir) => app.use(sirv(dir, { dev: !isProduction })));
|
|
25
80
|
// CORS
|
|
26
|
-
app
|
|
81
|
+
app
|
|
82
|
+
.use((req, res, next) => {
|
|
83
|
+
return cors({
|
|
84
|
+
allowedHeaders: req.headers['access-control-request-headers']
|
|
85
|
+
?.split(',')
|
|
86
|
+
.map((h) => h.trim()),
|
|
87
|
+
})(req, res, next);
|
|
88
|
+
})
|
|
89
|
+
.options('*', cors());
|
|
27
90
|
// Body parser
|
|
28
91
|
app.use(json());
|
|
29
92
|
app.get('/', (_req, res) => res.send(eta.render('index.html', { data: db.data })));
|
|
30
93
|
app.get('/:name', (req, res, next) => {
|
|
31
94
|
const { name = '' } = req.params;
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
.filter(([_, value]) => !Number.isNaN(value)));
|
|
42
|
-
res.locals['data'] = service.find(name, query);
|
|
43
|
-
next();
|
|
95
|
+
const { where, sort, page, perPage, embed } = parseListParams(req);
|
|
96
|
+
res.locals['data'] = service.find(name, {
|
|
97
|
+
where,
|
|
98
|
+
sort,
|
|
99
|
+
page,
|
|
100
|
+
perPage,
|
|
101
|
+
embed,
|
|
102
|
+
});
|
|
103
|
+
next?.();
|
|
44
104
|
});
|
|
45
105
|
app.get('/:name/:id', (req, res, next) => {
|
|
46
106
|
const { name = '', id = '' } = req.params;
|
|
47
107
|
res.locals['data'] = service.findById(name, id, req.query);
|
|
48
|
-
next();
|
|
49
|
-
});
|
|
50
|
-
app.post('/:name', async (req, res, next) => {
|
|
51
|
-
const { name = '' } = req.params;
|
|
52
|
-
if (isItem(req.body)) {
|
|
53
|
-
res.locals['data'] = await service.create(name, req.body);
|
|
54
|
-
}
|
|
55
|
-
next();
|
|
56
|
-
});
|
|
57
|
-
app.put('/:name', async (req, res, next) => {
|
|
58
|
-
const { name = '' } = req.params;
|
|
59
|
-
if (isItem(req.body)) {
|
|
60
|
-
res.locals['data'] = await service.update(name, req.body);
|
|
61
|
-
}
|
|
62
|
-
next();
|
|
63
|
-
});
|
|
64
|
-
app.put('/:name/:id', async (req, res, next) => {
|
|
65
|
-
const { name = '', id = '' } = req.params;
|
|
66
|
-
if (isItem(req.body)) {
|
|
67
|
-
res.locals['data'] = await service.updateById(name, id, req.body);
|
|
68
|
-
}
|
|
69
|
-
next();
|
|
70
|
-
});
|
|
71
|
-
app.patch('/:name', async (req, res, next) => {
|
|
72
|
-
const { name = '' } = req.params;
|
|
73
|
-
if (isItem(req.body)) {
|
|
74
|
-
res.locals['data'] = await service.patch(name, req.body);
|
|
75
|
-
}
|
|
76
|
-
next();
|
|
77
|
-
});
|
|
78
|
-
app.patch('/:name/:id', async (req, res, next) => {
|
|
79
|
-
const { name = '', id = '' } = req.params;
|
|
80
|
-
if (isItem(req.body)) {
|
|
81
|
-
res.locals['data'] = await service.patchById(name, id, req.body);
|
|
82
|
-
}
|
|
83
|
-
next();
|
|
108
|
+
next?.();
|
|
84
109
|
});
|
|
110
|
+
app.post('/:name', withBody(service.create.bind(service)));
|
|
111
|
+
app.put('/:name', withBody(service.update.bind(service)));
|
|
112
|
+
app.put('/:name/:id', withIdAndBody(service.updateById.bind(service)));
|
|
113
|
+
app.patch('/:name', withBody(service.patch.bind(service)));
|
|
114
|
+
app.patch('/:name/:id', withIdAndBody(service.patchById.bind(service)));
|
|
85
115
|
app.delete('/:name/:id', async (req, res, next) => {
|
|
86
116
|
const { name = '', id = '' } = req.params;
|
|
87
|
-
res.locals['data'] = await service.destroyById(name, id, req.query['
|
|
88
|
-
next();
|
|
117
|
+
res.locals['data'] = await service.destroyById(name, id, req.query['_dependent']);
|
|
118
|
+
next?.();
|
|
89
119
|
});
|
|
90
120
|
app.use('/:name', (req, res) => {
|
|
91
121
|
const { data } = res.locals;
|