@carbonorm/carbonnode 4.0.0 → 5.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/README.md +246 -507
- package/dist/api/executors/SqlExecutor.d.ts +6 -0
- package/dist/api/handlers/ExpressHandler.d.ts +2 -1
- package/dist/api/orm/builders/ConditionBuilder.d.ts +2 -0
- package/dist/api/types/ormInterfaces.d.ts +12 -0
- package/dist/api/utils/sqlAllowList.d.ts +2 -0
- package/dist/index.cjs.js +279 -20
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.esm.js +278 -21
- package/dist/index.esm.js.map +1 -1
- package/package.json +1 -1
- package/scripts/assets/handlebars/C6.test.ts.handlebars +578 -32
- package/scripts/generateRestBindings.cjs +5 -5
- package/scripts/generateRestBindings.ts +5 -5
- package/src/__tests__/fixtures/createTestServer.ts +11 -3
- package/src/__tests__/fixtures/sqlResponses/actor.get.json +13 -0
- package/src/__tests__/fixtures/sqlResponses/sqlAllowList.blocked.json +3 -0
- package/src/__tests__/fixtures/sqlResponses/sqlAllowList.json +3 -0
- package/src/__tests__/sakila-db/C6.js +1 -1
- package/src/__tests__/sakila-db/C6.mysql.cnf +6 -0
- package/src/__tests__/sakila-db/C6.mysqldump.json +1 -0
- package/src/__tests__/sakila-db/C6.mysqldump.sql +720 -0
- package/src/__tests__/sakila-db/C6.sqlAllowList.json +94 -0
- package/src/__tests__/sakila-db/C6.test.ts +578 -32
- package/src/__tests__/sakila-db/C6.ts +1 -1
- package/src/__tests__/sakila-db/sqlResponses/C6.actor.delete.json +10 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.actor.delete.lookup.json +9 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.actor.get.json +14 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.actor.join.json +15 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.actor.post.json +12 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.actor.post.latest.json +14 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.actor.put.json +11 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.actor.put.lookup.json +16 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.actor.seed.json +14 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.address.delete.json +10 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.address.delete.lookup.json +9 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.address.fk.current.json +358 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.address.fk.referenced.json +158 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.address.get.json +22 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.address.join.json +24 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.address.post.json +16 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.address.post.latest.json +22 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.address.put.json +11 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.address.put.lookup.json +24 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.address.seed.json +22 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.category.delete.json +10 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.category.delete.lookup.json +9 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.category.get.json +13 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.category.join.json +14 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.category.post.json +11 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.category.post.latest.json +13 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.category.put.json +11 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.category.put.lookup.json +15 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.category.seed.json +13 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.city.delete.json +10 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.city.delete.lookup.json +9 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.city.fk.current.json +158 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.city.fk.referenced.json +133 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.city.get.json +14 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.city.join.json +15 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.city.post.json +12 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.city.post.latest.json +14 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.city.put.json +11 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.city.put.lookup.json +16 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.city.seed.json +14 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.country.delete.json +10 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.country.delete.lookup.json +9 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.country.get.json +13 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.country.join.json +15 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.country.post.json +11 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.country.post.latest.json +13 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.country.put.json +11 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.country.put.lookup.json +15 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.country.seed.json +13 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.customer.delete.json +10 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.customer.delete.lookup.json +9 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.customer.fk.current.json +283 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.customer.fk.referenced.json +358 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.customer.get.json +19 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.customer.join.json +29 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.customer.post.json +17 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.customer.post.latest.json +19 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.customer.put.json +11 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.customer.put.lookup.json +21 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.customer.seed.json +19 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.film.delete.json +10 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.film.delete.lookup.json +9 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.film.fk.current.json +383 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.film.fk.referenced.json +38 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.film.get.json +23 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.film.join.json +24 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.film.post.json +20 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.film.post.latest.json +23 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.film.put.json +11 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.film.put.lookup.json +25 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.film.seed.json +23 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.inventory.delete.json +10 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.inventory.delete.lookup.json +9 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.inventory.fk.current.json +158 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.inventory.fk.referenced.json +20 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.inventory.get.json +14 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.inventory.join.json +25 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.inventory.post.json +12 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.inventory.post.latest.json +14 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.inventory.put.json +11 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.inventory.put.lookup.json +16 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.inventory.seed.json +14 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.language.delete.json +10 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.language.delete.lookup.json +9 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.language.get.json +13 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.language.join.json +24 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.language.post.json +11 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.language.post.latest.json +13 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.language.put.json +11 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.language.put.lookup.json +15 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.language.seed.json +13 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.payment.delete.json +10 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.payment.delete.lookup.json +9 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.payment.fk.current.json +233 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.payment.fk.referenced.json +233 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.payment.get.json +17 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.payment.join.json +24 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.payment.post.json +15 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.payment.post.latest.json +17 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.payment.put.json +11 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.payment.put.lookup.json +19 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.payment.seed.json +17 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.rental.delete.json +10 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.rental.delete.lookup.json +9 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.rental.fk.current.json +233 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.rental.fk.referenced.json +34 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.rental.get.json +17 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.rental.join.json +24 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.rental.post.json +15 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.rental.post.latest.json +17 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.rental.put.json +11 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.rental.put.lookup.json +19 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.rental.seed.json +17 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.staff.fk.current.json +34 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.staff.fk.referenced.json +20 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.staff.get.json +21 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.staff.join.json +31 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.staff.seed.json +21 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.store.fk.current.json +20 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.store.fk.referenced.json +34 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.store.get.json +14 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.store.join.json +24 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.store.seed.json +14 -0
- package/src/__tests__/sakila.generated.test.ts +31 -0
- package/src/__tests__/sqlAllowList.test.ts +135 -0
- package/src/__tests__/sqlBuilders.test.ts +17 -0
- package/src/api/executors/SqlExecutor.ts +156 -0
- package/src/api/handlers/ExpressHandler.ts +10 -1
- package/src/api/orm/builders/ConditionBuilder.ts +27 -7
- package/src/api/types/ormInterfaces.ts +15 -0
- package/src/api/utils/sqlAllowList.ts +54 -0
- package/src/index.ts +1 -0
package/README.md
CHANGED
|
@@ -7,12 +7,28 @@
|
|
|
7
7
|

