beech-api 3.8.0 → 3.9.0-beta.9-rc
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 +551 -146
- package/package.json +13 -12
- package/packages/cli/bin/beech-app.js +4 -4
- package/packages/cli/bin/beech-service.js +62 -25
- package/packages/cli/core/auth/Credentials.js +115 -88
- package/packages/cli/core/auth/Passport.js +111 -39
- package/packages/cli/core/configure/passport.config.js +1 -1
- package/packages/cli/core/databases/mysql.js +1 -1
- package/packages/cli/core/databases/sequelize.js +15 -6
- package/packages/cli/core/databases/test.js +125 -39
- package/packages/cli/core/generator/_models +3 -26
- package/packages/cli/core/generator/_models_basic +0 -9
- package/packages/cli/core/generator/_package +6 -7
- package/packages/cli/core/generator/_scheduler +16 -6
- package/packages/cli/core/generator/index.js +277 -23
- package/packages/cli/core/helpers/2fa.js +22 -1
- package/packages/cli/core/helpers/math.js +14 -2
- package/packages/cli/core/helpers/poolEntity.js +70 -26
- package/packages/cli/core/index.js +88 -10
- package/packages/cli/core/middleware/express/duplicateRequest.js +10 -6
- package/packages/cli/core/middleware/express/jwtCheckAllow.js +52 -34
- package/packages/cli/core/middleware/express/rateLimit.js +14 -2
- package/packages/cli/core/middleware/origin/guard/advance.js +5 -4
- package/packages/cli/core/services/http.express.js +49 -9
- package/packages/cli/core/test/check-node.js +21 -0
- package/packages/lib/src/endpoint.js +639 -286
- package/packages/lib/src/schema.js +4 -1
package/README.md
CHANGED
|
@@ -1,22 +1,74 @@
|
|
|
1
1
|
[](https://github.com/bombkiml)
|
|
2
2
|
|
|
3
3
|
# Beech API framework
|
|
4
|
-
####
|
|
4
|
+
#### Auto endpoint v.3.9.0 (LTS)
|
|
5
5
|
|
|
6
|
-
[](https://github.com/bombkiml/beech-api/releases/
|
|
6
|
+
[](https://github.com/bombkiml/beech-api/releases/)
|
|
7
7
|
[](https://github.com/bombkiml/beech-api/blob/master/README.md)
|
|
8
8
|
|
|
9
9
|
# What is Beech API ?
|
|
10
10
|
|
|
11
11
|
The Beech API is API framework, It's help you with very easy to create API project under [Node.js](https://nodejs.org)
|
|
12
12
|
|
|
13
|
+
## Let's go
|
|
14
|
+
- <b>[Installation](#installation)</b>
|
|
15
|
+
- <b>[Creating a project](#creating-a-project)</b>
|
|
16
|
+
- <b>[Upgrade to latest version](#upgrade-to-latest-version)</b>
|
|
17
|
+
- <b>[Beech CLI tool available](#beech-cli-tool-available)</b>
|
|
18
|
+
- ✨ <b>Automation Endpoints with CRUD</b>
|
|
19
|
+
- [Database Connection](#database-connection)
|
|
20
|
+
- [Model](#models)
|
|
21
|
+
- [Retrieving data with Query String](#retrieving-data-with-query-string)
|
|
22
|
+
- Conditions
|
|
23
|
+
- Grouping
|
|
24
|
+
- Ordering
|
|
25
|
+
- Timestamps (Add-on in Store and Update)
|
|
26
|
+
- [Transactions](#-transactions)
|
|
27
|
+
- [Disorganized transactions](#way-1---disorganized-transactions-)
|
|
28
|
+
- [Organized transactions](#way-2---organized-transactions-)
|
|
29
|
+
- [Transactions set Isolation levels](#way-3---transactions-set-isolation-levels-)
|
|
30
|
+
- [Endpoints](#endpoints)
|
|
31
|
+
- [Helpers](#helpers)
|
|
32
|
+
- 🔐 <b>System Management of Authentication</b>
|
|
33
|
+
- [Authentication Manegement](#authentication-passport-jwt)
|
|
34
|
+
- [Request Token](#request-token)
|
|
35
|
+
- [Beech Guard](#beech-guard)
|
|
36
|
+
- Verify Identity Management
|
|
37
|
+
- Two Factor (2fa, OTP, etc.)
|
|
38
|
+
- [Beech User Authentication Managements](#-beech-user-authentication-managements)
|
|
39
|
+
- Create Auth
|
|
40
|
+
- Update Auth
|
|
41
|
+
- 🛠️ <b>Safe Endpoints Request</b>
|
|
42
|
+
- [Rate Limit](#-custom-endpoint-specific-rate-limit)
|
|
43
|
+
- [Slow Down](#-custom-endpoint-specific-slow-down)
|
|
44
|
+
- [Block Duplicate Request per Window](#-custom-endpoint-specific-duplicate-request)
|
|
45
|
+
- [JWT Broken Role](#jwt-broken-role)
|
|
46
|
+
- [Advance Guard (Timimg)](#-beech-advanced-guard-timing)
|
|
47
|
+
- 🙂 <b>Hight Security under passport-jwt, oauth2</b>
|
|
48
|
+
- 🌐 <b>Supported Official Strategy</b>
|
|
49
|
+
- [Google](#-google-strategy)
|
|
50
|
+
- [Facebook](#-facebook-strategy)
|
|
51
|
+
- 🖥️ <b>CORS Origin & Server Configuration</b>
|
|
52
|
+
- [Config Base public path `./`](#cors-origin--server-configuration)
|
|
53
|
+
- [Allow origin whitelist](#cors-origin--server-configuration)
|
|
54
|
+
- 📚 <b>Databases Managements</b>
|
|
55
|
+
- [Migrations](#databases-managements)
|
|
56
|
+
- [Seeder](#-creating-first-seeder)
|
|
57
|
+
- ☕ <b>Testing</b>
|
|
58
|
+
- [Jest](#testing)
|
|
59
|
+
- 🏃 <b>Implementration</b>
|
|
60
|
+
- [Docker](#implementation)
|
|
61
|
+
- [PM2](#-implement-with-pm2)
|
|
62
|
+
|
|
13
63
|
# Environment
|
|
14
64
|
|
|
15
|
-
- [`Node.js`](https://nodejs.org) >=
|
|
65
|
+
- [`Node.js`](https://nodejs.org) >= 18.17.1+ (recommended)
|
|
16
66
|
|
|
17
67
|
# Installation
|
|
18
68
|
|
|
19
|
-
Beech API
|
|
69
|
+
Beech API needed Node.js version 18.17.1 or above. You can management multiple versions on the same machine with [nvm](https://github.com/creationix/nvm) or [nvm-windows](https://github.com/coreybutler/nvm-windows).
|
|
70
|
+
|
|
71
|
+
<b>So, Let's go to install</b> `beech-api`
|
|
20
72
|
|
|
21
73
|
```sh
|
|
22
74
|
# NPM
|
|
@@ -28,8 +80,11 @@ $ yarn global add beech-api
|
|
|
28
80
|
|
|
29
81
|
Installation demo:
|
|
30
82
|
|
|
31
|
-
[Demo](https://i.ibb.co/
|
|
32
|
-

|
|
84
|
+

|
|
85
|
+
|
|
86
|
+
[Demo 2](https://i.ibb.co/wrdgyzDP/ezgif-8539024298ec62a9.gif)
|
|
87
|
+

|
|
33
88
|
|
|
34
89
|
After installation, you will have access to the `beech-app` binary in your command line.
|
|
35
90
|
You can check you have the right version with this command:
|
|
@@ -98,7 +153,9 @@ The following commands are available:
|
|
|
98
153
|
$ beech key:generate, key:gen Re-Generate application key (Dangerous!).
|
|
99
154
|
$ beech hash:<text> Hash text for Access to Database connection.
|
|
100
155
|
```
|
|
101
|
-
❓ **Note:** Every to create new project will be generated new ``app_key`` in ``app.config.js`` file
|
|
156
|
+
❓ **Note:** Every to create new project will be generated new ``app_key`` in ``app.config.js`` file.
|
|
157
|
+
|
|
158
|
+
❓ **Note:** If you can re-generate. Can use command ``$ beech key:generate`` or ``$ beech key:gen``
|
|
102
159
|
|
|
103
160
|
# Database connection
|
|
104
161
|
|
|
@@ -121,11 +178,11 @@ $ beech hash:password
|
|
|
121
178
|
Output: FjgcgJPylkV7EeQJjea_EeifPwaHVO9onD3ATk3YYAyvjtMGu3dcDS0ejA
|
|
122
179
|
|
|
123
180
|
```
|
|
124
|
-
Example
|
|
181
|
+
***For Example :***
|
|
125
182
|
|
|
126
183
|
📂 app.config.js
|
|
127
184
|
```js
|
|
128
|
-
//
|
|
185
|
+
// Database configuration Only Basic & Sequelize (needed Hash)
|
|
129
186
|
|
|
130
187
|
...
|
|
131
188
|
|
|
@@ -150,9 +207,9 @@ database_config: [
|
|
|
150
207
|
// or use a named timezone supported by Intl.Locale
|
|
151
208
|
// timezone: 'America/Los_Angeles',
|
|
152
209
|
|
|
153
|
-
logging: console.log, // SQL trace logs
|
|
210
|
+
logging: console.log, // SQL trace logs. Learn more: https://sequelize.org/docs/v6/getting-started/#logging
|
|
154
211
|
|
|
155
|
-
is_connect: true, //
|
|
212
|
+
is_connect: true, // Boolean, Turn ON/OFF to connect
|
|
156
213
|
},
|
|
157
214
|
|
|
158
215
|
...
|
|
@@ -163,113 +220,26 @@ database_config: [
|
|
|
163
220
|
```
|
|
164
221
|
❓ **Caution! :** Every re-new generate `app_key`. Must to new Hash your Access and change to ALL Database connections.
|
|
165
222
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
## # Generate Endpoints
|
|
223
|
+
❓ **Development** : You can add ```dev: true``` in ```main_config``` for a better experience.
|
|
169
224
|
|
|
170
|
-
|
|
225
|
+
# Models
|
|
171
226
|
|
|
172
|
-
|
|
227
|
+
The `models` keep the files of function(s) data managemnets for Retriving, Creating, Updating and Destroying (CRUD). for understanding you might make model name same your table name inside `src/models` folder.
|
|
173
228
|
|
|
174
229
|
```sh
|
|
175
|
-
$ beech make
|
|
176
|
-
```
|
|
177
|
-
You might using [special] `-R, --require` for choose Model(s) used for that endpoint.
|
|
178
|
-
|
|
179
|
-
### Example ***(Basic)*** : Fruit endpoints.
|
|
180
|
-
|
|
181
|
-
📂 fruit-endpoints.js
|
|
182
|
-
```js
|
|
183
|
-
exports.init = () => {
|
|
184
|
-
|
|
185
|
-
// GET method
|
|
186
|
-
endpoint.get("/fruit", Credentials, (req, res) => {
|
|
187
|
-
// @response
|
|
188
|
-
res.json({
|
|
189
|
-
code: 200,
|
|
190
|
-
status: "SUCCESS",
|
|
191
|
-
message: "GET /fruit request.",
|
|
192
|
-
});
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
// POST method
|
|
197
|
-
endpoint.post("/fruit", Credentials, (req, res) => {
|
|
198
|
-
// @response
|
|
199
|
-
res.json({
|
|
200
|
-
code: 200,
|
|
201
|
-
status: "SUCCESS",
|
|
202
|
-
message: "POST request at /fruit",
|
|
203
|
-
result: {
|
|
204
|
-
id: req.body.id,
|
|
205
|
-
name: req.body.name,
|
|
206
|
-
},
|
|
207
|
-
});
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
// PUT method
|
|
212
|
-
endpoint.put("/fruit/:id", Credentials, (req, res) => {
|
|
213
|
-
// @response
|
|
214
|
-
res.json({
|
|
215
|
-
code: 200,
|
|
216
|
-
status: "SUCCESS",
|
|
217
|
-
message: "PUT request at /fruit/" + req.params.id,
|
|
218
|
-
});
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
// DELETE method
|
|
223
|
-
endpoint.delete("/fruit/:id", Credentials, (req, res) => {
|
|
224
|
-
// @response
|
|
225
|
-
res.json({
|
|
226
|
-
code: 200,
|
|
227
|
-
status: "SUCCESS",
|
|
228
|
-
message: "DELETE request at /fruit/" + req.params.id,
|
|
229
|
-
});
|
|
230
|
-
});
|
|
231
|
-
|
|
232
|
-
...
|
|
233
|
-
|
|
234
|
-
}
|
|
235
|
-
```
|
|
236
|
-
|
|
237
|
-
### Example ***(Sequelize)*** : Fruit endpoints.
|
|
238
|
-
|
|
239
|
-
📂 fruit-endpoints.js
|
|
240
|
-
```js
|
|
241
|
-
// Require Model schema, Function & Others
|
|
242
|
-
const { Fruit } = require("@/models/Fruit");
|
|
243
|
-
|
|
244
|
-
exports.init = () => {
|
|
245
|
-
|
|
246
|
-
// GET method
|
|
247
|
-
endpoint.get('/fruit', async (req, res) => {
|
|
248
|
-
// example call Fruit model for get data
|
|
249
|
-
res.json({
|
|
250
|
-
code: 200,
|
|
251
|
-
status: "SUCCESS",
|
|
252
|
-
results: await Fruit.findAll();
|
|
253
|
-
});
|
|
254
|
-
});
|
|
255
|
-
|
|
256
|
-
...
|
|
257
|
-
|
|
258
|
-
}
|
|
230
|
+
$ beech make modelName --model
|
|
259
231
|
```
|
|
260
232
|
|
|
233
|
+
## # Model (Basic)
|
|
261
234
|
|
|
262
|
-
|
|
235
|
+
Basic model only support `MySQL` Raw Query format and freedom of your SQL query
|
|
263
236
|
|
|
264
|
-
The
|
|
237
|
+
❓ **Note:** The Basic pool engine it's not support auto Endpoints.
|
|
265
238
|
|
|
266
|
-
```sh
|
|
267
|
-
$ beech make modelName --model
|
|
268
|
-
```
|
|
269
239
|
|
|
270
|
-
|
|
240
|
+
***For example :***
|
|
271
241
|
|
|
272
|
-
📂 Fruit.js
|
|
242
|
+
📂 models/Fruit.js
|
|
273
243
|
```js
|
|
274
244
|
module.exports = {
|
|
275
245
|
|
|
@@ -299,11 +269,17 @@ module.exports = {
|
|
|
299
269
|
};
|
|
300
270
|
```
|
|
301
271
|
|
|
302
|
-
|
|
272
|
+
## # Model (Sequelize)
|
|
303
273
|
|
|
274
|
+
Sequelize is a promise-based Node.js ORM tool for Postgres, MySQL, MariaDB, SQLite, Microsoft SQL Server, Oracle Database, Amazon Redshift and Snowflake’s Data Cloud. It features solid transaction support, relations, eager and lazy loading, read replication and more. <br/>You can learn more: [Sequelize docs](https://sequelize.org/docs/v6)
|
|
275
|
+
|
|
304
276
|
You can asign more DataTypes, Learn more : [Sequelize docs](https://sequelize.org/docs/v6/core-concepts/model-basics/#data-types)
|
|
305
277
|
|
|
306
|
-
|
|
278
|
+
❓ **Note:** When you generate a model it's create table structure for automatically for you.
|
|
279
|
+
|
|
280
|
+
***For example :***
|
|
281
|
+
|
|
282
|
+
📂 models/Fruit.js
|
|
307
283
|
```js
|
|
308
284
|
const { Schema } = require("beech-api");
|
|
309
285
|
|
|
@@ -327,6 +303,7 @@ const Fruit = Schema(sql.default_db).define("fruit", {
|
|
|
327
303
|
type: DataTypes.INTEGER,
|
|
328
304
|
allowNull: false, // Allow null feilds
|
|
329
305
|
},
|
|
306
|
+
sort: DataTypes.STRING,
|
|
330
307
|
createdAt: {
|
|
331
308
|
type: DataTypes.DATE,
|
|
332
309
|
allowNull: false,
|
|
@@ -350,11 +327,10 @@ Fruit.options = {
|
|
|
350
327
|
// Option 1: Allow all methods
|
|
351
328
|
defaultEndpoint: true,
|
|
352
329
|
|
|
353
|
-
|
|
330
|
+
// Option 2: Allow with specific per methods
|
|
354
331
|
defaultEndpoint: {
|
|
355
332
|
GET: true,
|
|
356
|
-
POST:
|
|
357
|
-
PATCH: {
|
|
333
|
+
POST: {
|
|
358
334
|
allow: true, // allow Auto-Endpoint
|
|
359
335
|
jwt: {
|
|
360
336
|
allow: true, // allow JWT
|
|
@@ -362,12 +338,23 @@ Fruit.options = {
|
|
|
362
338
|
{ role: [1, 2] },
|
|
363
339
|
],
|
|
364
340
|
},
|
|
341
|
+
// Rate Limit for POST
|
|
342
|
+
rate_limit: {
|
|
343
|
+
windowMs: 15 * 60 * 1000, // 15 minutes
|
|
344
|
+
limit: 100, // Limit each IP to 100 requests per `window` (here, per 15 minutes).
|
|
345
|
+
// store: ... , // Redis, Memcached, etc.
|
|
346
|
+
// See more: https://www.npmjs.com/package/express-rate-limit#Configuration
|
|
347
|
+
},
|
|
348
|
+
// Duplicate Request for POST
|
|
349
|
+
duplicate_request: {
|
|
350
|
+
expiration: 500, // Can't duplicate request for 5 milliseconds each IP requests per `window` (Disabled for Set expiration to 0 zero.)
|
|
351
|
+
},
|
|
365
352
|
},
|
|
353
|
+
PATCH: false,
|
|
366
354
|
DELETE: false,
|
|
367
355
|
},
|
|
368
356
|
|
|
369
357
|
limitRows: 100, // Limit rows default 100
|
|
370
|
-
|
|
371
358
|
};
|
|
372
359
|
|
|
373
360
|
// Example Finder by id (ORM), Learn more: https://sequelize.org/docs/v6/core-concepts/model-querying-finders/
|
|
@@ -375,7 +362,6 @@ function exampleFindOneFruitById(id) {
|
|
|
375
362
|
return Fruit.findOne({ where: { id: id } });
|
|
376
363
|
}
|
|
377
364
|
|
|
378
|
-
|
|
379
365
|
// Example Raw Query with Model Instances. This allows you to easily map a query to a predefined model
|
|
380
366
|
function exampleGetAllFruitWithModelInstance(id) {
|
|
381
367
|
return Fruit.query("SELECT * FROM fruit", {
|
|
@@ -400,18 +386,98 @@ module.exports = {
|
|
|
400
386
|
...
|
|
401
387
|
};
|
|
402
388
|
```
|
|
403
|
-
#### That's cool! It's like magic creating The endpoints for you (CRUD) ✨
|
|
404
389
|
|
|
405
|
-
|
|
390
|
+
## Retrieving data with Query String
|
|
391
|
+
|
|
392
|
+
Now you can add Query String with Conditional, Grouping and Ordering (Now Support Readonly for GET method)
|
|
393
|
+
|
|
394
|
+
### ✨ That's cool! It's like magic Creating The Endpoints for you (CRUD) ✨
|
|
395
|
+
|
|
396
|
+
<b style="font-size:12pt">For Example, Now!</b>, You can request to `/fruit` with methods GET, POST, PATCH and DELETE like this.
|
|
406
397
|
|
|
407
|
-
| Efficacy | Method | Endpoint | Body |
|
|
408
|
-
|
|
409
|
-
| Create | POST | /fruit | { }
|
|
410
|
-
|
|
|
411
|
-
| Read | GET | /fruit
|
|
412
|
-
| Read | GET | /fruit/:limit/:offset | No
|
|
413
|
-
|
|
|
414
|
-
|
|
|
398
|
+
| Efficacy | Method | Endpoint | Body | Mode |
|
|
399
|
+
|:---------|:---------|:-----------------------|:-------------|:--------|
|
|
400
|
+
| Create | POST | /fruit | { } | Single |
|
|
401
|
+
| Create | POST | /fruit | [ ] | Bulk |
|
|
402
|
+
| Read | GET | /fruit | No | Fetch |
|
|
403
|
+
| Read | GET | /fruit/:limit/:offset | No | Fetch |
|
|
404
|
+
| Read | GET | /fruit?someField=1 | No | Fetch |
|
|
405
|
+
| Read | GET | /fruit?orderby=sort | No | Fetch |
|
|
406
|
+
| Read | GET | /fruit?groupby=id | No | Fetch |
|
|
407
|
+
| Update | PATCH | /fruit/:id | { } | Single |
|
|
408
|
+
| Delete | DELETE | /fruit/:id | No | Single |
|
|
409
|
+
|
|
410
|
+
Add some Basic Conditions, Grouping and Ordering with `QUERY STRING` under GET methods<br/>
|
|
411
|
+
|
|
412
|
+
Retrieving `fruit` data with GET : `/fruit?someField=[eq,1]&groupby=[id]&orderby=[id,desc]`
|
|
413
|
+
|
|
414
|
+
***For Example :***
|
|
415
|
+
|
|
416
|
+
```java
|
|
417
|
+
// WHERE Conditions
|
|
418
|
+
GET: /fruit?id=1 // id = 1
|
|
419
|
+
GET: /fruit?isActived=[eq,1] // isActived = 1
|
|
420
|
+
GET: /fruit?fruitName=[like,Banana%] // fruitName LIKE 'Banana%' (Not allow with date, time)
|
|
421
|
+
GET: /fruit?cost=[gt,50]&qty=[lt,10] // cost > 50 AND qty < 10
|
|
422
|
+
GET: /fruit/10/0?qty=[lt,10] // qty < 10 LIMIT 0,10
|
|
423
|
+
|
|
424
|
+
// Grouping
|
|
425
|
+
GET: /fruit?groupby=id // GROUP BY id
|
|
426
|
+
GET: /fruit?groupby=[id,fruitName] // GROUP BY id, fruitName
|
|
427
|
+
|
|
428
|
+
// Ordering
|
|
429
|
+
GET: /fruit?oderby=id // ORDER BY id ASC
|
|
430
|
+
GET: /fruit?oderby=[sort,desc] // ORDER BY sort DESC
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
For usage avariable:
|
|
434
|
+
|
|
435
|
+
```java
|
|
436
|
+
// Basics conditions
|
|
437
|
+
3 // = 3
|
|
438
|
+
[eq, 3] // = 3
|
|
439
|
+
[ne, 20] // != 20
|
|
440
|
+
[is, null] // IS NULL
|
|
441
|
+
[not, null] // IS NOT NULL
|
|
442
|
+
[or, [5, 6]] // (someField = 5) OR (someField = 6) // Not support NULL value
|
|
443
|
+
|
|
444
|
+
// Number comparisons conditions
|
|
445
|
+
[gt, 6] // > 6
|
|
446
|
+
[gte, 6] // >= 6
|
|
447
|
+
[lt, 10] // < 10
|
|
448
|
+
[lte, 10] // <= 10
|
|
449
|
+
[between, [6, 10]] // BETWEEN 6 AND 10
|
|
450
|
+
[between, [2025-01-01, 2025-04-30]] // BETWEEN '2025-01-01 00:00:00' AND '2025-04-30 23:59:59'
|
|
451
|
+
// When assign Datetime format will only support field datatype is `Date`, `Datetime`, `Time`
|
|
452
|
+
|
|
453
|
+
// OR You can assign Datetime like this.
|
|
454
|
+
[between, [2025-01-01 12:00:00, 2025-04-30 15:00:00]] // BETWEEN '2025-01-01 12:00:00' AND '2025-04-30 15:00:00'
|
|
455
|
+
|
|
456
|
+
[notBetween, [11, 15]] // NOT BETWEEN 11 AND 15
|
|
457
|
+
|
|
458
|
+
// Other operators conditions
|
|
459
|
+
[in, [1, 2, 3]], // IN [1, 2, 3]
|
|
460
|
+
[notIn, [1, 2, 3]], // NOT IN [1, 2, 3]
|
|
461
|
+
[like, %hat] // LIKE '%hat' (Avoid use #, % and %<Number> between wording)
|
|
462
|
+
// Becuase URL will be decoded it, Reccommand use startsWith, endsWith and substring when you need assing Number value)
|
|
463
|
+
// And NOT SUPPORT field datatype is `date`, `datetime`, `time`. Should be use with datatype is `String` or `Number` it work!.
|
|
464
|
+
|
|
465
|
+
[notLike, %hat] // NOT LIKE '%hat'
|
|
466
|
+
[startsWith, hat] // LIKE 'hat%'
|
|
467
|
+
[endsWith, hat] // LIKE '%hat'
|
|
468
|
+
[substring, hat] // LIKE '%hat%'
|
|
469
|
+
|
|
470
|
+
// Grouping
|
|
471
|
+
groupby=id // GROUP BY id
|
|
472
|
+
groupby=[id] // ORDER BY id
|
|
473
|
+
groupby=[id, fruitName] // ORDER BY id, fruitName
|
|
474
|
+
|
|
475
|
+
// Ordering
|
|
476
|
+
oderby=id // ORDER BY id ASC (Basic usage default Ascending)
|
|
477
|
+
oderby=[id, asc] // ORDER BY id ASC
|
|
478
|
+
oderby=[id, desc] // ORDER BY id ASC
|
|
479
|
+
oderby=[[id, desc], [sort, asc]] // ORDER BY id DESC, sort ASC
|
|
480
|
+
```
|
|
415
481
|
|
|
416
482
|
## # Transactions
|
|
417
483
|
|
|
@@ -500,7 +566,306 @@ Fruit.transaction(
|
|
|
500
566
|
});
|
|
501
567
|
```
|
|
502
568
|
|
|
503
|
-
|
|
569
|
+
# Endpoints
|
|
570
|
+
|
|
571
|
+
The `endpoints` keep the endpoints basic request files currently support `GET`, `POST`, `PUT`, `PATCH` and `DELETE`.
|
|
572
|
+
|
|
573
|
+
So, you might create new endpoints with constant `endpoint` object variable in `src/endpoints/` folder and file neme must be end with `-endpoints.js`
|
|
574
|
+
|
|
575
|
+
```sh
|
|
576
|
+
$ beech make endpointName
|
|
577
|
+
```
|
|
578
|
+
You might using [special] `-R, --require` for choose Model(s) used for that endpoint.
|
|
579
|
+
|
|
580
|
+
***For Example :***
|
|
581
|
+
|
|
582
|
+
📂 endpoints/fruit-endpoints.js
|
|
583
|
+
```js
|
|
584
|
+
// Require Model schema, Function & Others
|
|
585
|
+
const { Fruit } = require("@/models/Fruit");
|
|
586
|
+
|
|
587
|
+
exports.init = () => {
|
|
588
|
+
|
|
589
|
+
// GET method
|
|
590
|
+
endpoint.get("/fruit", Credentials, async (req, res) => {
|
|
591
|
+
// example call Fruit model for get data
|
|
592
|
+
res.json({
|
|
593
|
+
code: 200,
|
|
594
|
+
status: "SUCCESS",
|
|
595
|
+
results: await Fruit.findAll();
|
|
596
|
+
});
|
|
597
|
+
});
|
|
598
|
+
|
|
599
|
+
|
|
600
|
+
// POST method
|
|
601
|
+
endpoint.post("/fruit", Credentials, (req, res) => {
|
|
602
|
+
// @response
|
|
603
|
+
res.json({
|
|
604
|
+
code: 200,
|
|
605
|
+
status: "SUCCESS",
|
|
606
|
+
message: "POST request at /fruit",
|
|
607
|
+
result: {
|
|
608
|
+
id: req.body.id,
|
|
609
|
+
name: req.body.name,
|
|
610
|
+
},
|
|
611
|
+
});
|
|
612
|
+
});
|
|
613
|
+
|
|
614
|
+
|
|
615
|
+
// PUT method
|
|
616
|
+
endpoint.put("/fruit/:id", Credentials, (req, res) => {
|
|
617
|
+
// @response
|
|
618
|
+
res.json({
|
|
619
|
+
code: 200,
|
|
620
|
+
status: "SUCCESS",
|
|
621
|
+
message: "PUT request at /fruit/" + req.params.id,
|
|
622
|
+
});
|
|
623
|
+
});
|
|
624
|
+
|
|
625
|
+
|
|
626
|
+
// DELETE method
|
|
627
|
+
endpoint.delete("/fruit/:id", Credentials, (req, res) => {
|
|
628
|
+
// @response
|
|
629
|
+
res.json({
|
|
630
|
+
code: 200,
|
|
631
|
+
status: "SUCCESS",
|
|
632
|
+
message: "DELETE request at /fruit/" + req.params.id,
|
|
633
|
+
});
|
|
634
|
+
});
|
|
635
|
+
|
|
636
|
+
...
|
|
637
|
+
|
|
638
|
+
}
|
|
639
|
+
```
|
|
640
|
+
|
|
641
|
+
# JWT Broken Role
|
|
642
|
+
|
|
643
|
+
The **JWT Broken Role** mechanism provides a flexible way to bypass or override role-based authorization rules when using JWT.
|
|
644
|
+
|
|
645
|
+
#### It can be applied in three different levels, depending on your use case.
|
|
646
|
+
|
|
647
|
+
| Priority | Level | Scope | Best For |
|
|
648
|
+
|:---------|:--------------------------------|:-------------------------|:--------------------------------|
|
|
649
|
+
| 3rd | Global (``passport.config.js``) | All endpoints | Common authorization exceptions |
|
|
650
|
+
| 2nd | Model-level options | Per model & HTTP method | Structured, reusable rules |
|
|
651
|
+
| 1st | Endpoint middleware | Single endpoint | Custom or special cases |
|
|
652
|
+
|
|
653
|
+
This multi-layer approach allows you to design **secure, flexible, and maintainable JWT authorization flows**.
|
|
654
|
+
|
|
655
|
+
### 1. Global Configuration (passport.config.js) - Priority 3
|
|
656
|
+
|
|
657
|
+
You can define global JWT Broken Role rules that apply to all endpoints by configuring them in ``passport.config.js``.
|
|
658
|
+
```js
|
|
659
|
+
module.exports = {
|
|
660
|
+
// Enable JWT authentication
|
|
661
|
+
jwt_allow: true,
|
|
662
|
+
|
|
663
|
+
...
|
|
664
|
+
|
|
665
|
+
// Global JWT broken role configuration
|
|
666
|
+
jwt_broken_role: [
|
|
667
|
+
{
|
|
668
|
+
role: [0, 2, 4],
|
|
669
|
+
email: "john.doe@company.com",
|
|
670
|
+
}, // Basic role matching
|
|
671
|
+
|
|
672
|
+
{
|
|
673
|
+
role: { $in: [0, 2, 4] },
|
|
674
|
+
email: { $regex: /@company\.com$/ },
|
|
675
|
+
}, // Advanced matching using operators
|
|
676
|
+
],
|
|
677
|
+
|
|
678
|
+
...
|
|
679
|
+
};
|
|
680
|
+
```
|
|
681
|
+
|
|
682
|
+
**Use cases**
|
|
683
|
+
|
|
684
|
+
- Apply common authorization exceptions across the entire application
|
|
685
|
+
- Support both simple role arrays and advanced operators (``$in``, ``$regex``, etc.)
|
|
686
|
+
|
|
687
|
+
### 2. Model-Level Configuration (Per HTTP Method) - Priority 2
|
|
688
|
+
|
|
689
|
+
You can configure JWT and Broken Role rules per model and per HTTP method using model options.
|
|
690
|
+
|
|
691
|
+
📂 src/models/Fruit.js
|
|
692
|
+
```js
|
|
693
|
+
Fruit.options = {
|
|
694
|
+
...
|
|
695
|
+
|
|
696
|
+
// Auto-endpoint configuration by HTTP method
|
|
697
|
+
defaultEndpoint: {
|
|
698
|
+
GET: true, // Enable auto-generated GET endpoint
|
|
699
|
+
|
|
700
|
+
POST: {
|
|
701
|
+
allow: true,
|
|
702
|
+
jwt: {
|
|
703
|
+
allow: true,
|
|
704
|
+
broken_role: [
|
|
705
|
+
{ role: [5, 6] },
|
|
706
|
+
],
|
|
707
|
+
},
|
|
708
|
+
},
|
|
709
|
+
|
|
710
|
+
PATCH: {
|
|
711
|
+
allow: true,
|
|
712
|
+
jwt: {
|
|
713
|
+
allow: true,
|
|
714
|
+
broken_role: [
|
|
715
|
+
{ role: [5, 6, 9] },
|
|
716
|
+
{ department: "IT" },
|
|
717
|
+
],
|
|
718
|
+
},
|
|
719
|
+
},
|
|
720
|
+
|
|
721
|
+
DELETE: false, // Disable auto-generated DELETE endpoint
|
|
722
|
+
},
|
|
723
|
+
|
|
724
|
+
...
|
|
725
|
+
};
|
|
726
|
+
```
|
|
727
|
+
|
|
728
|
+
**Use cases**
|
|
729
|
+
|
|
730
|
+
- Fine-grained access control per HTTP method
|
|
731
|
+
- Different role requirements for POST, PATCH, etc.
|
|
732
|
+
- Combine role-based and attribute-based conditions
|
|
733
|
+
|
|
734
|
+
### 3. Endpoint-Level Configuration (Credentials Middleware) - Priority 1
|
|
735
|
+
|
|
736
|
+
For maximum flexibility, you can define Broken Role rules directly on a specific endpoint using the Credentials middleware.
|
|
737
|
+
|
|
738
|
+
📂 endpoints/custom-fruit-endpoints.js
|
|
739
|
+
```js
|
|
740
|
+
const { Fruit } = require("@/models/Fruit");
|
|
741
|
+
|
|
742
|
+
endpoint.delete(
|
|
743
|
+
"/destroy-fruit-by-id/:id",
|
|
744
|
+
Credentials([{ role: [5, 9] }]),
|
|
745
|
+
async (req, res) => {
|
|
746
|
+
|
|
747
|
+
// Only JWT tokens with role level 5 or 9 are allowed
|
|
748
|
+
|
|
749
|
+
const deleted = await Fruit.destroy({
|
|
750
|
+
where: {
|
|
751
|
+
id: req.params.id,
|
|
752
|
+
},
|
|
753
|
+
});
|
|
754
|
+
|
|
755
|
+
console.log("result:", deleted);
|
|
756
|
+
|
|
757
|
+
// @response
|
|
758
|
+
}
|
|
759
|
+
);
|
|
760
|
+
```
|
|
761
|
+
|
|
762
|
+
**Use cases**
|
|
763
|
+
|
|
764
|
+
- One-off or custom endpoints
|
|
765
|
+
- Highly specific authorization rules
|
|
766
|
+
- Override global or model-level behavior when necessary
|
|
767
|
+
|
|
768
|
+
## Supported Basic & Operators Usage
|
|
769
|
+
|
|
770
|
+
The JWT Broken Role system supports both basic value matching and advanced operators for flexible authorization rules.
|
|
771
|
+
|
|
772
|
+
| Operator | Meaning | Description |
|
|
773
|
+
|:---------|:-------------------|:--------------------------------------------------------------|
|
|
774
|
+
| $eq | equal | Matches when the value is equal |
|
|
775
|
+
| $ne | not equal | Matches when the value is not equal |
|
|
776
|
+
| $in | value IN array | Matches when the value exists in the specified array |
|
|
777
|
+
| $not | value NOT IN array | Matches when the value does not exist in the specified array |
|
|
778
|
+
| $regex | regex match | Matches using a regular expression |
|
|
779
|
+
| $fn | custom function | Matches using a custom evaluation function |
|
|
780
|
+
|
|
781
|
+
## Evaluation Logic (Very Important !)
|
|
782
|
+
```js
|
|
783
|
+
[
|
|
784
|
+
{ rule1 AND rule1 },
|
|
785
|
+
{ rule2 AND rule2 }
|
|
786
|
+
]
|
|
787
|
+
⬇
|
|
788
|
+
OR
|
|
789
|
+
```
|
|
790
|
+
|
|
791
|
+
## Basic Usage for Examples :
|
|
792
|
+
|
|
793
|
+
```js
|
|
794
|
+
Credentials([
|
|
795
|
+
{ role: [9] }, // Only role check
|
|
796
|
+
{ email: 'a@b.com' }, // OR email
|
|
797
|
+
])
|
|
798
|
+
|
|
799
|
+
// Common Matching Patterns
|
|
800
|
+
{ role: [1, 2, 3] } // IN
|
|
801
|
+
{ role: 1 } // Equal
|
|
802
|
+
{ email: 'a@b.com' } // Equal
|
|
803
|
+
{ department: 'IT' } // Equal
|
|
804
|
+
{ status: ['active'] } // Dynamic key
|
|
805
|
+
```
|
|
806
|
+
|
|
807
|
+
## Operators Usage for Examples :
|
|
808
|
+
|
|
809
|
+
```js
|
|
810
|
+
// Equal with Role
|
|
811
|
+
Credentials([
|
|
812
|
+
{
|
|
813
|
+
role: { $eq: [1, 2, 5] }
|
|
814
|
+
}
|
|
815
|
+
])
|
|
816
|
+
|
|
817
|
+
// Not Equal with Role
|
|
818
|
+
Credentials([
|
|
819
|
+
{
|
|
820
|
+
role: { $ne: [0, 3] }
|
|
821
|
+
}
|
|
822
|
+
])
|
|
823
|
+
|
|
824
|
+
// Regex with Email domain
|
|
825
|
+
Credentials([
|
|
826
|
+
{
|
|
827
|
+
email: { $regex: /@company\.com$/ }
|
|
828
|
+
}
|
|
829
|
+
])
|
|
830
|
+
|
|
831
|
+
// IN with Role
|
|
832
|
+
Credentials([
|
|
833
|
+
{
|
|
834
|
+
role: { $in: [1, 2, 4] }
|
|
835
|
+
}
|
|
836
|
+
])
|
|
837
|
+
|
|
838
|
+
// NOT IN with Role
|
|
839
|
+
Credentials([
|
|
840
|
+
{
|
|
841
|
+
role: { $not: [0, 3] }
|
|
842
|
+
}
|
|
843
|
+
])
|
|
844
|
+
|
|
845
|
+
// Multiple OR rules
|
|
846
|
+
Credentials([
|
|
847
|
+
{
|
|
848
|
+
role: { $in: [1] },
|
|
849
|
+
email: 'john.doe@company.com'
|
|
850
|
+
},
|
|
851
|
+
// OR
|
|
852
|
+
{
|
|
853
|
+
role: { $in: [4] },
|
|
854
|
+
email: { $regex: /@company\.com$/ }
|
|
855
|
+
},
|
|
856
|
+
])
|
|
857
|
+
|
|
858
|
+
// Custom Function
|
|
859
|
+
Credentials([
|
|
860
|
+
{
|
|
861
|
+
role: {
|
|
862
|
+
$fn: (value, user) => value >= 4 && user.department === 'IT'
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
])
|
|
866
|
+
```
|
|
867
|
+
|
|
868
|
+
# Helpers
|
|
504
869
|
|
|
505
870
|
The `helpers` keep the files of functions for process specific something in the project. So, you might create the `helpers` in path `src/helpers` folder.
|
|
506
871
|
|
|
@@ -508,9 +873,9 @@ The `helpers` keep the files of functions for process specific something in the
|
|
|
508
873
|
$ beech make helperName --helper
|
|
509
874
|
```
|
|
510
875
|
|
|
511
|
-
***Example:***
|
|
876
|
+
***For Example :***
|
|
512
877
|
|
|
513
|
-
📂 TextEditor.js
|
|
878
|
+
📂 helpers/TextEditor.js
|
|
514
879
|
```js
|
|
515
880
|
module.exports = {
|
|
516
881
|
|
|
@@ -598,7 +963,7 @@ module.exports = {
|
|
|
598
963
|
|
|
599
964
|
};
|
|
600
965
|
```
|
|
601
|
-
|
|
966
|
+
### Request Token
|
|
602
967
|
**Authentication structure :** Simple ``users`` table:
|
|
603
968
|
```
|
|
604
969
|
==============================================================
|
|
@@ -611,8 +976,9 @@ module.exports = {
|
|
|
611
976
|
When you config passport with ```users``` table already. You will got Auth endpoint in available.
|
|
612
977
|
```js
|
|
613
978
|
POST: "/authentication" // Request token
|
|
979
|
+
POST: "/authentication/refresh" // Request refresh token
|
|
614
980
|
POST: "/authentication/create" // Create new Auth data
|
|
615
|
-
PATCH: "/authentication/update/:id" // Update old Auth data (needed id)
|
|
981
|
+
PATCH: "/authentication/update/:id" // Update old Auth data (needed user id)
|
|
616
982
|
```
|
|
617
983
|
|
|
618
984
|
***XHR Example :***
|
|
@@ -620,26 +986,28 @@ PATCH: "/authentication/update/:id" // Update old Auth data (needed id)
|
|
|
620
986
|
```js
|
|
621
987
|
// Request with body for gether Token
|
|
622
988
|
POST: "/authentication"
|
|
623
|
-
{
|
|
989
|
+
payload: {
|
|
624
990
|
username: "bombkiml",
|
|
625
991
|
password: "secret"
|
|
626
992
|
}
|
|
627
993
|
|
|
994
|
+
// Request with header for refresh token
|
|
995
|
+
POST: "/authentication/refresh"
|
|
996
|
+
headers: Authorization: Bearer <your_token>
|
|
628
997
|
|
|
629
998
|
// Request with body for Create Auth data
|
|
630
999
|
POST: "/authentication/create"
|
|
631
|
-
{
|
|
1000
|
+
payload: {
|
|
632
1001
|
username: "add_new_username",
|
|
633
1002
|
password: "add_new_secret",
|
|
634
1003
|
name: "add_new_my_name",
|
|
635
1004
|
email: "add_new_email"
|
|
636
1005
|
}
|
|
637
1006
|
|
|
638
|
-
|
|
639
1007
|
// Request with body for Update Auth data
|
|
640
1008
|
PATCH: "/authentication/update/1"
|
|
641
1009
|
headers: Authorization: Bearer <your_token>
|
|
642
|
-
{
|
|
1010
|
+
payload: {
|
|
643
1011
|
username: "update_bombkiml",
|
|
644
1012
|
password: "update_secret",
|
|
645
1013
|
name: "update_my_name",
|
|
@@ -647,10 +1015,10 @@ headers: Authorization: Bearer <your_token>
|
|
|
647
1015
|
}
|
|
648
1016
|
```
|
|
649
1017
|
|
|
650
|
-
# Beech
|
|
1018
|
+
# Beech Guard
|
|
651
1019
|
You can easy using 2 Factor authenticate with ```guard_field``` inside ```passport.config.js``` file and add your Guard field ex: ```2fa``` field for Authenticate Conditions.
|
|
652
1020
|
|
|
653
|
-
## #
|
|
1021
|
+
## # Guard (2FA, Other)
|
|
654
1022
|
|
|
655
1023
|
📂 passport.config.js
|
|
656
1024
|
```js
|
|
@@ -659,7 +1027,7 @@ module.exports = {
|
|
|
659
1027
|
|
|
660
1028
|
guard: {
|
|
661
1029
|
// Other fields add for authenticate, exmaple ["pin", "hint", "2fa"]
|
|
662
|
-
guard_field: ["2fa"], 👈 // your feild guard.
|
|
1030
|
+
guard_field: ["2fa"], 👈 // your feild guard. (Disabled to remove it.)
|
|
663
1031
|
|
|
664
1032
|
...
|
|
665
1033
|
},
|
|
@@ -680,7 +1048,7 @@ module.exports = {
|
|
|
680
1048
|
guard: {
|
|
681
1049
|
...
|
|
682
1050
|
|
|
683
|
-
// Advanced guard
|
|
1051
|
+
// Advanced guard to Request (Needed some logical from front-end)
|
|
684
1052
|
advanced_guard: {
|
|
685
1053
|
allow: false, 👈 // advanced guard allow for All Endpoint.
|
|
686
1054
|
entity: "", // default entity `timing`
|
|
@@ -709,26 +1077,42 @@ $ npm install --save beech-auth0 moment
|
|
|
709
1077
|
# Yarn
|
|
710
1078
|
$ yarn add beech-auth0 moment
|
|
711
1079
|
```
|
|
712
|
-
Now! you can add some logic.
|
|
1080
|
+
Now! you can add some logic like this.
|
|
1081
|
+
|
|
1082
|
+
- Import packages
|
|
1083
|
+
|
|
713
1084
|
```js
|
|
1085
|
+
// CommonJS
|
|
714
1086
|
const { Auth0 } = require("beech-auth0");
|
|
715
1087
|
const moment = require("moment");
|
|
716
1088
|
|
|
1089
|
+
// ES6
|
|
1090
|
+
import { Auth0 } from "beech-auth0";
|
|
1091
|
+
import moment from "moment";
|
|
1092
|
+
```
|
|
1093
|
+
|
|
1094
|
+
- Get unix time with momentJS
|
|
1095
|
+
|
|
1096
|
+
```js
|
|
717
1097
|
// Get UNIX TIME with moment
|
|
718
1098
|
let unix_time = moment().unix();
|
|
1099
|
+
```
|
|
1100
|
+
|
|
1101
|
+
- Get hashing with Beech Auth0
|
|
719
1102
|
|
|
1103
|
+
```js
|
|
720
1104
|
// Auth0 Policy.
|
|
721
1105
|
Auth0(unix_time, 'your_advance_guard_secret', (error, hashing) => {
|
|
722
|
-
|
|
1106
|
+
|
|
723
1107
|
// Your XHR request for All Endpoint.
|
|
724
1108
|
POST: "/authentication"
|
|
725
|
-
headers: timing: hashing
|
|
1109
|
+
headers: { timing: hashing } 👈 // Assign advance guard entity to headers with callback hashing.
|
|
726
1110
|
|
|
727
1111
|
});
|
|
728
1112
|
|
|
729
1113
|
```
|
|
730
1114
|
|
|
731
|
-
## # Beech User
|
|
1115
|
+
## # Beech User Authentication Managements ###
|
|
732
1116
|
You can easy management `users` data with Beech, Only ```Store, Update``` NO ```Delete```, Anything you can make DELETE endpoint by yourself
|
|
733
1117
|
|
|
734
1118
|
```js
|
|
@@ -929,6 +1313,23 @@ module.exports = {
|
|
|
929
1313
|
duplicateRequest: {
|
|
930
1314
|
expiration: 500, // Can't duplicate request for 5 milliseconds each IP requests per `window`
|
|
931
1315
|
},
|
|
1316
|
+
|
|
1317
|
+
payload: {
|
|
1318
|
+
// The limit of request body size, json default "100KB", urlencoded default "100KB". Learn more: https://expressjs.com/en/4x/api.html#express.json
|
|
1319
|
+
json: {
|
|
1320
|
+
limit: "100KB", // default: "100KB"
|
|
1321
|
+
},
|
|
1322
|
+
urlencoded: {
|
|
1323
|
+
limit: "100KB", // default: "100KB"
|
|
1324
|
+
extended: true, // default: true (reccomended: true)
|
|
1325
|
+
},
|
|
1326
|
+
file: {
|
|
1327
|
+
uploadAllowMethod: ["POST", "PATCH", "PUT"], // Only apply file upload limit for POST, PATCH, PUT method.
|
|
1328
|
+
allowedTypes: ["image/jpeg", "image/png", "application/pdf"], // Example: Allow only JPEG, PNG, and PDF files. Learn more: https://www.digipres.org/formats/mime-types/
|
|
1329
|
+
limit: 5 * 1024 * 1024, // 5MB (default: Infinity)
|
|
1330
|
+
},
|
|
1331
|
+
},
|
|
1332
|
+
|
|
932
1333
|
},
|
|
933
1334
|
},
|
|
934
1335
|
}
|
|
@@ -1014,8 +1415,6 @@ endpoint.get("/banana", specificDup1, (req, res) => {
|
|
|
1014
1415
|
...
|
|
1015
1416
|
```
|
|
1016
1417
|
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
1418
|
# Databases managements
|
|
1020
1419
|
|
|
1021
1420
|
## # Migrations & Seeder
|
|
@@ -1050,6 +1449,7 @@ Before continuing further we will need to tell CLI how to connect to database. T
|
|
|
1050
1449
|
"password": null,
|
|
1051
1450
|
"database": "database_development",
|
|
1052
1451
|
"host": "127.0.0.1",
|
|
1452
|
+
"port": 3306, // IF your another port
|
|
1053
1453
|
"dialect": "mysql"
|
|
1054
1454
|
},
|
|
1055
1455
|
"test": {
|
|
@@ -1098,7 +1498,12 @@ Until this step, we haven't inserted anything into the database. We have just cr
|
|
|
1098
1498
|
|
|
1099
1499
|
- **Migrate Down** : you can use `db:migrate:undo`, this command will revert most recent migration.
|
|
1100
1500
|
```sh
|
|
1501
|
+
// Step to undo
|
|
1101
1502
|
$ npx sequelize-cli db:migrate:undo
|
|
1503
|
+
|
|
1504
|
+
// All to undo
|
|
1505
|
+
$ npx sequelize-cli db:migrate:undo:all
|
|
1506
|
+
|
|
1102
1507
|
```
|
|
1103
1508
|
|
|
1104
1509
|
## # Creating First Seeder
|
|
@@ -1147,9 +1552,9 @@ Test using [Jest](https://jestjs.io/en/) for testing the project. Jest is a deli
|
|
|
1147
1552
|
|
|
1148
1553
|
So, When you make the new endpoints it's automatic create test file end with `.spec.js` in `__test__` folder with constant `baseUrl` variable and `axios` package.
|
|
1149
1554
|
|
|
1150
|
-
Example
|
|
1555
|
+
***For Example :***
|
|
1151
1556
|
|
|
1152
|
-
📂 fruit-endpoints.spec.js
|
|
1557
|
+
📂 \_\_test\_\_/unit/endpoints/fruit-endpoints.spec.js
|
|
1153
1558
|
```js
|
|
1154
1559
|
const endpoint = baseUrl.concat("/fruit");
|
|
1155
1560
|
|
|
@@ -1180,7 +1585,7 @@ Docker builds images automatically by reading the instructions from a Dockerfile
|
|
|
1180
1585
|
|
|
1181
1586
|
📂 Dockerfile
|
|
1182
1587
|
```js
|
|
1183
|
-
FROM node:
|
|
1588
|
+
FROM node:18-alpine
|
|
1184
1589
|
ENV NODE_ENV=production
|
|
1185
1590
|
WORKDIR /usr/src/api
|
|
1186
1591
|
COPY ["package.json", "package-lock.json*", "./"]
|