json-server 1.0.0-beta.1 → 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 +140 -88
- package/lib/app.js +81 -51
- 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 +58 -44
- 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
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
This starts the server at `http://localhost:3000`. You should see:
|
|
69
|
+
```
|
|
70
|
+
JSON Server started on PORT :3000
|
|
71
|
+
http://localhost:3000
|
|
64
72
|
```
|
|
65
73
|
|
|
66
|
-
|
|
74
|
+
Access your REST API:
|
|
67
75
|
|
|
68
|
-
```
|
|
69
|
-
|
|
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,135 +90,174 @@ Run `json-server --help` for a list of options
|
|
|
77
90
|
|
|
78
91
|
## Sponsors ✨
|
|
79
92
|
|
|
80
|
-
|
|
81
|
-
| :---: |
|
|
82
|
-
| <a href="https://mockend.com/" target="_blank"><img src="https://jsonplaceholder.typicode.com/mockend.svg" height="100px"></a> |
|
|
83
|
-
| <a href="https://zuplo.link/json-server-gh"><picture><source media="(prefers-color-scheme: dark)" srcset="https://github.com/typicode/json-server/assets/5502029/bb7abea9-fc54-4612-b7ab-0221c60b8ac7"><img alt="scalar" src="https://github.com/typicode/json-server/assets/5502029/51f5afed-7ba0-41bf-b9f2-ef9d8ab9dff7"></picture></a> |
|
|
93
|
+
### Gold
|
|
84
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>
|
|
85
102
|
|
|
86
|
-
| Sponsors |
|
|
87
|
-
| :---: |
|
|
88
|
-
| <a href="https://konghq.com/products/kong-konnect/register?utm_medium=referral&utm_source=github&utm_campaign=platform&utm_content=json-server"><img src="https://github.com/typicode/json-server/assets/5502029/e8d8ecb2-3c45-4f60-92d0-a060b820fa7f" height="75px"></a> |
|
|
89
103
|
|
|
90
|
-
|
|
91
|
-
| :---: | :---: |
|
|
92
|
-
| <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> |
|
|
93
|
-
| <a href="https://www.globalsoftwarecompanies.com" target="_blank"><img src="https://github.com/typicode/json-server/assets/5502029/d0bb2e9b-9b74-4d91-9c9e-f3c87e152918" height="35px"></a> | <a href="https://beeceptor.com/?utm_source=json-server"><img src="https://github.com/typicode/json-server/assets/5502029/57b852a6-60b9-426b-986e-a148e82783c2" height="35px"></a> |
|
|
104
|
+
### Silver
|
|
94
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> |
|
|
95
109
|
|
|
110
|
+
### Bronze
|
|
96
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> |
|
|
97
115
|
|
|
98
116
|
[Become a sponsor and have your company logo here](https://github.com/users/typicode/sponsorship)
|
|
99
117
|
|
|
100
|
-
##
|
|
118
|
+
## Query Capabilities
|
|
101
119
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
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.
|
|
106
131
|
|
|
107
132
|
## Routes
|
|
108
133
|
|
|
109
|
-
|
|
134
|
+
### Array Resources
|
|
110
135
|
|
|
111
|
-
|
|
136
|
+
For array resources like `posts` and `comments`:
|
|
137
|
+
|
|
138
|
+
```http
|
|
112
139
|
GET /posts
|
|
113
140
|
GET /posts/:id
|
|
114
141
|
POST /posts
|
|
115
142
|
PUT /posts/:id
|
|
116
143
|
PATCH /posts/:id
|
|
117
144
|
DELETE /posts/:id
|
|
118
|
-
|
|
119
|
-
# Same for comments
|
|
120
145
|
```
|
|
121
146
|
|
|
122
|
-
|
|
147
|
+
### Object Resources
|
|
148
|
+
|
|
149
|
+
For singular object resources like `profile`:
|
|
150
|
+
|
|
151
|
+
```http
|
|
123
152
|
GET /profile
|
|
124
153
|
PUT /profile
|
|
125
154
|
PATCH /profile
|
|
126
155
|
```
|
|
127
156
|
|
|
128
|
-
##
|
|
157
|
+
## Query params
|
|
129
158
|
|
|
130
159
|
### Conditions
|
|
131
160
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
- `
|
|
137
|
-
- `
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
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
|
|
141
184
|
```
|
|
142
185
|
|
|
143
|
-
###
|
|
144
|
-
|
|
145
|
-
- `start`
|
|
146
|
-
- `end`
|
|
147
|
-
- `limit`
|
|
186
|
+
### Sort
|
|
148
187
|
|
|
149
|
-
```
|
|
150
|
-
GET /posts?
|
|
151
|
-
GET /posts?
|
|
188
|
+
```http
|
|
189
|
+
GET /posts?_sort=title
|
|
190
|
+
GET /posts?_sort=-views
|
|
191
|
+
GET /posts?_sort=author.name,-views
|
|
152
192
|
```
|
|
153
193
|
|
|
154
|
-
###
|
|
194
|
+
### Pagination
|
|
155
195
|
|
|
156
|
-
|
|
157
|
-
- `per_page` (default = 10)
|
|
158
|
-
|
|
159
|
-
```
|
|
196
|
+
```http
|
|
160
197
|
GET /posts?_page=1&_per_page=25
|
|
161
198
|
```
|
|
162
199
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
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
|
+
}
|
|
169
214
|
```
|
|
170
215
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
- `
|
|
174
|
-
- `x.y.z[i]...`
|
|
175
|
-
|
|
176
|
-
```
|
|
177
|
-
GET /foo?a.b=bar
|
|
178
|
-
GET /foo?x.y_lt=100
|
|
179
|
-
GET /foo?arr[0]=bar
|
|
180
|
-
```
|
|
216
|
+
**Notes:**
|
|
217
|
+
- `_per_page` defaults to `10` if not specified
|
|
218
|
+
- Invalid `_page` or `_per_page` values are automatically normalized to valid ranges
|
|
181
219
|
|
|
182
220
|
### Embed
|
|
183
221
|
|
|
184
|
-
```
|
|
222
|
+
```http
|
|
185
223
|
GET /posts?_embed=comments
|
|
186
224
|
GET /comments?_embed=post
|
|
187
225
|
```
|
|
188
226
|
|
|
189
|
-
|
|
227
|
+
### Complex filter with `_where`
|
|
228
|
+
|
|
229
|
+
`_where` accepts a JSON object and overrides normal query params when valid.
|
|
190
230
|
|
|
231
|
+
```http
|
|
232
|
+
GET /posts?_where={"or":[{"views":{"gt":100}},{"author":{"name":{"lt":"m"}}}]}
|
|
191
233
|
```
|
|
192
|
-
|
|
234
|
+
|
|
235
|
+
## Delete dependents
|
|
236
|
+
|
|
237
|
+
```http
|
|
193
238
|
DELETE /posts/1?_dependent=comments
|
|
194
239
|
```
|
|
195
240
|
|
|
196
|
-
##
|
|
241
|
+
## Static Files
|
|
197
242
|
|
|
198
|
-
|
|
243
|
+
JSON Server automatically serves files from the `./public` directory.
|
|
199
244
|
|
|
200
|
-
|
|
245
|
+
To serve additional static directories:
|
|
201
246
|
|
|
202
|
-
```
|
|
203
|
-
json-server -s ./static
|
|
204
|
-
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
|
|
205
250
|
```
|
|
206
251
|
|
|
207
|
-
|
|
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
|
|
208
262
|
|
|
209
|
-
-
|
|
210
|
-
- use `_per_page` with `_page` instead of `_limit`for pagination
|
|
211
|
-
- 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
117
|
res.locals['data'] = await service.destroyById(name, id, req.query['_dependent']);
|
|
88
|
-
next();
|
|
118
|
+
next?.();
|
|
89
119
|
});
|
|
90
120
|
app.use('/:name', (req, res) => {
|
|
91
121
|
const { data } = res.locals;
|