remult 0.23.0-exp.3 → 0.23.0-exp.5
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 +69 -49
- package/esm/src/entity.js +1 -0
- package/package.json +1 -1
- package/src/entity.js +1 -0
package/README.md
CHANGED
|
@@ -1,31 +1,48 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
<a href="http://remult.dev/">
|
|
3
|
-
<img
|
|
3
|
+
<img
|
|
4
|
+
src="https://github.com/remult/remult/raw/master/docs/public/logo.png"
|
|
5
|
+
width="140"
|
|
6
|
+
height="140"
|
|
7
|
+
/>
|
|
4
8
|
</a>
|
|
5
9
|
<h1>Remult</h1>
|
|
6
10
|
<p>Full-stack CRUD, simplified, with SSOT TypeScript entities</p>
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
11
|
+
<a href="https://circleci.com/gh/remult/remult/tree/master" rel="nofollow">
|
|
12
|
+
<img
|
|
13
|
+
alt="CircleCI"
|
|
14
|
+
src="https://circleci.com/gh/remult/remult/tree/master.svg?style=shield"
|
|
15
|
+
/>
|
|
16
|
+
</a>
|
|
17
|
+
<a href="https://codecov.io/gh/remult/remult" rel="nofollow">
|
|
18
|
+
<img
|
|
19
|
+
src="https://codecov.io/gh/remult/remult/branch/master/graph/badge.svg?token=LYWQRUN3D1"
|
|
20
|
+
/>
|
|
21
|
+
</a>
|
|
22
|
+
<a
|
|
23
|
+
href="https://raw.githubusercontent.com/remult/remult/master/LICENSE"
|
|
24
|
+
rel="nofollow"
|
|
25
|
+
>
|
|
26
|
+
<img
|
|
27
|
+
alt="GitHub license"
|
|
28
|
+
src="https://img.shields.io/badge/license-MIT-blue.svg"
|
|
29
|
+
/>
|
|
30
|
+
</a>
|
|
31
|
+
<a href="https://www.npmjs.com/package/remult" rel="nofollow">
|
|
32
|
+
<img alt="npm version" src="https://badge.fury.io/js/remult.svg" />
|
|
33
|
+
</a>
|
|
34
|
+
<a href="https://www.npmjs.com/package/remult" rel="nofollow">
|
|
35
|
+
<img alt="npm downloads" src="https://img.shields.io/npm/dm/remult" />
|
|
36
|
+
</a>
|
|
37
|
+
<a href="https://discord.gg/GXHk7ZfuG5" rel="nofollow">
|
|
38
|
+
<img
|
|
39
|
+
alt="Join Discord"
|
|
40
|
+
src="https://badgen.net/discord/online-members/GXHk7ZfuG5?icon=discord&label=Discord"
|
|
41
|
+
/>
|
|
25
42
|
</a>
|
|
26
43
|
</div>
|
|
27
44
|
|
|
28
|
-
<br/>
|
|
45
|
+
<br />
|
|
29
46
|
|
|
30
47
|
<div align="center">
|
|
31
48
|
<a href="#getting-started">Getting Started</a> |
|
|
@@ -33,12 +50,15 @@
|
|
|
33
50
|
<a href="#example-apps">Example Apps</a>
|
|
34
51
|
</div>
|
|
35
52
|
|
|
36
|
-
<hr/>
|
|
37
|
-
<br/>
|
|
53
|
+
<hr />
|
|
54
|
+
<br />
|
|
38
55
|
|
|
39
56
|
<a href="https://www.youtube.com/watch?v=rEoScmSVNUE" target="_blank">
|
|
40
57
|
<p align="center">
|
|
41
|
-
<img
|
|
58
|
+
<img
|
|
59
|
+
src="https://github.com/remult/remult/raw/master/static/images/video-thumbnail.jpg"
|
|
60
|
+
alt="Video thumbnail"
|
|
61
|
+
/>
|
|
42
62
|
</p>
|
|
43
63
|
</a>
|
|
44
64
|
|
|
@@ -101,14 +121,14 @@ npm i remult
|
|
|
101
121
|
```ts
|
|
102
122
|
// shared/product.ts
|
|
103
123
|
|
|
104
|
-
import { Entity, Fields } from
|
|
124
|
+
import { Entity, Fields } from 'remult'
|
|
105
125
|
|
|
106
|
-
@Entity(
|
|
107
|
-
allowApiCrud: true
|
|
126
|
+
@Entity('products', {
|
|
127
|
+
allowApiCrud: true,
|
|
108
128
|
})
|
|
109
129
|
export class Product {
|
|
110
130
|
@Fields.string()
|
|
111
|
-
name =
|
|
131
|
+
name = ''
|
|
112
132
|
|
|
113
133
|
@Fields.number()
|
|
114
134
|
unitPrice = 0
|
|
@@ -120,17 +140,17 @@ export class Product {
|
|
|
120
140
|
```ts
|
|
121
141
|
// backend/index.ts
|
|
122
142
|
|
|
123
|
-
import express from
|
|
124
|
-
import { remultExpress } from
|
|
125
|
-
import { Product } from
|
|
143
|
+
import express from 'express'
|
|
144
|
+
import { remultExpress } from 'remult/remult-express'
|
|
145
|
+
import { Product } from '../shared/product'
|
|
126
146
|
|
|
127
147
|
const port = 3001
|
|
128
148
|
const app = express()
|
|
129
149
|
|
|
130
150
|
app.use(
|
|
131
151
|
remultExpress({
|
|
132
|
-
entities: [Product]
|
|
133
|
-
})
|
|
152
|
+
entities: [Product],
|
|
153
|
+
}),
|
|
134
154
|
)
|
|
135
155
|
|
|
136
156
|
app.listen(port, () => {
|
|
@@ -151,13 +171,13 @@ app.listen(port, () => {
|
|
|
151
171
|
```ts
|
|
152
172
|
// frontend/code.ts
|
|
153
173
|
|
|
154
|
-
import { remult } from
|
|
155
|
-
import { Product } from
|
|
174
|
+
import { remult } from 'remult'
|
|
175
|
+
import { Product } from '../shared/product'
|
|
156
176
|
|
|
157
177
|
async function increasePriceOfTofu(priceIncrease: number) {
|
|
158
178
|
const productsRepo = remult.repo(Product)
|
|
159
179
|
|
|
160
|
-
const product = await productsRepo.findFirst({ name:
|
|
180
|
+
const product = await productsRepo.findFirst({ name: 'Tofu' }) // filter is passed through API request all the way to the db
|
|
161
181
|
product.unitPrice += priceIncrease
|
|
162
182
|
productsRepo.save(product) // mutation request updates the db with no boilerplate code
|
|
163
183
|
}
|
|
@@ -179,32 +199,32 @@ static async increasePriceOfTofu(priceIncrease: number) {
|
|
|
179
199
|
### :ballot_box_with_check: Data validation and constraints - defined once
|
|
180
200
|
|
|
181
201
|
```ts
|
|
182
|
-
import { Entity, Fields, Validators } from
|
|
202
|
+
import { Entity, Fields, Validators } from 'remult'
|
|
183
203
|
|
|
184
|
-
@Entity(
|
|
185
|
-
allowApiCrud: true
|
|
204
|
+
@Entity('products', {
|
|
205
|
+
allowApiCrud: true,
|
|
186
206
|
})
|
|
187
207
|
export class Product {
|
|
188
208
|
@Fields.string({
|
|
189
|
-
validate: Validators.required
|
|
209
|
+
validate: Validators.required,
|
|
190
210
|
})
|
|
191
|
-
name =
|
|
211
|
+
name = ''
|
|
192
212
|
|
|
193
213
|
@Fields.string<Product>({
|
|
194
214
|
validate: (product) => {
|
|
195
215
|
if (product.description.trim().length < 50) {
|
|
196
|
-
throw
|
|
216
|
+
throw 'too short'
|
|
197
217
|
}
|
|
198
|
-
}
|
|
218
|
+
},
|
|
199
219
|
})
|
|
200
|
-
description =
|
|
220
|
+
description = ''
|
|
201
221
|
|
|
202
222
|
@Fields.number({
|
|
203
223
|
validate: (_, field) => {
|
|
204
224
|
if (field.value < 0) {
|
|
205
|
-
field.error =
|
|
225
|
+
field.error = 'must not be less than 0' // or: throw "must not be less than 0";
|
|
206
226
|
}
|
|
207
|
-
}
|
|
227
|
+
},
|
|
208
228
|
})
|
|
209
229
|
unitPrice = 0
|
|
210
230
|
}
|
|
@@ -233,20 +253,20 @@ try {
|
|
|
233
253
|
### :lock: Secure the API with fine-grained authorization
|
|
234
254
|
|
|
235
255
|
```ts
|
|
236
|
-
@Entity<Article>(
|
|
256
|
+
@Entity<Article>('Articles', {
|
|
237
257
|
allowApiRead: true,
|
|
238
258
|
allowApiInsert: (_, remult) => remult.authenticated(),
|
|
239
|
-
allowApiUpdate: (article, remult) => article.author.id == remult.user.id
|
|
259
|
+
allowApiUpdate: (article, remult) => article.author.id == remult.user.id,
|
|
240
260
|
})
|
|
241
261
|
export class Article {
|
|
242
262
|
@Fields.string({ allowApiUpdate: false })
|
|
243
|
-
slug =
|
|
263
|
+
slug = ''
|
|
244
264
|
|
|
245
265
|
@Field(() => Profile, { allowApiUpdate: false })
|
|
246
266
|
author!: Profile
|
|
247
267
|
|
|
248
268
|
@Fields.string()
|
|
249
|
-
content =
|
|
269
|
+
content = ''
|
|
250
270
|
}
|
|
251
271
|
```
|
|
252
272
|
|
package/esm/src/entity.js
CHANGED
package/package.json
CHANGED
package/src/entity.js
CHANGED