|
|
8
8
|
[](https://github.com/CarbonORM/CarbonNode/actions/workflows/npm-publish-on-bump.yml)
|
|
9
9
|
|
|
10
|
-
# CarbonNode
|
|
10
|
+
# CarbonNode
|
|
11
|
+
|
|
12
|
+
CarbonNode is a part of the CarbonORM series. It is a NodeJS MySQL ORM that can run independently in the backend or paired with
|
|
13
|
+
CarbonReact for 1=1 syntax. Note the CarbonNode + CarbonReact experience is unmatched in interoperability.
|
|
14
|
+
|
|
15
|
+
# Purpose
|
|
16
|
+
|
|
17
|
+
CarbonNode is designed to generate RESTful API bindings for a MySQL database. The generated code provides a simple and
|
|
18
|
+
consistent interface for performing CRUD operations on the database tables. The goal is to reduce the amount of boilerplate
|
|
19
|
+
code needed to interact with the database and to provide a more efficient and reliable way to work with MySQL data in a NodeJS
|
|
20
|
+
environment. The major goals:
|
|
21
|
+
- Allow a 1-1 interoperability when querying data from the frontend to the backend.
|
|
22
|
+
- Language based Objects/Arrays for representing and modifying queries to eliminate string manipulation operations.
|
|
23
|
+
- Explicit column references to allow for easier refactoring and code completion in IDEs.
|
|
24
|
+
- Selecting a dead column will result in a compile time error instead of a runtime error.
|
|
25
|
+
- TypeScript types generated for each table in the database.
|
|
26
|
+
- Lifecycle hooks for each CRUD operation to allow for custom logic to be executed before and after the operation.
|
|
27
|
+
- Validation of data types and formats before executing CRUD operations to ensure data integrity.
|
|
28
|
+
|
|
29
|
+
It's easier to scale your middleware than your database.
|
|
30
|
+
CarbonNode aims to capture issues before they reach your database.
|
|
11
31
|
|
|
12
|
-
CarbonNode is a part of the CarbonORM series. It is a NodeJS MySQL ORM that is designed to work with CarbonPHP. This langauge
|
|
13
|
-
will implement the same ORM as CarbonPHP, but will be written in Typescript. Currently only C6 enabled requests can be sent
|
|
14
|
-
using the bindings. Receiving API requests and handling it appropriately is not yet implemented. This is scheduled for
|
|
15
|
-
early 2024. This repository is in the early stages of development an any support is greatly appreciated.
|
|
16
32
|
|
|
17
33
|
## Alpha Release
|
|
18
34
|
|
|
@@ -31,422 +47,173 @@ npm install @carbonorm/carbonnode
|
|
|
31
47
|
|
|
32
48
|
## Generate Models
|
|
33
49
|
|
|
34
|
-
The
|
|
35
|
-
|
|
36
|
-
control system. All arguments are optional. If you do not provide them the defaults will be used. The example arguments
|
|
37
|
-
below are the defaults.
|
|
50
|
+
The generator produces a single `C6.ts` file containing all tables, types, and REST bindings. Keep this file in version
|
|
51
|
+
control and share it between server and client. All arguments are optional; the example below shows the defaults.
|
|
38
52
|
|
|
39
53
|
```bash
|
|
40
|
-
npx generateRestBindings --user root --pass password --host 127.0.0.1 --port 3306 --dbname carbonPHP --prefix carbon_ --output /
|
|
54
|
+
npx generateRestBindings --user root --pass password --host 127.0.0.1 --port 3306 --dbname carbonPHP --prefix carbon_ --output ./shared/rest/C6.ts
|
|
41
55
|
```
|
|
42
56
|
|
|
43
|
-
|
|
44
|
-
[this command](https://github.com/CarbonORM/CarbonNode/blob/main/scripts/generateRestBindings.ts) in
|
|
45
|
-
[this repository](git@github.com:CarbonORM/CarbonNode.git). We use [Handlebars templates](https://mustache.github.io/)
|
|
46
|
-
to generate the code.
|
|
57
|
+
The generated file exports `C6`, `GLOBAL_REST_PARAMETERS`, `TABLES`, `ORM`, and per-table bindings (e.g. `Users`):
|
|
47
58
|
|
|
48
|
-
|
|
59
|
+
```typescript
|
|
60
|
+
import { C6, GLOBAL_REST_PARAMETERS, Users } from "./shared/rest/C6";
|
|
61
|
+
```
|
|
49
62
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
63
|
+
You can view the generator source in
|
|
64
|
+
[CarbonNode](https://github.com/CarbonORM/CarbonNode/blob/main/scripts/generateRestBindings.ts). We use
|
|
65
|
+
[Handlebars templates](https://mustache.github.io/) to generate the code.
|
|
53
66
|
|
|
54
|
-
|
|
55
|
-
[IntelliJ](https://www.jetbrains.com/help/idea/file-nesting-dialog.html) or
|
|
56
|
-
[VSCode](https://code.visualstudio.com/updates/v1_67#_explorer-file-nesting).
|
|
67
|
+
### Runtime Setup
|
|
57
68
|
|
|
58
|
-
|
|
69
|
+
CarbonNode executes SQL directly when `GLOBAL_REST_PARAMETERS.mysqlPool` is provided. If no pool is set, it will use
|
|
70
|
+
the HTTP executor (useful for frontends or non-Node runtimes).
|
|
59
71
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
72
|
+
```typescript
|
|
73
|
+
import mysql from "mysql2/promise";
|
|
74
|
+
import { GLOBAL_REST_PARAMETERS } from "./shared/rest/C6";
|
|
75
|
+
|
|
76
|
+
GLOBAL_REST_PARAMETERS.mysqlPool = mysql.createPool({
|
|
77
|
+
host: "127.0.0.1",
|
|
78
|
+
user: "root",
|
|
79
|
+
password: "password",
|
|
80
|
+
database: "carbonPHP",
|
|
81
|
+
});
|
|
64
82
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
83
|
+
// Optional HTTP path:
|
|
84
|
+
// GLOBAL_REST_PARAMETERS.axios = axiosInstance;
|
|
85
|
+
// GLOBAL_REST_PARAMETERS.restURL = "/rest/";
|
|
68
86
|
|
|
69
|
-
|
|
87
|
+
// Optional websocket broadcast on writes:
|
|
88
|
+
// GLOBAL_REST_PARAMETERS.websocketBroadcast = (payload) => wsServer.broadcast(JSON.stringify(payload));
|
|
89
|
+
```
|
|
70
90
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
|
80
|
-
/*!50503 SET character_set_client = utf8mb4 */;
|
|
81
|
-
CREATE TABLE `carbon_users` (
|
|
82
|
-
`user_username` varchar(100) NOT NULL,
|
|
83
|
-
`user_password` varchar(225) NOT NULL,
|
|
84
|
-
`user_id` binary(16) NOT NULL,
|
|
85
|
-
`user_type` varchar(20) NOT NULL DEFAULT 'Athlete',
|
|
86
|
-
`user_sport` varchar(20) DEFAULT 'GOLF',
|
|
87
|
-
`user_session_id` varchar(225) DEFAULT NULL,
|
|
88
|
-
`user_facebook_id` varchar(225) DEFAULT NULL,
|
|
89
|
-
`user_first_name` varchar(25) NOT NULL,
|
|
90
|
-
`user_last_name` varchar(25) NOT NULL,
|
|
91
|
-
`user_profile_pic` varchar(225) DEFAULT NULL,
|
|
92
|
-
`user_profile_uri` varchar(225) DEFAULT NULL,
|
|
93
|
-
`user_cover_photo` varchar(225) DEFAULT NULL,
|
|
94
|
-
`user_birthday` varchar(9) DEFAULT NULL,
|
|
95
|
-
`user_gender` varchar(25) DEFAULT NULL,
|
|
96
|
-
`user_about_me` varchar(225) DEFAULT NULL,
|
|
97
|
-
`user_rank` int DEFAULT '0',
|
|
98
|
-
`user_email` varchar(50) NOT NULL,
|
|
99
|
-
`user_email_code` varchar(225) DEFAULT NULL,
|
|
100
|
-
`user_email_confirmed` tinyint DEFAULT '0' COMMENT 'need to change to enums, but no support in rest yet',
|
|
101
|
-
`user_generated_string` varchar(200) DEFAULT NULL,
|
|
102
|
-
`user_membership` int DEFAULT '0',
|
|
103
|
-
`user_deactivated` tinyint DEFAULT '0',
|
|
104
|
-
`user_last_login` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
105
|
-
`user_ip` varchar(20) NOT NULL,
|
|
106
|
-
`user_education_history` varchar(200) DEFAULT NULL,
|
|
107
|
-
`user_location` varchar(20) DEFAULT NULL,
|
|
108
|
-
`user_creation_date` datetime DEFAULT CURRENT_TIMESTAMP,
|
|
109
|
-
PRIMARY KEY (`user_id`),
|
|
110
|
-
UNIQUE KEY `carbon_users_user_username_uindex` (`user_username`),
|
|
111
|
-
UNIQUE KEY `user_user_profile_uri_uindex` (`user_profile_uri`),
|
|
112
|
-
UNIQUE KEY `carbon_users_user_facebook_id_uindex` (`user_facebook_id`),
|
|
113
|
-
CONSTRAINT `user_entity_entity_pk_fk` FOREIGN KEY (`user_id`) REFERENCES `carbon_carbons` (`entity_pk`) ON DELETE CASCADE ON UPDATE CASCADE
|
|
114
|
-
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
|
|
115
|
-
/*!40101 SET character_set_client = @saved_cs_client */;
|
|
91
|
+
### Request Flow
|
|
92
|
+
|
|
93
|
+
```mermaid
|
|
94
|
+
flowchart LR
|
|
95
|
+
Client["App code\nActor.Get(...)" ] --> RestRequest["restOrm + restRequest"]
|
|
96
|
+
RestRequest -->|"Node + mysqlPool"| SqlExec["SqlExecutor"] --> MySQL[("MySQL")]
|
|
97
|
+
RestRequest -->|"No pool"| HttpExec["HttpExecutor"] --> RestApi["/rest/:table"]
|
|
98
|
+
RestApi --> Express["ExpressHandler"] --> SqlExec
|
|
116
99
|
```
|
|
117
|
-
|
|
118
|
-
|
|
100
|
+
|
|
101
|
+
### SQL Allowlist
|
|
102
|
+
|
|
103
|
+
To restrict which SQL statements can run in production, set `GLOBAL_REST_PARAMETERS.sqlAllowListPath` to a JSON file
|
|
104
|
+
containing allowed SQL strings. When the path is set, `SqlExecutor` normalizes whitespace and validates each query against
|
|
105
|
+
the allowlist. If the file is missing, an error is thrown; if the SQL is not listed, execution is blocked.
|
|
106
|
+
|
|
119
107
|
```typescript
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
'user_password'?: string;
|
|
123
|
-
'user_id'?: string;
|
|
124
|
-
'user_type'?: string;
|
|
125
|
-
'user_sport'?: string | null;
|
|
126
|
-
'user_session_id'?: string | null;
|
|
127
|
-
'user_facebook_id'?: string | null;
|
|
128
|
-
'user_first_name'?: string;
|
|
129
|
-
'user_last_name'?: string;
|
|
130
|
-
'user_profile_pic'?: string | null;
|
|
131
|
-
'user_profile_uri'?: string | null;
|
|
132
|
-
'user_cover_photo'?: string | null;
|
|
133
|
-
'user_birthday'?: string | null;
|
|
134
|
-
'user_gender'?: string | null;
|
|
135
|
-
'user_about_me'?: string | null;
|
|
136
|
-
'user_rank'?: number | null;
|
|
137
|
-
'user_email'?: string;
|
|
138
|
-
'user_email_code'?: string | null;
|
|
139
|
-
'user_email_confirmed'?: number | null;
|
|
140
|
-
'user_generated_string'?: string | null;
|
|
141
|
-
'user_membership'?: number | null;
|
|
142
|
-
'user_deactivated'?: number | null;
|
|
143
|
-
'user_last_login'?: string;
|
|
144
|
-
'user_ip'?: string;
|
|
145
|
-
'user_education_history'?: string | null;
|
|
146
|
-
'user_location'?: string | null;
|
|
147
|
-
'user_creation_date'?: string | null;
|
|
148
|
-
}
|
|
108
|
+
GLOBAL_REST_PARAMETERS.sqlAllowListPath = "/path/to/sqlAllowList.json";
|
|
109
|
+
```
|
|
149
110
|
|
|
150
|
-
|
|
111
|
+
Allowlist format:
|
|
151
112
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
113
|
+
```json
|
|
114
|
+
[
|
|
115
|
+
"SELECT * FROM `actor` LIMIT 1"
|
|
116
|
+
]
|
|
117
|
+
```
|
|
155
118
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
119
|
+
Generated tests in `src/__tests__/sakila-db/C6.test.ts` write response fixtures into `src/__tests__/sakila-db/sqlResponses/`
|
|
120
|
+
and compile `src/__tests__/sakila-db/C6.sqlAllowList.json` after the suite finishes. Pass that file path to enable
|
|
121
|
+
validation.
|
|
159
122
|
|
|
160
|
-
|
|
161
|
-
[C6C.SUBSELECT]: {
|
|
162
|
-
[C6C.SELECT]: [Property_Units.LOCATION],
|
|
163
|
-
[C6C.FROM]: Property_Units.TABLE_NAME,
|
|
164
|
-
[C6C.WHERE]: { [Property_Units.UNIT_ID]: [C6C.EQUAL, unitIdParam] },
|
|
165
|
-
[C6C.LIMIT]: 1,
|
|
166
|
-
},
|
|
167
|
-
[C6C.AS]: 'pu_target',
|
|
168
|
-
});
|
|
123
|
+
When using the REST handler directly, forward the path as well:
|
|
169
124
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
Property_Units.UNIT_ID,
|
|
173
|
-
Property_Units.LOCATION,
|
|
174
|
-
F(Property_Units.LOCATION, 'pu_target'),
|
|
175
|
-
],
|
|
176
|
-
[C6C.JOIN]: {
|
|
177
|
-
[C6C.INNER]: {
|
|
178
|
-
'parcel_sales ps': { 'ps.parcel_id': [C6C.EQUAL, Property_Units.PARCEL_ID] },
|
|
179
|
-
[puTarget as any]: {},
|
|
180
|
-
},
|
|
181
|
-
},
|
|
182
|
-
[C6C.PAGINATION]: {
|
|
183
|
-
[C6C.ORDER]: {
|
|
184
|
-
[C6C.ST_DISTANCE_SPHERE]: [
|
|
185
|
-
Property_Units.LOCATION,
|
|
186
|
-
F(Property_Units.LOCATION, 'pu_target'),
|
|
187
|
-
],
|
|
188
|
-
},
|
|
189
|
-
},
|
|
190
|
-
};
|
|
125
|
+
```typescript
|
|
126
|
+
app.all("/rest/:table", ExpressHandler({ C6, mysqlPool, sqlAllowListPath }));
|
|
191
127
|
```
|
|
192
128
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
129
|
+
### Generated Tests
|
|
130
|
+
|
|
131
|
+
The generator also writes `C6.test.ts` alongside `C6.ts`. Tests use Vitest and the generated bindings. Keep or delete the
|
|
132
|
+
file depending on your workflow.
|
|
133
|
+
|
|
134
|
+
The generator also writes `C6.MySqlDump.json`, `C6.mysqldump.sql`, and `C6.mysql.cnf` into the same output directory for
|
|
135
|
+
debugging and inspection.
|
|
136
|
+
|
|
137
|
+
### Templates
|
|
138
|
+
|
|
139
|
+
Two templates are used to generate the output:
|
|
140
|
+
|
|
141
|
+
1) [C6.ts.handlebars](https://github.com/CarbonORM/CarbonNode/blob/main/scripts/assets/handlebars/C6.ts.handlebars)
|
|
142
|
+
2) [C6.test.ts.handlebars](https://github.com/CarbonORM/CarbonNode/blob/main/scripts/assets/handlebars/C6.test.ts.handlebars)
|
|
143
|
+
|
|
144
|
+
#### Generation Example
|
|
145
|
+
|
|
146
|
+
0) **npx generateRestBindings** is executed.
|
|
147
|
+
1) **The MySQL dump tool** outputs a structure for every table.
|
|
148
|
+
|
|
149
|
+
```mysql
|
|
150
|
+
CREATE TABLE actor (
|
|
151
|
+
actor_id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
|
152
|
+
first_name VARCHAR(45) NOT NULL,
|
|
153
|
+
last_name VARCHAR(45) NOT NULL,
|
|
154
|
+
last_update TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
155
|
+
PRIMARY KEY (actor_id),
|
|
156
|
+
KEY idx_actor_last_name (last_name)
|
|
157
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
2) **The generator** parses the table structure and creates an internal representation.
|
|
161
|
+
```typescript
|
|
162
|
+
export interface iActor {
|
|
163
|
+
'actor_id'?: number;
|
|
164
|
+
'first_name'?: string;
|
|
165
|
+
'last_name'?: string;
|
|
166
|
+
'last_update'?: Date | number | string;
|
|
225
167
|
}
|
|
226
168
|
|
|
227
|
-
export
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
USER_GENDER: 'carbon_users.user_gender',
|
|
243
|
-
USER_ABOUT_ME: 'carbon_users.user_about_me',
|
|
244
|
-
USER_RANK: 'carbon_users.user_rank',
|
|
245
|
-
USER_EMAIL: 'carbon_users.user_email',
|
|
246
|
-
USER_EMAIL_CODE: 'carbon_users.user_email_code',
|
|
247
|
-
USER_EMAIL_CONFIRMED: 'carbon_users.user_email_confirmed',
|
|
248
|
-
USER_GENERATED_STRING: 'carbon_users.user_generated_string',
|
|
249
|
-
USER_MEMBERSHIP: 'carbon_users.user_membership',
|
|
250
|
-
USER_DEACTIVATED: 'carbon_users.user_deactivated',
|
|
251
|
-
USER_LAST_LOGIN: 'carbon_users.user_last_login',
|
|
252
|
-
USER_IP: 'carbon_users.user_ip',
|
|
253
|
-
USER_EDUCATION_HISTORY: 'carbon_users.user_education_history',
|
|
254
|
-
USER_LOCATION: 'carbon_users.user_location',
|
|
255
|
-
USER_CREATION_DATE: 'carbon_users.user_creation_date',
|
|
169
|
+
export type ActorPrimaryKeys =
|
|
170
|
+
'actor_id'
|
|
171
|
+
;
|
|
172
|
+
|
|
173
|
+
const actor:
|
|
174
|
+
C6RestfulModel<
|
|
175
|
+
'actor',
|
|
176
|
+
iActor,
|
|
177
|
+
ActorPrimaryKeys
|
|
178
|
+
> = {
|
|
179
|
+
TABLE_NAME: 'actor',
|
|
180
|
+
ACTOR_ID: 'actor.actor_id',
|
|
181
|
+
FIRST_NAME: 'actor.first_name',
|
|
182
|
+
LAST_NAME: 'actor.last_name',
|
|
183
|
+
LAST_UPDATE: 'actor.last_update',
|
|
256
184
|
PRIMARY: [
|
|
257
|
-
'
|
|
185
|
+
'actor.actor_id',
|
|
258
186
|
],
|
|
259
187
|
PRIMARY_SHORT: [
|
|
260
|
-
'
|
|
188
|
+
'actor_id',
|
|
261
189
|
],
|
|
262
190
|
COLUMNS: {
|
|
263
|
-
'
|
|
264
|
-
'
|
|
265
|
-
'
|
|
266
|
-
'
|
|
267
|
-
'carbon_users.user_sport': 'user_sport',
|
|
268
|
-
'carbon_users.user_session_id': 'user_session_id',
|
|
269
|
-
'carbon_users.user_facebook_id': 'user_facebook_id',
|
|
270
|
-
'carbon_users.user_first_name': 'user_first_name',
|
|
271
|
-
'carbon_users.user_last_name': 'user_last_name',
|
|
272
|
-
'carbon_users.user_profile_pic': 'user_profile_pic',
|
|
273
|
-
'carbon_users.user_profile_uri': 'user_profile_uri',
|
|
274
|
-
'carbon_users.user_cover_photo': 'user_cover_photo',
|
|
275
|
-
'carbon_users.user_birthday': 'user_birthday',
|
|
276
|
-
'carbon_users.user_gender': 'user_gender',
|
|
277
|
-
'carbon_users.user_about_me': 'user_about_me',
|
|
278
|
-
'carbon_users.user_rank': 'user_rank',
|
|
279
|
-
'carbon_users.user_email': 'user_email',
|
|
280
|
-
'carbon_users.user_email_code': 'user_email_code',
|
|
281
|
-
'carbon_users.user_email_confirmed': 'user_email_confirmed',
|
|
282
|
-
'carbon_users.user_generated_string': 'user_generated_string',
|
|
283
|
-
'carbon_users.user_membership': 'user_membership',
|
|
284
|
-
'carbon_users.user_deactivated': 'user_deactivated',
|
|
285
|
-
'carbon_users.user_last_login': 'user_last_login',
|
|
286
|
-
'carbon_users.user_ip': 'user_ip',
|
|
287
|
-
'carbon_users.user_education_history': 'user_education_history',
|
|
288
|
-
'carbon_users.user_location': 'user_location',
|
|
289
|
-
'carbon_users.user_creation_date': 'user_creation_date',
|
|
191
|
+
'actor.actor_id': 'actor_id',
|
|
192
|
+
'actor.first_name': 'first_name',
|
|
193
|
+
'actor.last_name': 'last_name',
|
|
194
|
+
'actor.last_update': 'last_update',
|
|
290
195
|
},
|
|
291
196
|
TYPE_VALIDATION: {
|
|
292
|
-
'
|
|
293
|
-
MYSQL_TYPE: '
|
|
294
|
-
MAX_LENGTH: '100',
|
|
295
|
-
AUTO_INCREMENT: false,
|
|
296
|
-
SKIP_COLUMN_IN_POST: false
|
|
297
|
-
},
|
|
298
|
-
'carbon_users.user_password': {
|
|
299
|
-
MYSQL_TYPE: 'varchar',
|
|
300
|
-
MAX_LENGTH: '225',
|
|
301
|
-
AUTO_INCREMENT: false,
|
|
302
|
-
SKIP_COLUMN_IN_POST: false
|
|
303
|
-
},
|
|
304
|
-
'carbon_users.user_id': {
|
|
305
|
-
MYSQL_TYPE: 'binary',
|
|
306
|
-
MAX_LENGTH: '16',
|
|
307
|
-
AUTO_INCREMENT: false,
|
|
308
|
-
SKIP_COLUMN_IN_POST: false
|
|
309
|
-
},
|
|
310
|
-
'carbon_users.user_type': {
|
|
311
|
-
MYSQL_TYPE: 'varchar',
|
|
312
|
-
MAX_LENGTH: '20',
|
|
313
|
-
AUTO_INCREMENT: false,
|
|
314
|
-
SKIP_COLUMN_IN_POST: false
|
|
315
|
-
},
|
|
316
|
-
'carbon_users.user_sport': {
|
|
317
|
-
MYSQL_TYPE: 'varchar',
|
|
318
|
-
MAX_LENGTH: '20',
|
|
319
|
-
AUTO_INCREMENT: false,
|
|
320
|
-
SKIP_COLUMN_IN_POST: false
|
|
321
|
-
},
|
|
322
|
-
'carbon_users.user_session_id': {
|
|
323
|
-
MYSQL_TYPE: 'varchar',
|
|
324
|
-
MAX_LENGTH: '225',
|
|
325
|
-
AUTO_INCREMENT: false,
|
|
326
|
-
SKIP_COLUMN_IN_POST: false
|
|
327
|
-
},
|
|
328
|
-
'carbon_users.user_facebook_id': {
|
|
329
|
-
MYSQL_TYPE: 'varchar',
|
|
330
|
-
MAX_LENGTH: '225',
|
|
331
|
-
AUTO_INCREMENT: false,
|
|
332
|
-
SKIP_COLUMN_IN_POST: false
|
|
333
|
-
},
|
|
334
|
-
'carbon_users.user_first_name': {
|
|
335
|
-
MYSQL_TYPE: 'varchar',
|
|
336
|
-
MAX_LENGTH: '25',
|
|
337
|
-
AUTO_INCREMENT: false,
|
|
338
|
-
SKIP_COLUMN_IN_POST: false
|
|
339
|
-
},
|
|
340
|
-
'carbon_users.user_last_name': {
|
|
341
|
-
MYSQL_TYPE: 'varchar',
|
|
342
|
-
MAX_LENGTH: '25',
|
|
343
|
-
AUTO_INCREMENT: false,
|
|
344
|
-
SKIP_COLUMN_IN_POST: false
|
|
345
|
-
},
|
|
346
|
-
'carbon_users.user_profile_pic': {
|
|
347
|
-
MYSQL_TYPE: 'varchar',
|
|
348
|
-
MAX_LENGTH: '225',
|
|
349
|
-
AUTO_INCREMENT: false,
|
|
350
|
-
SKIP_COLUMN_IN_POST: false
|
|
351
|
-
},
|
|
352
|
-
'carbon_users.user_profile_uri': {
|
|
353
|
-
MYSQL_TYPE: 'varchar',
|
|
354
|
-
MAX_LENGTH: '225',
|
|
355
|
-
AUTO_INCREMENT: false,
|
|
356
|
-
SKIP_COLUMN_IN_POST: false
|
|
357
|
-
},
|
|
358
|
-
'carbon_users.user_cover_photo': {
|
|
359
|
-
MYSQL_TYPE: 'varchar',
|
|
360
|
-
MAX_LENGTH: '225',
|
|
361
|
-
AUTO_INCREMENT: false,
|
|
362
|
-
SKIP_COLUMN_IN_POST: false
|
|
363
|
-
},
|
|
364
|
-
'carbon_users.user_birthday': {
|
|
365
|
-
MYSQL_TYPE: 'varchar',
|
|
366
|
-
MAX_LENGTH: '9',
|
|
367
|
-
AUTO_INCREMENT: false,
|
|
368
|
-
SKIP_COLUMN_IN_POST: false
|
|
369
|
-
},
|
|
370
|
-
'carbon_users.user_gender': {
|
|
371
|
-
MYSQL_TYPE: 'varchar',
|
|
372
|
-
MAX_LENGTH: '25',
|
|
373
|
-
AUTO_INCREMENT: false,
|
|
374
|
-
SKIP_COLUMN_IN_POST: false
|
|
375
|
-
},
|
|
376
|
-
'carbon_users.user_about_me': {
|
|
377
|
-
MYSQL_TYPE: 'varchar',
|
|
378
|
-
MAX_LENGTH: '225',
|
|
379
|
-
AUTO_INCREMENT: false,
|
|
380
|
-
SKIP_COLUMN_IN_POST: false
|
|
381
|
-
},
|
|
382
|
-
'carbon_users.user_rank': {
|
|
383
|
-
MYSQL_TYPE: 'int',
|
|
384
|
-
MAX_LENGTH: '',
|
|
385
|
-
AUTO_INCREMENT: false,
|
|
386
|
-
SKIP_COLUMN_IN_POST: false
|
|
387
|
-
},
|
|
388
|
-
'carbon_users.user_email': {
|
|
389
|
-
MYSQL_TYPE: 'varchar',
|
|
390
|
-
MAX_LENGTH: '50',
|
|
391
|
-
AUTO_INCREMENT: false,
|
|
392
|
-
SKIP_COLUMN_IN_POST: false
|
|
393
|
-
},
|
|
394
|
-
'carbon_users.user_email_code': {
|
|
395
|
-
MYSQL_TYPE: 'varchar',
|
|
396
|
-
MAX_LENGTH: '225',
|
|
397
|
-
AUTO_INCREMENT: false,
|
|
398
|
-
SKIP_COLUMN_IN_POST: false
|
|
399
|
-
},
|
|
400
|
-
'carbon_users.user_email_confirmed': {
|
|
401
|
-
MYSQL_TYPE: 'tinyint',
|
|
402
|
-
MAX_LENGTH: '',
|
|
403
|
-
AUTO_INCREMENT: false,
|
|
404
|
-
SKIP_COLUMN_IN_POST: false
|
|
405
|
-
},
|
|
406
|
-
'carbon_users.user_generated_string': {
|
|
407
|
-
MYSQL_TYPE: 'varchar',
|
|
408
|
-
MAX_LENGTH: '200',
|
|
409
|
-
AUTO_INCREMENT: false,
|
|
410
|
-
SKIP_COLUMN_IN_POST: false
|
|
411
|
-
},
|
|
412
|
-
'carbon_users.user_membership': {
|
|
413
|
-
MYSQL_TYPE: 'int',
|
|
414
|
-
MAX_LENGTH: '',
|
|
415
|
-
AUTO_INCREMENT: false,
|
|
416
|
-
SKIP_COLUMN_IN_POST: false
|
|
417
|
-
},
|
|
418
|
-
'carbon_users.user_deactivated': {
|
|
419
|
-
MYSQL_TYPE: 'tinyint',
|
|
420
|
-
MAX_LENGTH: '',
|
|
421
|
-
AUTO_INCREMENT: false,
|
|
422
|
-
SKIP_COLUMN_IN_POST: false
|
|
423
|
-
},
|
|
424
|
-
'carbon_users.user_last_login': {
|
|
425
|
-
MYSQL_TYPE: 'datetime',
|
|
197
|
+
'actor.actor_id': {
|
|
198
|
+
MYSQL_TYPE: 'smallint',
|
|
426
199
|
MAX_LENGTH: '',
|
|
427
|
-
AUTO_INCREMENT:
|
|
200
|
+
AUTO_INCREMENT: true,
|
|
428
201
|
SKIP_COLUMN_IN_POST: false
|
|
429
202
|
},
|
|
430
|
-
'
|
|
203
|
+
'actor.first_name': {
|
|
431
204
|
MYSQL_TYPE: 'varchar',
|
|
432
|
-
MAX_LENGTH: '
|
|
205
|
+
MAX_LENGTH: '45',
|
|
433
206
|
AUTO_INCREMENT: false,
|
|
434
207
|
SKIP_COLUMN_IN_POST: false
|
|
435
208
|
},
|
|
436
|
-
'
|
|
209
|
+
'actor.last_name': {
|
|
437
210
|
MYSQL_TYPE: 'varchar',
|
|
438
|
-
MAX_LENGTH: '
|
|
211
|
+
MAX_LENGTH: '45',
|
|
439
212
|
AUTO_INCREMENT: false,
|
|
440
213
|
SKIP_COLUMN_IN_POST: false
|
|
441
214
|
},
|
|
442
|
-
'
|
|
443
|
-
MYSQL_TYPE: '
|
|
444
|
-
MAX_LENGTH: '20',
|
|
445
|
-
AUTO_INCREMENT: false,
|
|
446
|
-
SKIP_COLUMN_IN_POST: false
|
|
447
|
-
},
|
|
448
|
-
'carbon_users.user_creation_date': {
|
|
449
|
-
MYSQL_TYPE: 'datetime',
|
|
215
|
+
'actor.last_update': {
|
|
216
|
+
MYSQL_TYPE: 'timestamp',
|
|
450
217
|
MAX_LENGTH: '',
|
|
451
218
|
AUTO_INCREMENT: false,
|
|
452
219
|
SKIP_COLUMN_IN_POST: false
|
|
@@ -454,160 +221,133 @@ export const users: iC6RestfulModel<RestTableNames> & iDefineUsers = {
|
|
|
454
221
|
},
|
|
455
222
|
REGEX_VALIDATION: {
|
|
456
223
|
},
|
|
224
|
+
LIFECYCLE_HOOKS: {
|
|
225
|
+
GET: {beforeProcessing:{}, beforeExecution:{}, afterExecution:{}, afterCommit:{}},
|
|
226
|
+
PUT: {beforeProcessing:{}, beforeExecution:{}, afterExecution:{}, afterCommit:{}},
|
|
227
|
+
POST: {beforeProcessing:{}, beforeExecution:{}, afterExecution:{}, afterCommit:{}},
|
|
228
|
+
DELETE: {beforeProcessing:{}, beforeExecution:{}, afterExecution:{}, afterCommit:{}},
|
|
229
|
+
},
|
|
457
230
|
TABLE_REFERENCES: {
|
|
458
|
-
|
|
459
|
-
TABLE: 'carbon_carbons',
|
|
460
|
-
COLUMN: 'entity_pk',
|
|
461
|
-
CONSTRAINT: 'user_entity_entity_pk_fk',
|
|
462
|
-
},],
|
|
231
|
+
|
|
463
232
|
},
|
|
464
233
|
TABLE_REFERENCED_BY: {
|
|
465
|
-
|
|
234
|
+
'actor_id': [{
|
|
235
|
+
TABLE: 'film_actor',
|
|
236
|
+
COLUMN: 'actor_id',
|
|
237
|
+
CONSTRAINT: 'fk_film_actor_actor',
|
|
238
|
+
},],
|
|
466
239
|
}
|
|
467
240
|
}
|
|
241
|
+
|
|
242
|
+
export const Actor = {
|
|
243
|
+
...actor,
|
|
244
|
+
...restOrm<
|
|
245
|
+
OrmGenerics<any, 'actor', iActor, ActorPrimaryKeys>
|
|
246
|
+
>(() => ({
|
|
247
|
+
...GLOBAL_REST_PARAMETERS,
|
|
248
|
+
restModel: actor
|
|
249
|
+
}))
|
|
250
|
+
}
|
|
468
251
|
```
|
|
469
|
-
|
|
252
|
+
|
|
253
|
+
3) **Profit**
|
|
254
|
+
You import from the frontend or backend using the same syntax:
|
|
255
|
+
|
|
470
256
|
```typescript
|
|
471
|
-
import {
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
removeInvalidKeys,
|
|
483
|
-
iAPI,
|
|
484
|
-
Modify
|
|
485
|
-
} from "@carbonorm/carbonnode";
|
|
486
|
-
import {deleteRestfulObjectArrays, updateRestfulObjectArrays} from "@carbonorm/carbonreact";
|
|
487
|
-
import {C6, iUsers, users, RestTableNames} from "./C6";
|
|
488
|
-
|
|
489
|
-
type GetCustomAndRequiredFields = {}
|
|
490
|
-
|
|
491
|
-
type GetRequestTableOverrides = {}
|
|
492
|
-
|
|
493
|
-
// required parameters, optional parameters, parameter type overrides, response, and table names
|
|
494
|
-
const Get = restRequest<GetCustomAndRequiredFields, iUsers, GetRequestTableOverrides, iGetC6RestResponse<iUsers>, RestTableNames>({
|
|
495
|
-
C6: C6,
|
|
496
|
-
tableName: users.TABLE_NAME,
|
|
497
|
-
requestMethod: GET,
|
|
498
|
-
queryCallback: (request) => {
|
|
499
|
-
request.success ??= 'Successfully received users!'
|
|
500
|
-
request.error ??= 'An unknown issue occurred creating the users!'
|
|
501
|
-
return request
|
|
257
|
+
import { Actor, C6 } from "./shared/rest/C6";
|
|
258
|
+
|
|
259
|
+
// GET
|
|
260
|
+
const actors = await Actor.Get({
|
|
261
|
+
[C6.SELECT]: [
|
|
262
|
+
Actor.ACTOR_ID,
|
|
263
|
+
Actor.FIRST_NAME,
|
|
264
|
+
Actor.LAST_NAME,
|
|
265
|
+
],
|
|
266
|
+
[C6.WHERE]: {
|
|
267
|
+
[Actor.LAST_NAME]: { like: "%PITT%" },
|
|
502
268
|
},
|
|
503
|
-
|
|
504
|
-
const responseData = response?.data?.rest;
|
|
505
|
-
updateRestfulObjectArrays<iUsers>(Array.isArray(responseData) ? responseData : [responseData], "users", C6.users.PRIMARY_SHORT as (keyof iUsers)[])
|
|
506
|
-
}
|
|
269
|
+
[C6.PAGINATION]: { [C6.LIMIT]: 10 },
|
|
507
270
|
});
|
|
508
271
|
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
272
|
+
// POST
|
|
273
|
+
await Actor.Post({
|
|
274
|
+
[Actor.FIRST_NAME]: "Brad",
|
|
275
|
+
[Actor.LAST_NAME]: "Pitt",
|
|
276
|
+
});
|
|
512
277
|
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
}, C6.TABLES)
|
|
519
|
-
], "users", users.PRIMARY_SHORT as (keyof iUsers)[])
|
|
520
|
-
}
|
|
278
|
+
// PUT (singular)
|
|
279
|
+
await Actor.Put({
|
|
280
|
+
[Actor.ACTOR_ID]: 42,
|
|
281
|
+
[Actor.LAST_NAME]: "Updated",
|
|
282
|
+
});
|
|
521
283
|
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
requestMethod: PUT,
|
|
526
|
-
queryCallback: (request) => {
|
|
527
|
-
request.success ??= 'Successfully updated users data!'
|
|
528
|
-
request.error ??= 'An unknown issue occurred updating the users data!'
|
|
529
|
-
return request
|
|
530
|
-
},
|
|
531
|
-
responseCallback: putStateUsers
|
|
284
|
+
// DELETE (singular)
|
|
285
|
+
await Actor.Delete({
|
|
286
|
+
[Actor.ACTOR_ID]: 42,
|
|
532
287
|
});
|
|
288
|
+
```
|
|
533
289
|
|
|
534
|
-
|
|
290
|
+
Example response payloads (HTTP executor):
|
|
535
291
|
|
|
536
|
-
|
|
292
|
+
GET
|
|
537
293
|
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
}
|
|
546
|
-
updateRestfulObjectArrays<iUsers>(
|
|
547
|
-
undefined !== request.dataInsertMultipleRows
|
|
548
|
-
? request.dataInsertMultipleRows.map((request, index) => {
|
|
549
|
-
return removeInvalidKeys<iUsers>({
|
|
550
|
-
...request,
|
|
551
|
-
...(index === 0 ? response?.data?.rest : {}),
|
|
552
|
-
}, C6.TABLES)
|
|
553
|
-
})
|
|
554
|
-
: [
|
|
555
|
-
removeInvalidKeys<iUsers>({
|
|
556
|
-
...request,
|
|
557
|
-
...response?.data?.rest,
|
|
558
|
-
}, C6.TABLES)
|
|
559
|
-
],
|
|
560
|
-
"users",
|
|
561
|
-
users.PRIMARY_SHORT as (keyof iUsers)[]
|
|
562
|
-
)
|
|
294
|
+
```json
|
|
295
|
+
{
|
|
296
|
+
"success": true,
|
|
297
|
+
"rest": [
|
|
298
|
+
{ "actor_id": 1, "first_name": "PENELOPE", "last_name": "GUINESS" }
|
|
299
|
+
],
|
|
300
|
+
"next": "Function"
|
|
563
301
|
}
|
|
302
|
+
```
|
|
564
303
|
|
|
565
|
-
|
|
566
|
-
C6: C6,
|
|
567
|
-
tableName: users.TABLE_NAME,
|
|
568
|
-
requestMethod: POST,
|
|
569
|
-
queryCallback: (request) => {
|
|
570
|
-
request.success ??= 'Successfully created the users data!'
|
|
571
|
-
request.error ??= 'An unknown issue occurred creating the users data!'
|
|
572
|
-
return request
|
|
573
|
-
},
|
|
574
|
-
responseCallback: postStateUsers
|
|
575
|
-
});
|
|
304
|
+
POST
|
|
576
305
|
|
|
577
|
-
|
|
306
|
+
```json
|
|
307
|
+
{
|
|
308
|
+
"success": true,
|
|
309
|
+
"created": 201,
|
|
310
|
+
"rest": { "actor_id": 201, "first_name": "Brad", "last_name": "Pitt" }
|
|
311
|
+
}
|
|
312
|
+
```
|
|
578
313
|
|
|
579
|
-
|
|
314
|
+
PUT
|
|
580
315
|
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
316
|
+
```json
|
|
317
|
+
{
|
|
318
|
+
"success": true,
|
|
319
|
+
"updated": true,
|
|
320
|
+
"rest": { "actor_id": 42, "last_name": "Updated" }
|
|
585
321
|
}
|
|
322
|
+
```
|
|
586
323
|
|
|
587
|
-
|
|
588
|
-
C6: C6,
|
|
589
|
-
tableName: users.TABLE_NAME,
|
|
590
|
-
requestMethod: DELETE,
|
|
591
|
-
queryCallback: (request) => {
|
|
592
|
-
request.success ??= 'Successfully removed the users data!'
|
|
593
|
-
request.error ??= 'An unknown issue occurred removing the users data!'
|
|
594
|
-
return request
|
|
595
|
-
},
|
|
596
|
-
responseCallback: deleteStateUsers
|
|
597
|
-
});
|
|
324
|
+
DELETE
|
|
598
325
|
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
Delete,
|
|
326
|
+
```json
|
|
327
|
+
{
|
|
328
|
+
"success": true,
|
|
329
|
+
"deleted": true,
|
|
330
|
+
"rest": { "actor_id": 42 }
|
|
605
331
|
}
|
|
606
|
-
|
|
607
|
-
export default Users;
|
|
608
332
|
```
|
|
609
333
|
|
|
334
|
+
SQL executor responses omit `success` and include `sql` for GETs plus `affected` for writes. Express responses from `ExpressHandler` add `success: true`.
|
|
335
|
+
|
|
336
|
+
SQL executor example (GET):
|
|
610
337
|
|
|
338
|
+
```json
|
|
339
|
+
{
|
|
340
|
+
"rest": [
|
|
341
|
+
{ "actor_id": 1, "first_name": "PENELOPE", "last_name": "GUINESS" }
|
|
342
|
+
],
|
|
343
|
+
"sql": {
|
|
344
|
+
"sql": "SELECT * FROM `actor` LIMIT 10",
|
|
345
|
+
"values": []
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
Our CarbonReact extends this solution for automatic state and pagination management.
|
|
611
351
|
|
|
612
352
|
|
|
613
353
|
# Git Hooks
|
|
@@ -628,4 +368,3 @@ This will configure Git to use the hooks in the `.githooks` directory. The hooks
|
|
|
628
368
|
# Support and Issues
|
|
629
369
|
|
|
630
370
|
Any issues found should be reported on [GitHub](https://github.com/CarbonORM/CarbonNode/issues).
|
|
631
|
-
|