chadstart 1.0.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/.dockerignore +10 -0
- package/.env.example +46 -0
- package/.github/workflows/browser-test.yml +34 -0
- package/.github/workflows/docker-publish.yml +54 -0
- package/.github/workflows/docs.yml +31 -0
- package/.github/workflows/npm-chadstart.yml +27 -0
- package/.github/workflows/npm-sdk.yml +38 -0
- package/.github/workflows/test.yml +85 -0
- package/.weblate +9 -0
- package/Dockerfile +23 -0
- package/README.md +348 -0
- package/admin/index.html +2802 -0
- package/admin/login.html +207 -0
- package/chadstart.example.yml +416 -0
- package/chadstart.schema.json +367 -0
- package/chadstart.yaml +53 -0
- package/cli/cli.js +295 -0
- package/core/api-generator.js +606 -0
- package/core/auth.js +298 -0
- package/core/db.js +384 -0
- package/core/entity-engine.js +166 -0
- package/core/error-reporter.js +132 -0
- package/core/file-storage.js +97 -0
- package/core/functions-engine.js +353 -0
- package/core/openapi.js +171 -0
- package/core/plugin-loader.js +92 -0
- package/core/realtime.js +93 -0
- package/core/schema-validator.js +50 -0
- package/core/seeder.js +231 -0
- package/core/telemetry.js +119 -0
- package/core/upload.js +372 -0
- package/core/workers/php_worker.php +19 -0
- package/core/workers/python_worker.py +33 -0
- package/core/workers/ruby_worker.rb +21 -0
- package/core/yaml-loader.js +64 -0
- package/demo/chadstart.yaml +178 -0
- package/demo/docker-compose.yml +31 -0
- package/demo/functions/greet.go +39 -0
- package/demo/functions/hello.cpp +18 -0
- package/demo/functions/hello.py +13 -0
- package/demo/functions/hello.rb +10 -0
- package/demo/functions/onTodoCreated.js +13 -0
- package/demo/functions/ping.sh +13 -0
- package/demo/functions/stats.js +22 -0
- package/demo/public/index.html +522 -0
- package/docker-compose.yml +17 -0
- package/docs/access-policies.md +155 -0
- package/docs/admin-ui.md +29 -0
- package/docs/angular.md +69 -0
- package/docs/astro.md +71 -0
- package/docs/auth.md +160 -0
- package/docs/cli.md +56 -0
- package/docs/config.md +127 -0
- package/docs/crud.md +627 -0
- package/docs/deploy.md +113 -0
- package/docs/docker.md +59 -0
- package/docs/entities.md +385 -0
- package/docs/functions.md +196 -0
- package/docs/getting-started.md +79 -0
- package/docs/groups.md +85 -0
- package/docs/index.md +5 -0
- package/docs/llm-rules.md +81 -0
- package/docs/middlewares.md +78 -0
- package/docs/overrides/home.html +350 -0
- package/docs/plugins.md +59 -0
- package/docs/react.md +75 -0
- package/docs/realtime.md +43 -0
- package/docs/s3-storage.md +40 -0
- package/docs/security.md +23 -0
- package/docs/stylesheets/extra.css +375 -0
- package/docs/svelte.md +71 -0
- package/docs/telemetry.md +97 -0
- package/docs/upload.md +168 -0
- package/docs/validation.md +115 -0
- package/docs/vue.md +86 -0
- package/docs/webhooks.md +87 -0
- package/index.js +11 -0
- package/locales/en/admin.json +169 -0
- package/mkdocs.yml +82 -0
- package/package.json +65 -0
- package/playwright.config.js +24 -0
- package/public/.gitkeep +0 -0
- package/sdk/README.md +284 -0
- package/sdk/package.json +39 -0
- package/sdk/scripts/build.js +58 -0
- package/sdk/src/index.js +368 -0
- package/sdk/test/sdk.test.cjs +340 -0
- package/sdk/types/index.d.ts +217 -0
- package/server/express-server.js +734 -0
- package/test/access-policies.test.js +96 -0
- package/test/ai.test.js +81 -0
- package/test/api-keys.test.js +361 -0
- package/test/auth.test.js +122 -0
- package/test/browser/admin-ui.spec.js +127 -0
- package/test/browser/global-setup.js +71 -0
- package/test/browser/global-teardown.js +11 -0
- package/test/db.test.js +227 -0
- package/test/entity-engine.test.js +193 -0
- package/test/error-reporter.test.js +140 -0
- package/test/functions-engine.test.js +240 -0
- package/test/groups.test.js +212 -0
- package/test/hot-reload.test.js +153 -0
- package/test/i18n.test.js +173 -0
- package/test/middleware.test.js +76 -0
- package/test/openapi.test.js +67 -0
- package/test/schema-validator.test.js +83 -0
- package/test/sdk.test.js +90 -0
- package/test/seeder.test.js +279 -0
- package/test/settings.test.js +109 -0
- package/test/telemetry.test.js +254 -0
- package/test/test.js +17 -0
- package/test/upload.test.js +265 -0
- package/test/validation.test.js +96 -0
- package/test/yaml-loader.test.js +93 -0
- package/utils/logger.js +24 -0
package/docs/crud.md
ADDED
|
@@ -0,0 +1,627 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: crud
|
|
3
|
+
title: CRUD Operations
|
|
4
|
+
description: Documentation for out-of-the-box CRUD endpoints with ChadStart. Paginated lists, detail views, creating and updating single or collection entities.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# CRUD operations
|
|
8
|
+
|
|
9
|
+
## Introduction
|
|
10
|
+
|
|
11
|
+
Once you created your [entities](./entities.md), you probably want to interact with them. It is easy to connect your client to your ChadStart backend.
|
|
12
|
+
|
|
13
|
+
ChadStart provides out-of-the-box CRUD endpoints through the **REST API** or the **JS SDK**.
|
|
14
|
+
|
|
15
|
+
!!! info
|
|
16
|
+
By default CRUD endpoints are private, only accessible for logged-in **admin** users. You can open them to the public using [policies](./access-policies.md).
|
|
17
|
+
|
|
18
|
+
## Using the REST API
|
|
19
|
+
|
|
20
|
+
ChadStart exposes a REST API for CRUD operations. The **OpenAPI** documentation is automatically generated and the UI is available at http://localhost:3000/api. Have a look!
|
|
21
|
+
|
|
22
|
+
An`openapi.yml` file is also generated along a `types.ts` file in the `./chadstart` folder. Those 2 files are an amazing source of context for your **LLM**. If you want to connect a frontend to your ChadStart backend, make sure that your **AI coding tool** sees those files to simplify your development.
|
|
23
|
+
|
|
24
|
+
For CRUD endpoints, this prefix is followed by `collections` for [collections entities](#collections) and `singles` for [single entities](#singles) and by the slug of your entity (you can change it in the [entity params](entities.md#entity-params))
|
|
25
|
+
|
|
26
|
+
Examples:
|
|
27
|
+
|
|
28
|
+
- `http://localhost:3000/api/collections/cats` gets the list of the cats
|
|
29
|
+
- `http://localhost:3000/api/singles/home-content` gets the home content
|
|
30
|
+
|
|
31
|
+
!!! tip
|
|
32
|
+
In addition to **CRUD endpoints** that are generated automatically, you also can create your own [custom endpoints](./functions.md) to add your custom logic.
|
|
33
|
+
|
|
34
|
+
## Using the JavaScript SDK
|
|
35
|
+
|
|
36
|
+
The **ChadStart JS SDK** is used to fetch and manipulate your data from your JS client using an elegant and human-friendly interface.
|
|
37
|
+
|
|
38
|
+
The SDK can be integrated in any frontend stack app like [React](./react.md), [Vue](./vue.md), [Svelte](./svelte.md), [Astro](./astro.md), [Angular](./angular.md).... Or even by another server using NodeJS!
|
|
39
|
+
|
|
40
|
+
Install it via the terminal:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
npm i @chadstart/sdk
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Use the SDK directly in your favorite frontend:
|
|
47
|
+
|
|
48
|
+
```js title="Example SDK usage"
|
|
49
|
+
import ChadStart from '@chadstart/sdk'
|
|
50
|
+
|
|
51
|
+
// Initialize client with default backend URL: http://localhost:3000.
|
|
52
|
+
const chadstart = new ChadStart()
|
|
53
|
+
|
|
54
|
+
// Initialize client with custom base URL.
|
|
55
|
+
const chadstart = new ChadStart('https://example.com')
|
|
56
|
+
|
|
57
|
+
// Perform CRUD operations...
|
|
58
|
+
const cats = await chadstart.from('cats').find()
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Collections
|
|
62
|
+
|
|
63
|
+
The following CRUD operations can be done on [collections](./entities.md#collections) entities. A collection is a list of items that share a similar schema.
|
|
64
|
+
|
|
65
|
+
### Get a list of items
|
|
66
|
+
|
|
67
|
+
This operation will fetch a list of items from a collection.
|
|
68
|
+
=== "REST API"
|
|
69
|
+
**Request URL**: `GET /api/collections/:slug`
|
|
70
|
+
|
|
71
|
+
```http title="Example HTTP Request"
|
|
72
|
+
GET /api/collection/users
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
```json title="Example HTTP Response"
|
|
76
|
+
{
|
|
77
|
+
"data": [
|
|
78
|
+
{
|
|
79
|
+
"id": "2c4e6a8b-0d1f-4357-9ace-bdf024681357",
|
|
80
|
+
"name": "Lara"
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
"id": "e4d5c6b7-a890-4123-9876-543210fedcba",
|
|
84
|
+
"name": "Karl"
|
|
85
|
+
}
|
|
86
|
+
],
|
|
87
|
+
"currentPage": 1,
|
|
88
|
+
"lastPage": 1,
|
|
89
|
+
"from": 1,
|
|
90
|
+
"to": 10,
|
|
91
|
+
"total": 3,
|
|
92
|
+
"perPage": 10
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
**List filters**
|
|
97
|
+
|
|
98
|
+
You can filter by [property](./entities.md#properties) to refine the list of items. Use suffix to pass logic to it:
|
|
99
|
+
|
|
100
|
+
| Suffix | Description | Example |
|
|
101
|
+
| ---------- | --------------------- | -------------------------- |
|
|
102
|
+
| **\_eq** | equals | `isActive_eq=true` |
|
|
103
|
+
| **\_neq** | not equals | `name_neq=alice` |
|
|
104
|
+
| **\_gt** | greater than | `birthdate_gt=2020-01-01 ` |
|
|
105
|
+
| **\_gte** | greater than or equal | `age_gte=4` |
|
|
106
|
+
| **\_lt** | less than | `amount_lt=400` |
|
|
107
|
+
| **\_lte** | less than or equal | `amount_lte=400` |
|
|
108
|
+
| **\_like** | like | `name_like=%bi%` |
|
|
109
|
+
| **\_in** | included in | `customer_in=1,2,3` |
|
|
110
|
+
|
|
111
|
+
**Pagination**
|
|
112
|
+
|
|
113
|
+
All list requests are paginated by default. Just use the `page` parameter to choose your page and the `perPage` param if you want to change the number of items per page.
|
|
114
|
+
|
|
115
|
+
| Param | Description | Example |
|
|
116
|
+
| ----------- | -------------------------------- | ------------ |
|
|
117
|
+
| **page** | The number of the page requested | `page=3` |
|
|
118
|
+
| **perPage** | The number of items of each page | `perPage=40` |
|
|
119
|
+
|
|
120
|
+
**Order**
|
|
121
|
+
|
|
122
|
+
Order your list by a defined property. By default the results are ordered by `id` in a `DESC` order and thus shows the new ones first.
|
|
123
|
+
|
|
124
|
+
| Param | Description | Example |
|
|
125
|
+
| ----------- | ------------------------------------------ | ------------- |
|
|
126
|
+
| **orderBy** | The name of property you want to order by. | `orderBy=age` |
|
|
127
|
+
| **order** | Ascending 'ASC' or Descending 'DESC' | `order=DESC` |
|
|
128
|
+
|
|
129
|
+
=== "JS SDK"
|
|
130
|
+
```js title="Example SDK usage"
|
|
131
|
+
// Get all users.
|
|
132
|
+
const users = await chadstart.from('users').find()
|
|
133
|
+
|
|
134
|
+
console.log(users);
|
|
135
|
+
// Output: {
|
|
136
|
+
// "data": [
|
|
137
|
+
// {
|
|
138
|
+
// "id": 'e4d5c6b7-a890-4123-9876-543210fedcba',
|
|
139
|
+
// "name": "Lara"
|
|
140
|
+
// },
|
|
141
|
+
// {
|
|
142
|
+
// "id": '2c4e6a8b-0d1f-4357-9ace-bdf024681357',
|
|
143
|
+
// "name": "Karl"
|
|
144
|
+
// }
|
|
145
|
+
// ],
|
|
146
|
+
// "currentPage": 1,
|
|
147
|
+
// "lastPage": 1,
|
|
148
|
+
// "from": 1,
|
|
149
|
+
// "to": 10,
|
|
150
|
+
// "total": 3,
|
|
151
|
+
// "perPage": 10
|
|
152
|
+
// }
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
**List filters**
|
|
156
|
+
|
|
157
|
+
You can filter by [property](./entities.md#properties) to refine the list of items. Use the `where()` function with the correct operator to do it.
|
|
158
|
+
|
|
159
|
+
```js title="Example SDK list filtering"
|
|
160
|
+
const cats = await chadstart
|
|
161
|
+
.from('cats')
|
|
162
|
+
.where('breed = siamese')
|
|
163
|
+
.andWhere('active = true')
|
|
164
|
+
.andWhere('birthDate > 2020-01-01')
|
|
165
|
+
.find()
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
**Filter operators**
|
|
169
|
+
|
|
170
|
+
| Operator | Description | Example |
|
|
171
|
+
| -------- | --------------------- | ---------------------------------- |
|
|
172
|
+
| **=** | equals | `.where('isActive = true')` |
|
|
173
|
+
| **!=** | not equals | `.where('name != alice')` |
|
|
174
|
+
| **>** | greater than | `.where('birthdate > 2020-01-01')` |
|
|
175
|
+
| **>=** | greater than or equal | `.where('age >= 4')` |
|
|
176
|
+
| **\<** | less than | `.where('amount < 400')` |
|
|
177
|
+
| **\<=** | less than or equal | `.where('amount <= 400')` |
|
|
178
|
+
| **like** | like | `.where('name_like=%bi%')` |
|
|
179
|
+
| **in** | included in | `.where('customer_in=1,2,3')` |
|
|
180
|
+
|
|
181
|
+
**Pagination**
|
|
182
|
+
|
|
183
|
+
All list requests are paginated by default. Just use the `page` parameter to chose your page and the `perPage` param if you want to change the number of items per page.
|
|
184
|
+
|
|
185
|
+
```js title="Example SDK list pagination"
|
|
186
|
+
const cats = await chadstart.from('cats').find({ page: 1, perPage: 10 })
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
**Order**
|
|
190
|
+
|
|
191
|
+
Order your list by a defined property. By default the results are ordered by `id` in a `DESC` order and thus shows the new ones first.
|
|
192
|
+
|
|
193
|
+
```js title="Example SDK order"
|
|
194
|
+
// Order cats.
|
|
195
|
+
const cats = await chadstart.from('cats').orderBy('age', { desc: true }).find()
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### Get a single item
|
|
199
|
+
|
|
200
|
+
This operation will fetch a single item based on its ID.
|
|
201
|
+
|
|
202
|
+
=== "REST API"
|
|
203
|
+
**Request URL**: `GET /api/collections/:slug/:id`
|
|
204
|
+
|
|
205
|
+
```http title="Example HTTP Request"
|
|
206
|
+
GET /api/collections/cats/2c4e6a8b-0d1f-4357-9ace-bdf024681357
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
```json title="Example HTTP Response"
|
|
210
|
+
{
|
|
211
|
+
"name": "Mina",
|
|
212
|
+
"description": "A really cute cat"
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
=== "JS SDK"
|
|
217
|
+
```js title="Example SDK usage"
|
|
218
|
+
// Get cat with ID 2c4e6a8b-0d1f-4357-9ace-bdf024681357
|
|
219
|
+
const cat = await chadstart.from('cats').findOneById('2c4e6a8b-0d1f-4357-9ace-bdf024681357')
|
|
220
|
+
|
|
221
|
+
console.log(cat);
|
|
222
|
+
// Output: {
|
|
223
|
+
// name: "Mina",
|
|
224
|
+
// description: "A really cute cat"
|
|
225
|
+
// }
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### Create a new item
|
|
229
|
+
|
|
230
|
+
This operation will create a new item and store it in the database. The newly created item is returned as response.
|
|
231
|
+
=== "REST API"
|
|
232
|
+
**Request URL**: `POST /api/collections/:slug`
|
|
233
|
+
|
|
234
|
+
```http title="Example HTTP Request"
|
|
235
|
+
POST /api/collections/pokemons
|
|
236
|
+
Content-Type: application/json
|
|
237
|
+
Body:
|
|
238
|
+
{
|
|
239
|
+
"name": "Pikachu",
|
|
240
|
+
"type": "electric",
|
|
241
|
+
"level": 3,
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
```json title="Example HTTP Response"
|
|
247
|
+
{
|
|
248
|
+
"id": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
|
|
249
|
+
"name": "Pikachu",
|
|
250
|
+
"type": "electric",
|
|
251
|
+
"level": 3,
|
|
252
|
+
}
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
=== "JS SDK"
|
|
256
|
+
```js title="Example SDK usage"
|
|
257
|
+
// Create a new item in the "pokemons" entity.
|
|
258
|
+
const newPokemon = await chadstart.from('pokemons').create({
|
|
259
|
+
name: "Pikachu",
|
|
260
|
+
type: "electric",
|
|
261
|
+
level: 3,
|
|
262
|
+
})
|
|
263
|
+
|
|
264
|
+
console.log(newPokemon);
|
|
265
|
+
// Output: {
|
|
266
|
+
// id: "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
|
|
267
|
+
// name: "Pikachu",
|
|
268
|
+
// type: "electric",
|
|
269
|
+
// level: 3
|
|
270
|
+
// }
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
### Update an item
|
|
274
|
+
|
|
275
|
+
This operation will replace an existing item by the payload provided in the request and returns the updated item.
|
|
276
|
+
|
|
277
|
+
Unlike [partial updates](#patch-an-item), this operation will replace the whole item by the new one. Missing or empty properties will delete the previous ones.
|
|
278
|
+
|
|
279
|
+
=== "REST API"
|
|
280
|
+
**Request URL**: `PUT /api/collections/:slug/:id`
|
|
281
|
+
|
|
282
|
+
```http title="Example HTTP Request"
|
|
283
|
+
PUT /api/collections/pokemons/6ba7b810-9dad-11d1-80b4-00c04fd430c8
|
|
284
|
+
Content-Type: application/json
|
|
285
|
+
Body:
|
|
286
|
+
{
|
|
287
|
+
"name": "Raichu",
|
|
288
|
+
"type": "electric",
|
|
289
|
+
"level": 8
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
```json title="Example HTTP Response"
|
|
295
|
+
{
|
|
296
|
+
"id": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
|
|
297
|
+
"name": "Raichu",
|
|
298
|
+
"type": "electric",
|
|
299
|
+
"level": 8
|
|
300
|
+
}
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
=== "JS SDK"
|
|
304
|
+
```js title="Example SDK usage"
|
|
305
|
+
// Updates the Pokemon item with ID a1b2c3d4-e5f6-4789-abcd-ef0123456789.
|
|
306
|
+
const newPokemon = await chadstart.from('pokemons').update('a1b2c3d4-e5f6-4789-abcd-ef0123456789', {
|
|
307
|
+
name: "Raichu",
|
|
308
|
+
type: "electric",
|
|
309
|
+
level: 8,
|
|
310
|
+
})
|
|
311
|
+
|
|
312
|
+
console.log(newPokemon);
|
|
313
|
+
// Output: {
|
|
314
|
+
// id: "a1b2c3d4-e5f6-4789-abcd-ef0123456789",
|
|
315
|
+
// name: "Raichu",
|
|
316
|
+
// type: "electric",
|
|
317
|
+
// level: 8
|
|
318
|
+
// }
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
### Patch an item
|
|
322
|
+
|
|
323
|
+
This operation will partially replace an existing item and return the updated item.
|
|
324
|
+
|
|
325
|
+
Unlike [fully replacement](#update-an-item), this operation will only modify the properties provided in the payload and leave the other ones as they are.
|
|
326
|
+
=== "REST API"
|
|
327
|
+
**Request URL**: `PATCH /api/collections/:slug/:id`
|
|
328
|
+
|
|
329
|
+
```http title="Example HTTP Request"
|
|
330
|
+
PATCH /api/collections/pokemons/a1b2c3d4-e5f6-4789-abcd-ef0123456789
|
|
331
|
+
Content-Type: application/json
|
|
332
|
+
Body:
|
|
333
|
+
{
|
|
334
|
+
"level": 5,
|
|
335
|
+
}
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
```json title="Example HTTP Response"
|
|
339
|
+
{
|
|
340
|
+
"id": "a1b2c3d4-e5f6-4789-abcd-ef0123456789",
|
|
341
|
+
"name": "Pikachu",
|
|
342
|
+
"type": "electric",
|
|
343
|
+
"level": 5
|
|
344
|
+
}
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
=== "JS SDK"
|
|
348
|
+
```js title="Example SDK usage"
|
|
349
|
+
// Patches the Pokemon item with ID a1b2c3d4-e5f6-4789-abcd-ef0123456789.
|
|
350
|
+
const newPokemon = await chadstart.from('pokemons').patch('a1b2c3d4-e5f6-4789-abcd-ef0123456789', {
|
|
351
|
+
level: 5
|
|
352
|
+
})
|
|
353
|
+
|
|
354
|
+
console.log(newPokemon);
|
|
355
|
+
// Output: {
|
|
356
|
+
// id: "a1b2c3d4-e5f6-4789-abcd-ef0123456789",
|
|
357
|
+
// name: "Pikachu",
|
|
358
|
+
// type: "electric",
|
|
359
|
+
// level: 5
|
|
360
|
+
}
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
### Delete an item
|
|
364
|
+
|
|
365
|
+
This operation will delete permanently an item from the database. This is an irreversible action. The deleted item is returned in the response.
|
|
366
|
+
|
|
367
|
+
=== "REST API"
|
|
368
|
+
**Request URL**: `DELETE /api/collections/:slug/:id`
|
|
369
|
+
|
|
370
|
+
```http title="Example HTTP Request"
|
|
371
|
+
DELETE api/collections/cats/550e8400-e29b-41d4-a716-446655440000
|
|
372
|
+
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
```json title="Example HTTP Response"
|
|
376
|
+
{
|
|
377
|
+
"name": "Fido",
|
|
378
|
+
"description": "A cute black cat"
|
|
379
|
+
}
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
=== "JS SDK"
|
|
383
|
+
```js title="Example SDK usage"
|
|
384
|
+
// Delete the cat with ID 550e8400-e29b-41d4-a716-446655440000
|
|
385
|
+
const deletedCat = await chadstart.from('cats').delete('550e8400-e29b-41d4-a716-446655440000')
|
|
386
|
+
|
|
387
|
+
console.log(deletedCat);
|
|
388
|
+
// Output: {
|
|
389
|
+
// name: "Fido",
|
|
390
|
+
// description: "A cute black cat"
|
|
391
|
+
// }
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
## Singles
|
|
395
|
+
|
|
396
|
+
The following operations can be done on [singles](./entities.md#singles) entities. As there is only a single item per entity, there is no list-related operations, and single items cannot be deleted as we always want to return an object, even if empty.
|
|
397
|
+
|
|
398
|
+
### Get a single item
|
|
399
|
+
|
|
400
|
+
=== "REST API"
|
|
401
|
+
**Request URL**: `GET /api/singles/:slug`
|
|
402
|
+
|
|
403
|
+
```http title="Example HTTP Request"
|
|
404
|
+
GET /api/singles/homepage
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
```json title="Example HTTP Response"
|
|
408
|
+
{
|
|
409
|
+
"title": "My title",
|
|
410
|
+
"description": "Welcome to my website!"
|
|
411
|
+
}
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
=== "JS SDK"
|
|
415
|
+
```js title="Example SDK usage"
|
|
416
|
+
// Get the homepage entity.
|
|
417
|
+
const homepage = await chadstart.single('homepage').get()
|
|
418
|
+
|
|
419
|
+
console.log(homepage);
|
|
420
|
+
// Output: {
|
|
421
|
+
// title: "My title",
|
|
422
|
+
// description: "Welcome to my website!"
|
|
423
|
+
// }
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
### Update an item
|
|
427
|
+
|
|
428
|
+
This operation will replace an existing item by the payload provided in the request. Unlike [partial updates](#patch-an-item_1), this operation will replace the whole item by the new one. Missing or empty properties will delete the previous ones.
|
|
429
|
+
=== "REST API"
|
|
430
|
+
**Request URL**: `PUT /api/singles/:slug`
|
|
431
|
+
|
|
432
|
+
```http title="Example HTTP Request"
|
|
433
|
+
PUT /api/singles/homepage
|
|
434
|
+
Content-Type: application/json
|
|
435
|
+
Body:
|
|
436
|
+
{
|
|
437
|
+
"title": "My new title",
|
|
438
|
+
"description": "My new description"
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
```json title="Example HTTP Response"
|
|
444
|
+
{
|
|
445
|
+
"title": "My new title",
|
|
446
|
+
"description": "My new description"
|
|
447
|
+
}
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
=== "JS SDK"
|
|
451
|
+
```js title="Example SDK usage"
|
|
452
|
+
// Update single entity.
|
|
453
|
+
const newHomepage = await chadstart.single('homepage').update({
|
|
454
|
+
title: 'My new title',
|
|
455
|
+
description: 'My new description'
|
|
456
|
+
})
|
|
457
|
+
|
|
458
|
+
console.log(newHomepage);
|
|
459
|
+
// Output: {
|
|
460
|
+
// title: "My new title",
|
|
461
|
+
// description: "My new description"
|
|
462
|
+
// }
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
### Patch an item
|
|
466
|
+
|
|
467
|
+
This operation will partially replace an existing item. Unlike [fully replacement](#update-an-item_1), this operation will only modify the properties provided in the payload and leave the other ones as they are.
|
|
468
|
+
|
|
469
|
+
=== "REST API"
|
|
470
|
+
**Request URL**: `PATCH /api/singles/:slug`
|
|
471
|
+
|
|
472
|
+
```http title="Example HTTP Request"
|
|
473
|
+
PATCH /api/singles/homepage
|
|
474
|
+
Content-Type: application/json
|
|
475
|
+
Body:
|
|
476
|
+
{
|
|
477
|
+
"title": "My new title"
|
|
478
|
+
}
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
```json title="Example HTTP Response"
|
|
482
|
+
{
|
|
483
|
+
"title": "My new title",
|
|
484
|
+
"description": "Welcome to my website!"
|
|
485
|
+
}
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
=== "JS SDK"
|
|
489
|
+
```js title="Example SDK usage"
|
|
490
|
+
// Update single entity partially.
|
|
491
|
+
const homepage = await chadstart.single('homepage').patch({
|
|
492
|
+
title: 'My new title'
|
|
493
|
+
})
|
|
494
|
+
console.log(homepage)
|
|
495
|
+
// Output: {
|
|
496
|
+
// title: "My new title",
|
|
497
|
+
// description: "Welcome to my website!"
|
|
498
|
+
// }
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
## Work with relations
|
|
502
|
+
|
|
503
|
+
If you added [relationships](./entities.md#relations) between your entities, you probably want to include them in the CRUD operations.
|
|
504
|
+
|
|
505
|
+
### Load relations
|
|
506
|
+
|
|
507
|
+
You can specify which relations you want to load with your entities in your query. **Eager relations** are loaded automatically.
|
|
508
|
+
|
|
509
|
+
=== "JS SDK"
|
|
510
|
+
```js
|
|
511
|
+
// Fetch entities with 2 relations.
|
|
512
|
+
const cities = await chadstart
|
|
513
|
+
.from('cities')
|
|
514
|
+
.with(['region', 'mayor'])
|
|
515
|
+
.find()
|
|
516
|
+
|
|
517
|
+
// Fetch nested relations.
|
|
518
|
+
const cities = await chadstart
|
|
519
|
+
.from('cities')
|
|
520
|
+
.with(['region', 'region.country', 'region.country.planet'])
|
|
521
|
+
.find()
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
=== "REST API"
|
|
525
|
+
```http
|
|
526
|
+
|
|
527
|
+
// Fetch entities with 2 relations.
|
|
528
|
+
GET http://localhost:3000/api/dynamic/city?relations=region,mayor
|
|
529
|
+
|
|
530
|
+
// Fetch nested relations.
|
|
531
|
+
GET http://localhost:3000/api/dynamic/city?relations=region,region.country,region.country.planet
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
### Filter by relation
|
|
535
|
+
|
|
536
|
+
Once the relation is loaded, you can also filter items by their relation id or properties.
|
|
537
|
+
|
|
538
|
+
=== "JS SDK"
|
|
539
|
+
```js
|
|
540
|
+
// Get all cats that belong to owner with id 3f2504e0-4f89-11d3-9a0c-0305e82c3301.
|
|
541
|
+
const cats = await chadstart
|
|
542
|
+
.from('cats')
|
|
543
|
+
.with(['owner'])
|
|
544
|
+
.where('owner.id = 3f2504e0-4f89-11d3-9a0c-0305e82c3301')
|
|
545
|
+
.find()
|
|
546
|
+
|
|
547
|
+
// Get all cats that have an owner with name "Jorge".
|
|
548
|
+
const cats = await chadstart
|
|
549
|
+
.from('cats')
|
|
550
|
+
.with(['owner'])
|
|
551
|
+
.where('owner.name = Jorge')
|
|
552
|
+
.find()
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
=== "REST API"
|
|
556
|
+
```http
|
|
557
|
+
// Get all cats that belong to owner with id 3f2504e0-4f89-11d3-9a0c-0305e82c3301.
|
|
558
|
+
GET http://localhost:3000/api/dynamic/cats?relations=owner&owner.id_eq=3f2504e0-4f89-11d3-9a0c-0305e82c3301
|
|
559
|
+
|
|
560
|
+
// Get all cats that have an owner with name "Jorge".
|
|
561
|
+
GET http://localhost:3000/api/dynamic/cats?relations=owner&owner.name_eq=Jorge
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
### Store relations
|
|
565
|
+
|
|
566
|
+
To store or update an item with its relations, you have to pass the relation id(s) as a property that end with **Id** for many-to-one and **Ids** for many-to-many like `companyId` or `tagIds`
|
|
567
|
+
|
|
568
|
+
=== "REST API"
|
|
569
|
+
```http
|
|
570
|
+
// Store a new player with relations Team and Skill.
|
|
571
|
+
POST http://localhost:3000/api/dynamic/players
|
|
572
|
+
Content-Type: application/json
|
|
573
|
+
{
|
|
574
|
+
"name": "Mike",
|
|
575
|
+
"teamId": 'e4d5c6b7-a890-4123-9876-543210fedcba',
|
|
576
|
+
"skillIds": ['12345678-1234-5678-9abc-123456789012', '3f2504e0-4f89-11d3-9a0c-0305e82c3301']
|
|
577
|
+
}
|
|
578
|
+
```
|
|
579
|
+
|
|
580
|
+
=== "JS SDK"
|
|
581
|
+
```js
|
|
582
|
+
// Store a new player with relations Team and Skill.
|
|
583
|
+
const newPlayer = await chadstart.from('players').create({
|
|
584
|
+
name: 'Mike',
|
|
585
|
+
teamId: 'e4d5c6b7-a890-4123-9876-543210fedcba',
|
|
586
|
+
skillIds: ['12345678-1234-5678-9abc-123456789012', '3f2504e0-4f89-11d3-9a0c-0305e82c3301']
|
|
587
|
+
})
|
|
588
|
+
```
|
|
589
|
+
|
|
590
|
+
!!! note
|
|
591
|
+
When storing **many-to-many** relations, you always need to **pass an array**, even if you just have one single value.
|
|
592
|
+
|
|
593
|
+
### Update relations
|
|
594
|
+
|
|
595
|
+
As for updating properties, you can either do a **full replacement** using the update function (PUT) or a **partial replacement** using the patch function (PATCH).
|
|
596
|
+
|
|
597
|
+
=== "REST API"
|
|
598
|
+
```http
|
|
599
|
+
// Replaces the whole skill relations by the new skillIds array.
|
|
600
|
+
PUT http://localhost:3000/api/dynamic/players/e4d5c6b7-a890-4123-9876-543210fedcba
|
|
601
|
+
Content-Type: application/json
|
|
602
|
+
{
|
|
603
|
+
name: 'Mike',
|
|
604
|
+
teamId: 'e4d5c6b7-a890-4123-9876-543210fedcba',
|
|
605
|
+
skillIds: ['12345678-1234-5678-9abc-123456789012', '3f2504e0-4f89-11d3-9a0c-0305e82c3301']
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
// Updates the team without changing the skills or the name.
|
|
609
|
+
PATCH http://localhost:3000/api/dynamic/players/e4d5c6b7-a890-4123-9876-543210fedcba
|
|
610
|
+
Content-Type: application/json
|
|
611
|
+
{
|
|
612
|
+
teamId: '9b2fff23-ec93-4b48-9322-bbd4b6b5b123',
|
|
613
|
+
}
|
|
614
|
+
```
|
|
615
|
+
|
|
616
|
+
=== "JS SDK"
|
|
617
|
+
```js
|
|
618
|
+
// Replaces the whole skill relations by the new skillIds array.
|
|
619
|
+
await chadstart.from('players').update('e4d5c6b7-a890-4123-9876-543210fedcba', {
|
|
620
|
+
name: 'Mike',
|
|
621
|
+
teamId: 'e4d5c6b7-a890-4123-9876-543210fedcba',
|
|
622
|
+
skillIds: ['12345678-1234-5678-9abc-123456789012', '3f2504e0-4f89-11d3-9a0c-0305e82c3301']
|
|
623
|
+
})
|
|
624
|
+
|
|
625
|
+
// Updates the team without changing the skills or the name.
|
|
626
|
+
await chadstart.from('players').patch('e4d5c6b7-a890-4123-9876-543210fedcba', {teamId: '9b2fff23-ec93-4b48-9322-bbd4b6b5b123'})
|
|
627
|
+
```
|