data-primals-engine 1.5.2 → 1.6.1

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.
Files changed (70) hide show
  1. package/README.md +938 -915
  2. package/client/README.md +136 -136
  3. package/client/index.js +0 -1
  4. package/client/public/doc/API.postman_collection.json +213 -213
  5. package/client/public/react.svg +9 -9
  6. package/client/src/AddWidgetTypeModal.jsx +47 -47
  7. package/client/src/App.css +42 -42
  8. package/client/src/App.jsx +92 -150
  9. package/client/src/App.scss +6 -0
  10. package/client/src/AssistantChat.jsx +362 -363
  11. package/client/src/Button.jsx +12 -12
  12. package/client/src/Dashboard.jsx +480 -480
  13. package/client/src/DashboardHtmlViewItem.jsx +146 -146
  14. package/client/src/DashboardView.jsx +654 -654
  15. package/client/src/DataEditor.jsx +383 -383
  16. package/client/src/DataLayout.jsx +779 -808
  17. package/client/src/DataTable.jsx +782 -822
  18. package/client/src/DataTable.scss +186 -186
  19. package/client/src/GeolocationField.jsx +93 -93
  20. package/client/src/HistoryDialog.jsx +307 -307
  21. package/client/src/HistoryDialog.scss +319 -319
  22. package/client/src/HtmlViewBuilderModal.jsx +90 -90
  23. package/client/src/HtmlViewBuilderModal.scss +17 -17
  24. package/client/src/HtmlViewCard.jsx +43 -43
  25. package/client/src/ModelCreator.jsx +683 -686
  26. package/client/src/ModelCreator.scss +1 -1
  27. package/client/src/ModelCreatorField.jsx +950 -950
  28. package/client/src/ModelImporter.jsx +3 -0
  29. package/client/src/ModelList.jsx +280 -280
  30. package/client/src/Notification.jsx +136 -136
  31. package/client/src/PackGallery.jsx +391 -391
  32. package/client/src/PackGallery.scss +231 -231
  33. package/client/src/Pagination.jsx +143 -143
  34. package/client/src/RelationField.jsx +354 -354
  35. package/client/src/RelationSelectorWidget.jsx +172 -172
  36. package/client/src/RelationValue.jsx +2 -1
  37. package/client/src/contexts/CommandContext.jsx +260 -0
  38. package/client/src/contexts/ModelContext.jsx +4 -1
  39. package/client/src/contexts/UIContext.jsx +72 -72
  40. package/client/src/filter.js +263 -263
  41. package/client/src/index.css +90 -90
  42. package/package.json +6 -5
  43. package/perf/artillery-hooks.js +37 -37
  44. package/perf/setup.yml +25 -25
  45. package/src/constants.js +4 -0
  46. package/src/defaultModels.js +7 -0
  47. package/src/engine.js +335 -335
  48. package/src/events.js +232 -137
  49. package/src/filter.js +274 -274
  50. package/src/index.js +1 -0
  51. package/src/modules/assistant/assistant.js +225 -83
  52. package/src/modules/auth-google/index.js +50 -50
  53. package/src/modules/auth-microsoft/index.js +81 -81
  54. package/src/modules/data/data.core.js +118 -118
  55. package/src/modules/data/data.history.js +555 -555
  56. package/src/modules/data/data.operations.js +112 -9
  57. package/src/modules/data/data.relations.js +686 -686
  58. package/src/modules/data/data.routes.js +1879 -1879
  59. package/src/modules/data/data.validation.js +2 -2
  60. package/src/modules/file.js +247 -247
  61. package/src/modules/user.js +14 -9
  62. package/src/packs.js +2 -2
  63. package/src/providers.js +2 -2
  64. package/src/services/stripe.js +196 -196
  65. package/src/sso.js +193 -193
  66. package/test/data.history.integration.test.js +264 -264
  67. package/test/data.integration.test.js +1281 -1206
  68. package/test/events.test.js +108 -10
  69. package/test/model.integration.test.js +377 -377
  70. package/test/user.test.js +106 -1
package/README.md CHANGED
@@ -1,915 +1,938 @@
1
- # data-primals-engine
2
- [![Node.js CI](https://github.com/anonympins/data-primals-engine/actions/workflows/node.js.yml/badge.svg?branch=main)](https://github.com/anonympins/data-primals-engine/actions/workflows/node.js.yml)
3
- ![](https://img.shields.io/npm/dw/data-primals-engine)
4
- ![](https://img.shields.io/npm/last-update/data-primals-engine)
5
- ![](https://img.shields.io/github/v/release/anonympins/data-primals-engine)
6
- ![](https://img.shields.io/github/license/anonympins/data-primals-engine)
7
-
8
- **data-primals-engine** is a powerful and flexible **Node.js** backend framework designed to accelerate development of complex data-driven applications. Built on **Express.js** and **MongoDB**, it offers dynamic data modeling, automation workflows, advanced user management, and more.
9
-
10
- > Whether you're building a CRM, e-commerce site, CMS, or SaaS platform, **data-primals-engine** provides the strong foundations so you can focus on what makes your application unique.
11
-
12
- <p align="center">
13
- <a href="https://data.primals.net/prez1.jpg" target="_blank"><img alt="Light" src="https://data.primals.net/prez1.jpg" width="35%"></a>
14
- <a href="https://data.primals.net/prez6.jpg" target="_blank"><img alt="Light" src="https://data.primals.net/prez6.jpg" width="35%"></a>
15
- <a href="https://data.primals.net/api-docs" target="_blank"><img alt="Dark" src="https://data.primals.net/prez5.jpg" width="25%"></a>
16
- </p>
17
- ---
18
-
19
- ## 🚀 Key Features
20
-
21
- - **Visual Data Modeling**: Define and update schemas using a powerful UI Model Creator, or directly with JSON. No migrations required.
22
- - **Custom API Endpoints**: Create server-side logic and new API endpoints directly from the UI in a secure, sandboxed environment.
23
- - **Automation Workflows**: Trigger complex actions based on data events (create, update, delete) or schedules (cron).
24
- - **Advanced Querying & Aggregation**: Go beyond simple filters with a visual Condition Builder, deep relation expansion, complex lookups, and dynamic calculated fields.
25
- - **Rich UI Data Views**: Ready-to-use React components to display your data, including a powerful and configurable Data Table, a Kanban board, and a Calendar view.
26
- - **Integrated Backup & Restore**: Secure, encrypted user data backups with rotation policies, supporting both local and AWS S3 storage.
27
- - **Automatic Data Auditing**: Automatically tracks all changes (create, update, delete) for every record, providing a complete version history for auditing and traceability.
28
- - **Event-Driven & Extensible**: A core event system allows for deep customization and the easy creation of new modules or plugins.
29
- - **Authentication & Authorization**: Robust role-based access control (RBAC) and pluggable user providers.
30
- - **Built-in File Management**: Handle file uploads seamlessly with integrated support for AWS S3 storage.
31
- - **🧠 AI Integration**: Natively supports OpenAI, DeepSeek and Google Gemini models via LangChain for content generation, analysis, and more.
32
- - **🌐 Internationalization (i18n)**: Fully supports multilingual interfaces and user-specific translated data.
33
- - **📦 Starter Packs**: Quickly bootstrap applications with pre-built data packs for CRM, e-commerce, and more.
34
- - **📄Auto-Generated API Documentation**: Interactive API documentation available via the interface or at `/api-docs`.
35
-
36
- ## 🌟 Why Choose data-primals-engine?
37
-
38
- - **Zero Boilerplate**: Focus on your business logic, not infrastructure
39
- - **Scalability**: Architecture designed for rapidly growing applications
40
- - **Modularity**: Enable/disable features as needed
41
- - **Batteries Included**: Everything you need to get started quickly
42
- - **Proven Performance**: Handles 50k+ documents efficiently
43
- - **AI Ready**: Built-in LangChain integration for main providers (OpenAI,Gemini,Anthropic,DeepSeek)
44
-
45
- ---
46
-
47
- ## 📊 Proven Performance
48
-
49
- The engine has been rigorously tested to ensure stability and scalability. Load tests simulating complex, multi-step user journeys (including model creation, data import, and API interactions) show excellent results:
50
- - **Zero Failures**: 100% success rate under sustained concurrent load.
51
- - **Excellent Responsiveness**: Median response times as low as 15-30ms.
52
- - **Linear Scaling**: Predictable performance as user load increases.
53
-
54
- For detailed reports, see the [Performance Testing Documentation](./perf/README.md).
55
-
56
- ---
57
-
58
- ## ⚙️ Requirements
59
-
60
- - Node.js ≥ 20
61
- - MongoDB (local or remote), see [installation guide](https://www.mongodb.com/docs/manual/installation/)
62
- - NPM or Yarn
63
-
64
- ---
65
-
66
- ## ⚡ Quick Start
67
-
68
- ### check
69
- ```bash
70
- # Verify required versions
71
- node -v # Must show ≥ v20
72
- mongod --version # Must be installed
73
- ```
74
-
75
- ### install
76
-
77
- ```bash
78
- npm i data-primals-engine
79
- ```
80
- or
81
- ```bash
82
- git clone https://github.com/anonympins/data-primals-engine.git
83
- cd data-primals-engine
84
- npm install
85
- ```
86
-
87
- ### configure
88
- Possibly create a `.env` file:
89
- ```env
90
- MONGO_DB_URL=mongodb://127.0.0.1:27017
91
- ```
92
- | Variable | Description | Example |
93
- |:----------------------|:------------------------------------------------------------------------|:-----------------------------------------|
94
- | MONGO_DB_URL | Connection URL for your MongoDB database. | mongodb://user:pass@host:27017/db |
95
- | PORT | Port on which the Express server will listen. | 7633 |
96
- | JWT_SECRET | Secret key for signing JWT authentication tokens. | a_long_random_secret_string |
97
- | OPENAI_API_KEY | Your optional OpenAI API key for AI features. | sk-xxxxxxxxxxxxxxxxxxxx |
98
- | GOOGLE_API_KEY | Your optional Google (Gemini) API key for AI features. | AIzaSyxxxxxxxxxxxxxxxxxxxx |
99
- | DEEPSEEK_API_KEY | Your optional DeepSeek API key for AI features. | sk-xxxxxxxxxxxxxxxxxxxx |
100
- | ANTHROPIC_API_KEY | Your optional Anthropic API key for AI features. | sk-ant-xxxxxxxxxxxxxxxxxxx |
101
- | AWS_ACCESS_KEY_ID | AWS access key for S3 storage (files, backups). Keep empty to disable | AKIAIOSFODNN7EXAMPLE |
102
- | AWS_SECRET_ACCESS_KEY | AWS secret access key. | wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY | |
103
- | AWS_REGION | Region for your S3 bucket. | eu-west-3 | |
104
- | AWS_S3_BUCKET_NAME | Name of the S3 bucket to use. | my-backup-bucket | |
105
- | SMTP_HOST | SMTP server host for sending emails. | smtp.example.com |
106
- | SMTP_PORT | SMTP server port. | 587 |
107
- | SMTP_USER | Username for SMTP authentication. | user@example.com |
108
- | SMTP_PASS | Password for SMTP authentication. | password |
109
- | TLS | Encrypted connection (TLS) mode. Disabled by default | 0/1 false/true |
110
- | CA_CERT | Path to CA cert file. | certs/ca.crt |
111
- | CERT | Path to cert file. | certs/cert.pem |
112
- | CERT_KEY | Path to the key file for your certificate. | certs/key.pem |
113
-
114
- ### Programmatic Configuration
115
-
116
- In addition to environment variables, you can programmatically override most of the application's internal constants at runtime. This is useful for fine-tuning performance, limits, or behavior without restarting the server.
117
-
118
- The engine exposes a `Config` object for this purpose. You can modify any constant defined in `src/constants.js`.
119
-
120
- To override a constant, use the `Config.Set()` method **before** initializing the engine:
121
-
122
- ```javascript
123
- import express from "express";
124
- import { Engine, Config } from 'data-primals-engine';
125
-
126
- // Override default constants before engine initialization
127
- Config.Set("maxRequestData", 1000); // Increase max data per request
128
- Config.Set("maxFileSize", 50 * 1024 * 1024); // Allow larger file uploads (50MB)
129
-
130
- const app = express();
131
- const engine = await Engine.Create({ app });
132
- ```
133
-
134
- ### Start the server
135
- ```bash
136
- # Development mode
137
- npm run devserver
138
-
139
- # Production mode
140
- npm run server
141
- ```
142
-
143
- By default, the app runs on port **7633** : http://localhost:7633
144
-
145
-
146
- ---
147
-
148
- ## 🧠 Concepts
149
-
150
- ### Authentication & Authorization
151
-
152
- The engine includes a pluggable system for user management. For development and testing, it uses a `DefaultUserProvider` which creates a volatile **`demo`** user. This is perfect for getting started quickly without setting up a user database.
153
-
154
- For production environments, you should use SSO providers as seen in the page below, or extend the base `UserProvider` class to connect to your actual user database (e.g., another MongoDB collection, a SQL database, or an external authentication service). This allows you to implement your own logic for finding and creating users.
155
-
156
- ### Model generation
157
- Models are the way to handle structured data. They organize data and they can be declared in JSON.
158
-
159
- ```json
160
- {
161
- "name": "product",
162
- "description": "E-commerce product schema",
163
- "fields": [
164
- { "name": "name", "type": "string", "required": true },
165
- { "name": "price", "type": "number", "required": true },
166
- { "name": "stock", "type": "number", "default": 0 },
167
- { "name": "category", "type": "relation", "relation": "taxonomy",
168
- "relationFilter": { "$eq": ["$type", "category"] }
169
- },
170
- { "name": "tags", "type": "relation", "relation": "taxonomy", "multiple": true,
171
- "relationFilter": { "$eq": ["$type", "keyword"] }
172
- }
173
- ]
174
- }
175
- ```
176
-
177
- ### Custom fields types
178
-
179
- | Type | Description | Properties/Notes |
180
- |:------------|:------------------------------------------------------------------------------------|:--------------------------------------------------------------------------|
181
- | string | Character string. | minLength, maxLength |
182
- | string_t | International character string ID. | same as string, translated in { key, value } |
183
- | number | Numeric value (integer or float). | min, max |
184
- | boolean | Boolean value (true/false). | – |
185
- | date | Stores a ISO date. | – |
186
- | datetime | Stores an ISO date and time. | – |
187
- | richtext | Rich text field (HTML) for WYSIWYG editors. | |
188
- | richtext_t | International rich text field (HTML) for WYSIWYG editors. | i18n |
189
- | email | String validated as an email address. | – |
190
- | password | String that will be automatically hashed. | – |
191
- | enum | Allows selecting a value from a predefined list. | items: ["value1", "value2"] |
192
- | relation | Creates a link to a document in another model. | relation: "target_model_name", multiple: true/false |
193
- | file | For uploading a file (stored on S3 if configured). | allowedTypes:['image/jpeg', 'image/png', 'image/bmp'], maxSize: 1024*1000 |
194
- | image | Specialized file type for images, with preview. | – |
195
- | array | Stores a list of values. | itemsType: 'enum' // any type except relations |
196
- | object | Stores a nested JSON object. – | |
197
- | code | Stores language="*" as string, stores language="json" as arbitrary JSON structure. | language="json" conditionBuilder=true |
198
- | color | Stores an hexadecimal value of an RGB color | '#FF0000' |
199
- | model | Stores a model by name | – |
200
- | modelField | Stores a model field path | – |
201
-
202
- ### Other model features
203
- - Handles up to 2,000 direct relations by default (can be customized). For larger datasets, use intermediate collections
204
- - Automatic indexing on key fields
205
- - Custom indexing on fields
206
- - Anonymizable fields (encrypted for API users)
207
-
208
- ### Model constraints
209
- ```javascript
210
- {
211
- "name": "modelName",
212
- "fields": [
213
- { "name": "fieldName1", .... },
214
- { "name": "fieldName2", .... }
215
- ],
216
- "constraints": [
217
- // uniqueness
218
- { "name": "uniqueConstraint", type: "unique", keys: ["fieldName1", "fieldName2"] }
219
- ]
220
- }
221
- ```
222
-
223
- ## 🏗️ Use Case Examples
224
-
225
- ### 🛒 E-Commerce Backoffice
226
- - Install ecommerce-starter pack
227
- - Add products via API/UI
228
- - Customize order workflows
229
-
230
- ### 🎫 Support Ticket System
231
- - Create ticket model with [open, pending, resolved] statuses
232
- - Configure notification workflows
233
- - Add custom endpoints or dashboards/kpi for analytics
234
-
235
- ### 🤖 AI Chatbot
236
- - Define your model
237
- - Set up workflow: "When new entry → generate AI content"
238
-
239
- ### 📧 Email Campaign Management
240
- The "Marketing & Campaigning" starter pack provides a powerful solution for sending large-scale email campaigns without overloading your server.
241
-
242
- - **Install the Pack**: A single command installs the necessary models (`campaign`, `audience`) and a sophisticated workflow.
243
- - **Dynamic Audiences**: Create target audiences with MongoDB filters. For example, select all contacts with the "newsletter" tag or located in a specific country.
244
- - **Personalized Content**: Use variables like `{recipient.firstName}` in the subject and body of your emails for a personal touch.
245
- - **Automated & Scalable Sending**: When you schedule a campaign, a pre-configured workflow takes over:
246
- - It processes your audience in small batches (e.g., 10 recipients at a time).
247
- - It sends emails to each batch and waits before processing the next, ensuring stability.
248
- - It tracks processed recipients to avoid duplicates and allow the campaign to be safely paused and resumed.
249
- - Once all emails are sent, the campaign is automatically marked as "completed".
250
-
251
- This use case demonstrates how starter packs and workflows can automate complex, performance-critical business logic right out of the box.
252
-
253
- ---
254
-
255
- ## 📁 Project Structure
256
- ```
257
- data-primals-engine/
258
- ├── src/
259
- │ ├── middlewares/
260
- │ ├── migrations/
261
- │ ├── modules/
262
- │ ├── workers/
263
- │ ├── engine.js // The Express engine that serves the API
264
- │ ├── constants.js // The inner-application constants definitions
265
- │ ├── packs.js // The packs that will be loaded and available with installPack() method
266
- │ ├── defaultModels.js // The default models available at startup.
267
- ├── ...
268
- └── server.js
269
- ```
270
-
271
- ---
272
-
273
- ## 🔌 API Examples (using `curl`)
274
-
275
- ### 📁 Model Management
276
-
277
- #### Create a model
278
- > Defines a new data model (schema) in the system. The request body must contain the model's name and an array of field definitions.
279
- ```bash
280
- curl -X POST http://localhost:7633/api/model?_user=demo \
281
- -H "Authorization: Bearer demotoken" \
282
- -H "Content-Type: application/json" \
283
- -d '{
284
- "name": "newModel",
285
- "description": "A test model.",
286
- "fields": [
287
- {"name": "title", "type": "string", "required": true},
288
- {"name": "price", "type": "number"}
289
- ]
290
- }'
291
- ```
292
-
293
- #### Update a model
294
- > Modifies an existing model's structure using its unique ID. This allows you to add, remove, or change fields.
295
- ```bash
296
- curl -X PUT http://localhost:7633/api/model/60d0fe4f5311236168a109ca?_user=demo \
297
- -H "Authorization: Bearer demotoken" \
298
- -H "Content-Type: application/json" \
299
- -d '{
300
- "name": "updatedModel",
301
- "description": "Updated description.",
302
- "fields": [
303
- {"name": "title", "type": "string", "required": true},
304
- {"name": "status", "type": "enum", "items": ["active", "inactive"]}
305
- ]
306
- }'
307
- ```
308
-
309
- #### Delete a model
310
- > Permanently deletes a model definition using its name. This action is irreversible.
311
- ```bash
312
- curl -X DELETE "http://localhost:7633/api/model?_user=demo&name=newModel" \
313
- -H "Authorization: Bearer demotoken"
314
- ```
315
-
316
- ### 🗂️ Data Management
317
-
318
- #### Create a document
319
- > Creates a single new document in the specified model. The `data` object must conform to the model's schema.
320
- ```bash
321
- curl -X POST http://localhost:7633/api/data?_user=demo \
322
- -H "Authorization: Bearer demotoken" \
323
- -H "Content-Type: application/json" \
324
- -d '{
325
- "model": "product",
326
- "data": {
327
- "name": "Widget Extraordinaire",
328
- "price": 99.99,
329
- "stock": 150,
330
- "published": true
331
- }
332
- }'
333
- ```
334
-
335
-
336
- #### Search documents
337
- > Searches for documents using a MongoDB-style filter. This endpoint is ideal for complex queries, pagination, and sorting.
338
-
339
- ```bash
340
- curl -X POST http://localhost:7633/api/data/search?_user=demo \
341
- -H "Authorization: Bearer demotoken" \
342
- -H "Content-Type: application/json" \
343
- -d '{
344
- "model": "product",
345
- "filter": { "$gt" : ["$price", 50] }
346
- }'
347
- ```
348
-
349
- #### Update a document by ID
350
- > Updates a single document by its unique ID. The request body contains the fields to be modified.
351
- ```bash
352
- curl -X PUT http://localhost:7633/api/data/64a31c123ef59d4c8d55aa99?_user=demo \
353
- -H "Authorization: Bearer demotoken" \
354
- -H "Content-Type: application/json" \
355
- -d '{
356
- "model": "product",
357
- "data": { "price": 109.99 }
358
- }'
359
- ```
360
-
361
-
362
- #### Bulk update
363
- > Updates multiple documents matching a filter. This is efficient for applying changes to a batch of records, such as restocking all out-of-stock products.
364
- ```bash
365
- curl -X PUT http://localhost:7633/api/data?_user=demo \
366
- -H "Authorization: Bearer demotoken" \
367
- -H "Content-Type: application/json" \
368
- -d '{
369
- "model": "product",
370
- "data": { "stock": 999 }
371
- }'
372
- ```
373
-
374
- #### Delete documents
375
- > Deletes one or more documents. You can provide an array of `ids` to delete specific documents.
376
- ```bash
377
- curl -X DELETE http://localhost:7633/api/data?_user=demo \
378
- -H "Authorization: Bearer demotoken" \
379
- -H "Content-Type: application/json" \
380
- -d '{
381
- "ids": ["64a31c123ef59d4c8d55aa99"]
382
- }'
383
- ```
384
-
385
- ## Other operations
386
-
387
- Make sure you use the code below to initialize the user :
388
- ```javascript
389
- import express from "express";
390
- import {Engine, insertData, searchData } from 'data-primals-engine';
391
-
392
- // Ensure the engine is initialized
393
-
394
- const app = express();
395
- const engine = await Engine.Create({ app });
396
- const currentUser = await engine.userProvider.findUserByUsername('demo');
397
- if (!currentUser) {
398
- throw new Error("Could not retrieve the user. Please check credentials or user provider.");
399
- }
400
- console.log(`Successfully authenticated as ${currentUser.username}`);
401
- ```
402
-
403
- ### insertData(modelName, data, files, user)
404
-
405
- > Inserts one or more documents, intelligently handling nested relationships.
406
-
407
- ```javascript
408
- // Uses the `currentUser` object defined above
409
- const newProduct = { name: 'Super Widget', price: 99.99, status: 'available' };
410
- const result = await insertData('product', newProduct, {}, currentUser);
411
-
412
- if (result.success) {
413
- console.log(`Successfully inserted document with ID: ${result.insertedIds[0]}`);
414
- }
415
- ```
416
-
417
- ### editData(modelName, filter, data, files, user)
418
-
419
- > Updates existing data matching the filter.
420
-
421
- Example:
422
-
423
- ```javascript
424
- await editData(
425
- "userProfile",
426
- { _id: "507f1f77bcf86cd799439011" },
427
- { bio: "Updated bio text" },
428
- null, // No files
429
- currentUser
430
- );
431
- ```
432
-
433
- ```javascript
434
- await editData(
435
- "resource",
436
- { source: "507f1f77bcf86cd799439011" },
437
- { "file[]": {
438
- "path": "C:/Users/.../test-upload-on-insert.txt",
439
- "name": 'test-upload-on-insert.txt',
440
- "type": 'text/plain',
441
- "size": 1524,
442
- "lastModifiedDate": new Date(),
443
- }}, // No files
444
- currentUser
445
- );
446
- ```
447
-
448
- ### patchData(modelName, filter, data, files, user)
449
-
450
- > Partially updates data (only modifies specified fields).
451
-
452
- Example:
453
-
454
- ```javascript
455
- await patchData(
456
- "settings",
457
- { userId: "507f1f77bcf86cd799439011" },
458
- { theme: "dark" },
459
- null,
460
- currentUser
461
- );
462
- ```
463
-
464
- ### deleteData(modelName, filter, user)
465
-
466
- >Deletes data with cascading relation cleanup.
467
-
468
- Examples:
469
-
470
- ```javascript
471
- // Delete by IDs
472
- await deleteData("comments", ["61d1f1a9e3f1a9e3f1a9e3f1"], user);
473
-
474
- // Delete by filter
475
- await deleteData("logs", { createdAt: { $lt: "2023-01-01" } }, user);
476
- ```
477
-
478
- ### searchData(query, user)
479
-
480
- Powerful search with relation expansion and filtering.
481
-
482
- Query Options:
483
-
484
- - model: Model name to search
485
- - filter: MongoDB-style filter
486
- - depth: Relation expansion depth (default: 1)
487
- - limit/page: Pagination
488
- - sort: Sorting criteria
489
-
490
- Example:
491
- ```javascript
492
- const results = await searchData({
493
- model: "blogPost",
494
- filter: { status: "published" },
495
- depth: 2, // Expand author and comments
496
- limit: 10,
497
- sort: "createdAt:DESC"
498
- }, user);
499
- ```
500
-
501
- ## Import/Export
502
- ### importData(options, files, user)
503
- > Imports data from Excel / JSON / CSV files.
504
-
505
- Supported Formats:
506
-
507
- - Excel with headers or field mapping
508
- - CSV with headers or field mapping
509
- - JSON arrays
510
- - JSON with model-keyed objects
511
-
512
- Example:
513
-
514
- ```javascript
515
- const result = await importData(
516
- {
517
- model: "products",
518
- hasHeaders: true
519
- },
520
- {
521
- file: req.files.myFileField // from multipart body
522
- },
523
- currentUser
524
- );
525
- ```
526
-
527
- ### exportData(options, user)
528
- > Exports data to a structured format.
529
-
530
- Example:
531
-
532
- ```javascript
533
- await exportData(
534
- {
535
- models: ["products", "categories"],
536
- depth: 1, // Include relations
537
- lang: "fr" // Localized data
538
- },
539
- currentUser
540
- );
541
- // Returns: { success: true, data: { products: [...], categories: [...] } }
542
- ```
543
-
544
- ## Backup & Restore
545
- ### dumpUserData(user)
546
- > Creates an encrypted backup of user data.
547
-
548
- Features:
549
-
550
- - Automatic encryption
551
- - S3 or local storage
552
- - Retention policies by plan (daily/weekly/monthly)
553
-
554
- Example:
555
-
556
- ```javascript
557
- await dumpUserData(currentUser);
558
- // Backup saved to S3 or ./backups/
559
- ```
560
-
561
- ### loadFromDump(user, options)
562
-
563
- > Restores user data from backup.
564
-
565
- Options:
566
- - modelsOnly: Restore only model definitions
567
-
568
- Example:
569
-
570
- ```javascript
571
- await loadFromDump(currentUser, { modelsOnly: false });
572
- // Full restore including data
573
- ```
574
-
575
- ## Pack Management
576
-
577
- ### installPack(packId, user, lang)
578
-
579
- > Installs a data pack.
580
-
581
- Example:
582
-
583
- ```javascript
584
- const result = await installPack([
585
- {
586
- "name": "My custom pack",
587
- "description": "Markdown **description** of the pack",
588
- "tags": ["customPack", "tag1", "tag2"],
589
- "models": [
590
- "env", // default model
591
- {
592
- "name": "post",
593
- "description": "Defines a post",
594
- "fields": [
595
- {"name": "subject", "type": "string", "required": true},
596
- ]
597
- }, // or custom
598
- ],
599
- "data": {
600
- "all": { // all languages installed data
601
- "post": [
602
- {"subject": "My pack first data"}
603
- ]
604
- },
605
- "en": { // English specific installed data
606
- "post": [
607
- {"subject": "My english first post"}
608
- ]
609
- }
610
- }
611
- }
612
- ], user, "en");
613
- // Returns installation summary
614
- ```
615
-
616
- ```javascript
617
- const result = await installPack("61d1f1a9e3f1a9e3f1a9e3f1", user, "en");
618
- // Returns installation summary
619
- ```
620
-
621
- > You can also open the pack gallery to see the JSON structure of each pack, before installing them.
622
-
623
-
624
- ---
625
- ### Data Auditing & History
626
- data-primals-engine includes a built-in, automatic auditing system that creates a complete history for every record in your database.
627
-
628
- >This feature is essential for traceability, debugging, and maintaining a clear audit trail of all data manipulations.
629
-
630
- #### How It Works
631
- The history feature is non-intrusive and fully automatic.
632
-
633
- Whenever you use the standard data management functions (insertData, patchData, deleteData, etc.), the engine performs two actions:
634
- 1. It executes the requested operation (create, update, or delete) on the target document.
635
- 2. It saves a complete "snapshot" of the document's state into a dedicated history collection.
636
-
637
- >Each history entry contains the original data along with crucial metadata:
638
- - `_op`: The type of operation (i for insert, u for update, d for delete).
639
- - `_v`: The version number of the record.
640
- - `_user`: The username of the user who performed the action.
641
- - `_updatedAt`: The timestamp of the operation.
642
- - `_rid`: The ID of the original record, linking all history entries together.
643
-
644
- ### Accessing the History
645
- You can retrieve the full history for any record via a simple API endpoint:
646
- ```x-sh
647
- curl -X GET http://localhost:7633/api/data/history/:modelName/:recordId
648
- ```
649
-
650
- ---
651
-
652
- ## Workflows: Automate Your Business Logic
653
-
654
- > Workflows are the automation engine of your application.
655
-
656
- They allow you to define complex business processes that run in response to specific events, without writing custom code.
657
-
658
- This is perfect for tasks like **sending welcome emails**, managing **order fulfillment**, or triggering data synchronization.
659
-
660
- A workflow is composed of two main parts: **Triggers** and **Actions**.
661
-
662
- > A 'workflowTrigger' is the event that initiates a workflow run.
663
- - **DataAdded**: Fires when a new document is created (e.g., a new user signs up).
664
- - **DataEdited**: Fires when a document is updated (e.g., an order status changes to "shipped").
665
- - **DataDeleted**: Fires when a document is removed.
666
- - **Scheduled**: Runs at a specific time or interval using a Cron expression (e.g., 0 0 * * * for a nightly data cleanup job).
667
- - **Manual**: Triggered on-demand via an API call, allowing you to integrate workflows into any part of your application.
668
-
669
- > A 'workflowAction' is the individual steps a workflow executes. You can chain them together to create sophisticated logic.
670
- - **CreateData**: Create a new document in any model.
671
- - **UpdateData**: Modify one or more existing documents that match a filter.
672
- - **SendEmail**: Send a transactional email using dynamic data from the trigger.
673
- - **HttpRequest**: Make an HTTP request (GET, POST, etc.) to an external service or API.
674
- - **ExecuteScript**: Run a custom JavaScript snippet for complex logic, data transformation, or conditional branching.
675
- - **GenerateAIContent**: Use an integrated AI provider (like OpenAI or Gemini) to generate text, summarize content, or make decisions.
676
- - **Wait**: Pause the workflow for a specific duration before continuing to the next step
677
-
678
- See the details of the workflow models for more details.
679
-
680
- ## ⚡ Dynamic API Endpoints
681
- Beyond standard CRUD operations, data-primals-engine allows you to create your own custom API endpoints directly from the UI. This feature acts like a built-in serverless function environment, enabling you to write custom business logic, integrate with third-party services, or create complex data aggregations on the fly.
682
-
683
- Your code is executed in a secure, isolated sandbox, with access to the core data functions and the incoming request context.
684
-
685
- ### How It Works
686
- Define an Endpoint: You create a new document in the endpoint model.
687
- Write Your Logic: In the code field, you write the JavaScript that will be executed.
688
- Activate: The engine automatically listens for requests on /api/actions/:path that match your endpoint's definition.
689
-
690
- ### The endpoint Model
691
- To create a custom endpoint, you need to define a document with the following structure:
692
- ```json
693
- {
694
- "name": "GetContactPostCount",
695
- "path": "postCount/:name",
696
- "method": "GET",
697
- "code": "const posts = await db.find('content', { author: { $find: { $eq: ['$lastName', request.params.name]}}}); return { postCount: posts.length };",
698
- "isActive": true,
699
- "isPublic": true
700
- }
701
- ```
702
- | Field | Type | Description |
703
- |:---------|:--------|:---------------------------------------------------------------------|
704
- | name | string | A descriptive name for your endpoint (e.g., "Calculate User Stats"). |
705
- | path | string | The URL path. It can include parameters like :id. |
706
- | method | enum | The HTTP method: GET, POST, PUT, PATCH, or DELETE. |
707
- | code | code | The JavaScript code to execute when the endpoint is called. |
708
- | isActive | boolean | A flag to enable or disable the endpoint without deleting it. |
709
- | isPublic | boolean | A flag to enable public access (private by default). |
710
- ---
711
-
712
- ### The Execution Context
713
- Your JavaScript code runs in an async context and has access to several global objects that are securely injected into the sandbox:
714
- Your current user is used to make the calls.
715
-
716
- #### The context Object
717
- > This object contains all the information about the incoming HTTP request.
718
- - context.request.**body**: The parsed request body (for POST, PUT, PATCH).
719
- - context.request.**query**: The URL query parameters as an object.
720
- - context.request.**params**: The URL path parameters (e.g., username from /user-summary/:username).
721
- - context.request.**headers**: The incoming request headers.
722
-
723
- #### The db Object
724
- > A secure API to interact with the database. All methods are async and must be awaited. They automatically operate within the authenticated user's permissions.
725
- - await db.**create**(modelName, dataObject): Inserts a new document.
726
- - await db.**find**(modelName, filter): Finds multiple documents. Returns an array.
727
- - await db.**findOne**(modelName, filter): Finds a single document. Returns an object or null.
728
- - await db.**update**(modelName, filter, updateObject): Partially updates documents matching the filter (similar to a PATCH).
729
- - await db.**delete**(modelName, filter): Deletes documents matching the filter.
730
-
731
- #### The logger Object
732
- > A safe way to log messages from your script. These logs will be collected and can be returned in the API response if an error occurs, which is very useful for debugging.
733
- - logger.**info**(...args)
734
- - logger.**warn**(...args)
735
- - logger.**error**(...args)
736
-
737
- #### The env Object
738
- > Provides access to the user-defined variables stored in the env model, not the server's process.env.
739
- - await env.**get**(variableName): Retrieves a single environment variable's value.
740
- - await env.**getAll**(): Retrieves all user environment variables as an object.
741
-
742
- #### Example: Creating a User Summary Endpoint
743
- Let's create an endpoint that fetches a user's profile and counts how many posts they have published.
744
- 1. Create the Endpoint Document
745
- Create a new document in the endpoint model with the following data:
746
- ```json
747
- {
748
- "name": "Get User Summary",
749
- "path": "user-summary/:username",
750
- "method": "GET",
751
- "isActive": true,
752
- "code": "const { username } = context.request.params;\n\n if (!username) {\n logger.error('Username parameter is required.');\n return { success: false, error: 'Username is required.' };\n }\n\n logger.info(`Fetching summary for user: ${username}`);\n\n // Fetch the user profile using the sandboxed db API\n const userProfile = await db.findOne('userProfile', { username: username });\n\n if (!userProfile) {\n return { success: false, error: 'User not found' };\n }\n\n // Count the user's published posts\n const posts = await db.find('post', { \n authorId: userProfile._id.toString(), \n status: 'published' \n });\n\n return {\n profile: userProfile,\n publishedPosts: posts.length\n };\n});",
753
- }
754
- ```
755
- 2. Call the New Endpoint
756
- You can now call this custom endpoint like any other API route:
757
-
758
- Expected response :
759
- ```json
760
- {
761
- "profile": {
762
- "_id": "60d0fe4f5311236168a109ca",
763
- "username": "demo",
764
- "bio": "A demo user profile.",
765
- "...": "..."
766
- },
767
- "publishedPosts": 15
768
- }
769
-
770
- ```
771
-
772
- ### Pluggable SSO Authentication (Google, SAML, Azure AD)
773
-
774
- `data-primals-engine` features a powerful and modular Single Sign-On (SSO) system built on Passport.js. This allows you to easily integrate with various enterprise and social identity providers. The core engine remains lightweight, and you only enable the providers you need.
775
-
776
- #### How It Works
777
-
778
- The engine provides a central `Sso` component that manages authentication logic. Each provider (Google, SAML, etc.) is an independent module that "plugs into" this central component.
779
-
780
- To enable an SSO provider, follow these three steps:
781
-
782
- #### Example: Enabling Google Sign-In
783
-
784
- **1. Install the Provider's Library**
785
-
786
- The required Passport strategy is a `peerDependency`. You must install it in your project:
787
- ```bash
788
- npm install passport-google-oauth20
789
- ```
790
-
791
- **2. Enable the Module**
792
-
793
- To enable the module in your next engine initilization, just add it to the `modules` array:
794
- ```javascript
795
- Config.Set("modules", ["auth-google", "..."]);
796
- ```
797
-
798
- **3. Set Environment Variables**
799
-
800
- Create or update your `.env` file with the credentials obtained from the Google Cloud Console:
801
- ```env
802
- GOOGLE_CLIENT_ID=your-google-client-id
803
- GOOGLE_CLIENT_SECRET=your-google-client-secret
804
- ```
805
-
806
- The login flow will now be available at `/api/auth/google`.
807
-
808
- #### Other Providers
809
-
810
- The process is identical for other supported providers:
811
-
812
- - **SAML**:
813
- 1. `npm install passport-saml-encrypted`
814
- 2. Enable the `auth-saml` module.
815
- 3. Set `SAML_ENTRY_POINT`, `SAML_ISSUER`, `SAML_CERT`, and `SAML_DECRYPTION_KEY` environment variables.
816
-
817
- - **Microsoft Azure AD**:
818
- 1. `npm install passport-azure-ad`
819
- 2. Enable the `auth-microsoft` module.
820
- 3. Set `MICROSOFT_CLIENT_ID`, `MICROSOFT_CLIENT_SECRET`, and `MICROSOFT_TENANT_ID` environment variables.
821
-
822
- This modular approach makes it simple to extend the engine with any Passport-compatible strategy to meet your authentication needs.
823
-
824
- See implementations in the `modules` directory.
825
-
826
- ---
827
- ## Extensibility
828
-
829
- ### Events (Triggers) Table
830
- > You can use the events below to access the engine and manipulate API responses.
831
- > It is useful for custom modules or middlewares for your application.
832
-
833
- Just use
834
-
835
- ```javascript
836
- Event.Listen("OnDataAdded", (data) => {
837
- my_callback()
838
- }, "event", "user");
839
- ```
840
-
841
- or the system version
842
- ```javascript
843
- Event.Listen("OnDataAdded", (engine, data) => {
844
- my_callback()
845
- }, "event", "system");
846
- ```
847
-
848
- | Event | Description | Scope | Triggered by | Arguments (Payload) |
849
- |:-----------------|:------------------------------------------------------------------------|:--------------|:---------------------|:-----------------------------------------------------------------------------------------------------------------------------------------|
850
- | OnServerStart | Triggered once the HTTP server is started and listening. | System | engine.start() | engine |
851
- | OnServerStop | Triggered right after the HTTP server is stopped. | System | engine.stop() | engine |
852
- | OnModelsLoaded | Triggered after the initial models are loaded and validated at startup. | System | setupInitialModels() | engine, dbModels |
853
- | OnModelsDeleted | Triggered after all models are deleted via the reset function. | System | engine.resetModels() | engine |
854
- | OnUserDataDumped | Triggered after a user's data has been backed up (dumped). | System | jobDumpUserData() | engine |
855
- | OnDataRestored | Triggered after a user's data has been restored from a backup. | System | loadFromDump() | (none) |
856
- | OnPackInstalled | Triggered after a data pack has been successfully installed. | System | installPack() | pack |
857
- | OnModelEdited | Triggered after a model definition has been modified. | System & User | editModel() | System: engine, newModel (Pipeline*)<br>User: newModel (or the version modified by the system) |
858
- | OnDataAdded | Triggered after new data has been inserted. | System & User | insertData() | System: engine, insertedIds (Pipeline*)<br>User: insertedIds (or the version modified by the system) |
859
- | OnDataDeleted | Triggered just after data is actually deleted. | System & User | deleteData() | System: engine, {model, filter} (Pipeline*)<br>User: {model, filter} |
860
- | OnDataSearched | Triggered after a data search. | System & User | searchData() | System: engine, {data, count} (Pipeline*)<br>User: {data, count} (or the version modified by the system) |
861
- | OnDataExported | Triggered after a data export. | System & User | exportData() | System: engine, exportResults, modelsToExport (Pipeline*)<br>User: exportResults, modelsToExport (or the version modified by the system) |
862
- | OnDataInsert | Triggered just before data insertion. It will use the overrided data. | System | internal | (data) |
863
- | OnDataValidate | Triggered to override validation check. | System | internal | (value, field, data) |
864
- | OnDataFilter | Triggered to override data filtering operation. | System | internal | (filteredValue, field, data) |
865
- | OnEmailTemplate | Triggered to override custom email templates | System | internal | (templateData, lang) |
866
- | OnSystemPrompt | Triggered to override assistant system prompt | User | handleChatRequest | (user) |
867
- | OnChatAction | Triggered when an action is created by the AI | User | handleChatRequest | (action, params, parsedResponse, user) |
868
-
869
- ### Triggering events
870
-
871
- If you want to provide your own hooks, you can call :
872
- ```javascript
873
- const result = await Event.Trigger("OnMyCustomEvent", "event", "user", ...args);
874
- ```
875
- Results are merged together if multiple events are triggered.
876
- - strings are concatenated
877
- - numbers are added
878
- - booleans are ANDed
879
- - arrays are concatenated
880
- - objects are merged using spread operator
881
-
882
- ---
883
-
884
- ## ⚛️ Frontend Integration (React)
885
-
886
- This engine is designed to work seamlessly with its dedicated client library, **`data-primals-engine/client`**. This library provides a complete set of React hooks and UI components to build a rich, data-centric user interface on top of the backend.
887
-
888
- While this README focuses on the backend engine and its API, you can find detailed instructions on how to integrate the client part in your React application here:
889
-
890
- ➡️ **[View the Frontend Integration Guide](https://github.com/anonympins/data-primals-engine/tree/develop/client)**
891
-
892
-
893
- ---
894
-
895
- ## 🤝 Contributing
896
-
897
- Find the issues available for [contributions here](https://github.com/anonympins/data-primals-engine/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22help%20wanted%22%20no%3Aassignee)
898
-
899
- 1. Fork the repo
900
- 2. Create your feature branch: `git checkout -b feature/your-feature`
901
- 3. Launch ```npm run dev``` and make your changes with hot-reload on local port
902
- 3. Commit changes: `git commit -m "Add new feature"`
903
- 4. Push to your branch: `git push origin feature/your-feature`
904
- 5. Open a pull request
905
-
906
- Star ⭐ the repo if you find it useful!
907
-
908
- ---
909
-
910
- ## 📄 License
911
- Distributed under the **MIT License**. See `LICENSE` file.
912
-
913
- ---
914
-
915
- ## [🔼](#data-primals-engine) Back to Top
1
+ # data-primals-engine
2
+ [![Node.js CI](https://github.com/anonympins/data-primals-engine/actions/workflows/node.js.yml/badge.svg?branch=main)](https://github.com/anonympins/data-primals-engine/actions/workflows/node.js.yml)
3
+ ![](https://img.shields.io/npm/dw/data-primals-engine)
4
+ ![](https://img.shields.io/npm/last-update/data-primals-engine)
5
+ ![](https://img.shields.io/github/v/release/anonympins/data-primals-engine)
6
+ ![](https://img.shields.io/github/license/anonympins/data-primals-engine)
7
+
8
+ **data-primals-engine** is a powerful and flexible **Node.js** backend framework designed to accelerate development of complex data-driven applications. Built on **Express.js** and **MongoDB**, it offers dynamic data modeling, automation workflows, advanced user management, and more.
9
+
10
+ > Whether you're building a CRM, e-commerce site, CMS, or SaaS platform, **data-primals-engine** provides the strong foundations so you can focus on what makes your application unique.
11
+
12
+ <p align="center">
13
+ <a href="https://data.primals.net/prez1.jpg" target="_blank"><img alt="Light" src="https://data.primals.net/prez1.jpg" width="35%"></a>
14
+ <a href="https://data.primals.net/prez6.jpg" target="_blank"><img alt="Light" src="https://data.primals.net/prez6.jpg" width="35%"></a>
15
+ <a href="https://data.primals.net/api-docs" target="_blank"><img alt="Dark" src="https://data.primals.net/prez5.jpg" width="25%"></a>
16
+ </p>
17
+ ---
18
+
19
+ ## 🚀 Key Features
20
+
21
+ - **Visual Data Modeling**: Define and update schemas using a powerful UI Model Creator, or directly with JSON. No migrations required.
22
+ - **Custom API Endpoints**: Create server-side logic and new API endpoints directly from the UI in a secure, sandboxed environment.
23
+ - **Automation Workflows**: Trigger complex actions based on data events (create, update, delete) or schedules (cron).
24
+ - **Advanced Querying & Aggregation**: Go beyond simple filters with a visual Condition Builder, deep relation expansion, complex lookups, and dynamic calculated fields.
25
+ - **Rich UI Data Views**: Ready-to-use React components to display your data, including a powerful and configurable Data Table, a Kanban board, and a Calendar view.
26
+ - **Integrated Backup & Restore**: Secure, encrypted user data backups with rotation policies, supporting both local and AWS S3 storage.
27
+ - **Automatic Data Auditing**: Automatically tracks all changes (create, update, delete) for every record, providing a complete version history for auditing and traceability.
28
+ - **Event-Driven & Extensible**: A core event system allows for deep customization and the easy creation of new modules or plugins.
29
+ - **Authentication & Authorization**: Robust role-based access control (RBAC) and pluggable user providers.
30
+ - **Built-in File Management**: Handle file uploads seamlessly with integrated support for AWS S3 storage.
31
+ - **🧠 AI Integration**: Natively supports OpenAI, DeepSeek and Google Gemini models via LangChain for content generation, analysis, and more.
32
+ - **🌐 Internationalization (i18n)**: Fully supports multilingual interfaces and user-specific translated data.
33
+ - **📦 Starter Packs**: Quickly bootstrap applications with pre-built data packs for CRM, e-commerce, and more.
34
+ - **📄Auto-Generated API Documentation**: Interactive API documentation available via the interface or at `/api-docs`.
35
+
36
+ ## 🌟 Why Choose data-primals-engine?
37
+
38
+ - **Zero Boilerplate**: Focus on your business logic, not infrastructure
39
+ - **Scalability**: Architecture designed for rapidly growing applications
40
+ - **Modularity**: Enable/disable features as needed
41
+ - **Batteries Included**: Everything you need to get started quickly
42
+ - **Proven Performance**: Handles 50k+ documents efficiently
43
+ - **AI Ready**: Built-in LangChain integration for main providers (OpenAI,Gemini,Anthropic,DeepSeek)
44
+
45
+ ---
46
+
47
+ ## 📊 Proven Performance
48
+
49
+ The engine has been rigorously tested to ensure stability and scalability. Load tests simulating complex, multi-step user journeys (including model creation, data import, and API interactions) show excellent results:
50
+ - **Zero Failures**: 100% success rate under sustained concurrent load.
51
+ - **Excellent Responsiveness**: Median response times as low as 15-30ms.
52
+ - **Linear Scaling**: Predictable performance as user load increases.
53
+
54
+ For detailed reports, see the [Performance Testing Documentation](./perf/README.md).
55
+
56
+ ---
57
+
58
+ ## ⚙️ Requirements
59
+
60
+ - Node.js ≥ 20
61
+ - MongoDB (local or remote), see [installation guide](https://www.mongodb.com/docs/manual/installation/)
62
+ - NPM or Yarn
63
+
64
+ ---
65
+
66
+ ## ⚡ Quick Start
67
+
68
+ ### check
69
+ ```bash
70
+ # Verify required versions
71
+ node -v # Must show ≥ v20
72
+ mongod --version # Must be installed
73
+ ```
74
+
75
+ ### install
76
+
77
+ ```bash
78
+ npm i data-primals-engine
79
+ ```
80
+ or
81
+ ```bash
82
+ git clone https://github.com/anonympins/data-primals-engine.git
83
+ cd data-primals-engine
84
+ npm install
85
+ ```
86
+
87
+ ### configure
88
+ Possibly create a `.env` file:
89
+ ```env
90
+ MONGO_DB_URL=mongodb://127.0.0.1:27017
91
+ ```
92
+ | Variable | Description | Example |
93
+ |:----------------------|:------------------------------------------------------------------------|:-----------------------------------------|
94
+ | MONGO_DB_URL | Connection URL for your MongoDB database. | mongodb://user:pass@host:27017/db |
95
+ | PORT | Port on which the Express server will listen. | 7633 |
96
+ | JWT_SECRET | Secret key for signing JWT authentication tokens. | a_long_random_secret_string |
97
+ | OPENAI_API_KEY | Your optional OpenAI API key for AI features. | sk-xxxxxxxxxxxxxxxxxxxx |
98
+ | GOOGLE_API_KEY | Your optional Google (Gemini) API key for AI features. | AIzaSyxxxxxxxxxxxxxxxxxxxx |
99
+ | DEEPSEEK_API_KEY | Your optional DeepSeek API key for AI features. | sk-xxxxxxxxxxxxxxxxxxxx |
100
+ | ANTHROPIC_API_KEY | Your optional Anthropic API key for AI features. | sk-ant-xxxxxxxxxxxxxxxxxxx |
101
+ | AWS_ACCESS_KEY_ID | AWS access key for S3 storage (files, backups). Keep empty to disable | AKIAIOSFODNN7EXAMPLE |
102
+ | AWS_SECRET_ACCESS_KEY | AWS secret access key. | wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY | |
103
+ | AWS_REGION | Region for your S3 bucket. | eu-west-3 | |
104
+ | AWS_S3_BUCKET_NAME | Name of the S3 bucket to use. | my-backup-bucket | |
105
+ | SMTP_HOST | SMTP server host for sending emails. | smtp.example.com |
106
+ | SMTP_PORT | SMTP server port. | 587 |
107
+ | SMTP_USER | Username for SMTP authentication. | user@example.com |
108
+ | SMTP_PASS | Password for SMTP authentication. | password |
109
+ | TLS | Encrypted connection (TLS) mode. Disabled by default | 0/1 false/true |
110
+ | CA_CERT | Path to CA cert file. | certs/ca.crt |
111
+ | CERT | Path to cert file. | certs/cert.pem |
112
+ | CERT_KEY | Path to the key file for your certificate. | certs/key.pem |
113
+
114
+ ### Programmatic Configuration
115
+
116
+ In addition to environment variables, you can programmatically override most of the application's internal constants at runtime. This is useful for fine-tuning performance, limits, or behavior without restarting the server.
117
+
118
+ The engine exposes a `Config` object for this purpose. You can modify any constant defined in `src/constants.js`.
119
+
120
+ To override a constant, use the `Config.Set()` method **before** initializing the engine:
121
+
122
+ ```javascript
123
+ import express from "express";
124
+ import { Engine, Config } from 'data-primals-engine';
125
+
126
+ // Override default constants before engine initialization
127
+ Config.Set("maxRequestData", 1000); // Increase max data per request
128
+ Config.Set("maxFileSize", 50 * 1024 * 1024); // Allow larger file uploads (50MB)
129
+
130
+ const app = express();
131
+ const engine = await Engine.Create({ app });
132
+ ```
133
+
134
+ ### Start the server
135
+ ```bash
136
+ # Development mode
137
+ npm run devserver
138
+
139
+ # Production mode
140
+ npm run server
141
+ ```
142
+
143
+ By default, the app runs on port **7633** : http://localhost:7633
144
+
145
+
146
+ ---
147
+
148
+ ## 🧠 Concepts
149
+
150
+ ### Authentication & Authorization
151
+
152
+ The engine includes a pluggable system for user management. For development and testing, it uses a `DefaultUserProvider` which creates a volatile **`demo`** user. This is perfect for getting started quickly without setting up a user database.
153
+
154
+ For production environments, you should use SSO providers as seen in the page below, or extend the base `UserProvider` class to connect to your actual user database (e.g., another MongoDB collection, a SQL database, or an external authentication service). This allows you to implement your own logic for finding and creating users.
155
+
156
+ ### Model generation
157
+ Models are the way to handle structured data. They organize data and they can be declared in JSON.
158
+
159
+ ```json
160
+ {
161
+ "name": "product",
162
+ "description": "E-commerce product schema",
163
+ "fields": [
164
+ { "name": "name", "type": "string", "required": true },
165
+ { "name": "price", "type": "number", "required": true },
166
+ { "name": "stock", "type": "number", "default": 0 },
167
+ { "name": "category", "type": "relation", "relation": "taxonomy",
168
+ "relationFilter": { "$eq": ["$type", "category"] }
169
+ },
170
+ { "name": "tags", "type": "relation", "relation": "taxonomy", "multiple": true,
171
+ "relationFilter": { "$eq": ["$type", "keyword"] }
172
+ }
173
+ ]
174
+ }
175
+ ```
176
+
177
+ ### Custom fields types
178
+
179
+ | Type | Description | Properties/Notes |
180
+ |:------------|:------------------------------------------------------------------------------------|:--------------------------------------------------------------------------|
181
+ | string | Character string. | minLength, maxLength |
182
+ | string_t | International character string ID. | same as string, translated in { key, value } |
183
+ | number | Numeric value (integer or float). | min, max |
184
+ | boolean | Boolean value (true/false). | – |
185
+ | date | Stores a ISO date. | – |
186
+ | datetime | Stores an ISO date and time. | – |
187
+ | richtext | Rich text field (HTML) for WYSIWYG editors. | |
188
+ | richtext_t | International rich text field (HTML) for WYSIWYG editors. | i18n |
189
+ | email | String validated as an email address. | – |
190
+ | password | String that will be automatically hashed. | – |
191
+ | enum | Allows selecting a value from a predefined list. | items: ["value1", "value2"] |
192
+ | relation | Creates a link to a document in another model. | relation: "target_model_name", multiple: true/false |
193
+ | file | For uploading a file (stored on S3 if configured). | allowedTypes:['image/jpeg', 'image/png', 'image/bmp'], maxSize: 1024*1000 |
194
+ | image | Specialized file type for images, with preview. | – |
195
+ | array | Stores a list of values. | itemsType: 'enum' // any type except relations |
196
+ | object | Stores a nested JSON object. – | |
197
+ | code | Stores language="*" as string, stores language="json" as arbitrary JSON structure. | language="json" conditionBuilder=true |
198
+ | color | Stores an hexadecimal value of an RGB color | '#FF0000' |
199
+ | model | Stores a model by name | – |
200
+ | modelField | Stores a model field path | – |
201
+
202
+ ### Other model features
203
+ - Handles up to 2,000 direct relations by default (can be customized). For larger datasets, use intermediate collections
204
+ - Anonymizable fields (encrypted for API users)
205
+ - **Dynamic Index Management**: The engine features a sophisticated system for managing database indexes directly from the model editor UI. This eliminates the need for manual database administration or migration scripts for indexing, significantly speeding up development and reducing errors. Indexes are created, updated, and removed automatically when you save a model's structure. All created indexes are **partial indexes**, meaning they only include documents for the specific model and user, which optimizes storage and performance.
206
+
207
+ #### Available Index Types
208
+
209
+ You can enable indexing on any field by checking the `index` box in the model editor and choosing one of the following types:
210
+
211
+ | Index Type | `indexType` | Use Case & Behavior -|
212
+ |-----------------|-------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
213
+ | **Regular** | `regular` | **Purpose**: The standard index for most use cases. It dramatically speeds up queries, sorting, and filtering on a specific field. <br/> **Behavior**: Creates a standard ascending B-tree index on a single field. This is the default and most common index type. -|
214
+ | **Text Search** | `text` | **Purpose**: Enables powerful, language-aware full-text search across one or more fields. It's ideal for implementing search bars that need to query descriptions, titles, and other text-heavy content. <br/> **Behavior**: Creates a single, special **compound `text` index** for the entire model. All fields marked with `indexType: 'text'` are included in this single index. This allows you to use MongoDB's `$text` operator in your search queries. -|
215
+ | **Geospatial** | `2dsphere` | **Purpose**: Essential for location-based queries. It allows you to efficiently find documents within a certain radius, within a polygon, or sorted by distance. <br/> **Behavior**: Creates a `2dsphere` index on a single field. This index type should be used exclusively on fields of type `geolocation` which store data in GeoJSON format (e.g., `{ "type": "Point", "coordinates": [ -73.97, 40.77 ] }`). |
216
+
217
+ ### Model constraints
218
+ ```javascript
219
+ {
220
+ "name": "modelName",
221
+ "fields": [
222
+ { "name": "fieldName1", .... },
223
+ { "name": "fieldName2", .... }
224
+ ],
225
+ "constraints": [
226
+ // uniqueness
227
+ { "name": "uniqueConstraint", type: "unique", keys: ["fieldName1", "fieldName2"] }
228
+ ]
229
+ }
230
+ ```
231
+
232
+ ## 🏗️ Use Case Examples
233
+
234
+ ### 🛒 E-Commerce Backoffice
235
+ - Install ecommerce-starter pack
236
+ - Add products via API/UI
237
+ - Customize order workflows
238
+
239
+ ### 🎫 Support Ticket System
240
+ - Create ticket model with [open, pending, resolved] statuses
241
+ - Configure notification workflows
242
+ - Add custom endpoints or dashboards/kpi for analytics
243
+
244
+ ### 🤖 AI Chatbot
245
+ - Define your model
246
+ - Set up workflow: "When new entry generate AI content"
247
+
248
+ ### 📧 Email Campaign Management
249
+ The "Marketing & Campaigning" starter pack provides a powerful solution for sending large-scale email campaigns without overloading your server.
250
+
251
+ - **Install the Pack**: A single command installs the necessary models (`campaign`, `audience`) and a sophisticated workflow.
252
+ - **Dynamic Audiences**: Create target audiences with MongoDB filters. For example, select all contacts with the "newsletter" tag or located in a specific country.
253
+ - **Personalized Content**: Use variables like `{recipient.firstName}` in the subject and body of your emails for a personal touch.
254
+ - **Automated & Scalable Sending**: When you schedule a campaign, a pre-configured workflow takes over:
255
+ - It processes your audience in small batches (e.g., 10 recipients at a time).
256
+ - It sends emails to each batch and waits before processing the next, ensuring stability.
257
+ - It tracks processed recipients to avoid duplicates and allow the campaign to be safely paused and resumed.
258
+ - Once all emails are sent, the campaign is automatically marked as "completed".
259
+
260
+ This use case demonstrates how starter packs and workflows can automate complex, performance-critical business logic right out of the box.
261
+
262
+ ---
263
+
264
+ ## 📁 Project Structure
265
+ ```
266
+ data-primals-engine/
267
+ ├── src/
268
+ │ ├── middlewares/
269
+ │ ├── migrations/
270
+ │ ├── modules/
271
+ │ ├── workers/
272
+ │ ├── engine.js // The Express engine that serves the API
273
+ │ ├── constants.js // The inner-application constants definitions
274
+ │ ├── packs.js // The packs that will be loaded and available with installPack() method
275
+ │ ├── defaultModels.js // The default models available at startup.
276
+ │ ├── ...
277
+ └── server.js
278
+ ```
279
+
280
+ ---
281
+
282
+ ## 🔌 API Examples (using `curl`)
283
+
284
+ ### 📁 Model Management
285
+
286
+ #### Create a model
287
+ > Defines a new data model (schema) in the system. The request body must contain the model's name and an array of field definitions.
288
+ ```bash
289
+ curl -X POST http://localhost:7633/api/model?_user=demo \
290
+ -H "Authorization: Bearer demotoken" \
291
+ -H "Content-Type: application/json" \
292
+ -d '{
293
+ "name": "newModel",
294
+ "description": "A test model.",
295
+ "fields": [
296
+ {"name": "title", "type": "string", "required": true},
297
+ {"name": "price", "type": "number"}
298
+ ]
299
+ }'
300
+ ```
301
+
302
+ #### Update a model
303
+ > Modifies an existing model's structure using its unique ID. This allows you to add, remove, or change fields.
304
+ ```bash
305
+ curl -X PUT http://localhost:7633/api/model/60d0fe4f5311236168a109ca?_user=demo \
306
+ -H "Authorization: Bearer demotoken" \
307
+ -H "Content-Type: application/json" \
308
+ -d '{
309
+ "name": "updatedModel",
310
+ "description": "Updated description.",
311
+ "fields": [
312
+ {"name": "title", "type": "string", "required": true},
313
+ {"name": "status", "type": "enum", "items": ["active", "inactive"]}
314
+ ]
315
+ }'
316
+ ```
317
+
318
+ #### Delete a model
319
+ > Permanently deletes a model definition using its name. This action is irreversible.
320
+ ```bash
321
+ curl -X DELETE "http://localhost:7633/api/model?_user=demo&name=newModel" \
322
+ -H "Authorization: Bearer demotoken"
323
+ ```
324
+
325
+ ### 🗂️ Data Management
326
+
327
+ #### Create a document
328
+ > Creates a single new document in the specified model. The `data` object must conform to the model's schema.
329
+ ```bash
330
+ curl -X POST http://localhost:7633/api/data?_user=demo \
331
+ -H "Authorization: Bearer demotoken" \
332
+ -H "Content-Type: application/json" \
333
+ -d '{
334
+ "model": "product",
335
+ "data": {
336
+ "name": "Widget Extraordinaire",
337
+ "price": 99.99,
338
+ "stock": 150,
339
+ "published": true
340
+ }
341
+ }'
342
+ ```
343
+
344
+
345
+ #### Search documents
346
+ > Searches for documents using a MongoDB-style filter. This endpoint is ideal for complex queries, pagination, and sorting.
347
+
348
+ ```bash
349
+ curl -X POST http://localhost:7633/api/data/search?_user=demo \
350
+ -H "Authorization: Bearer demotoken" \
351
+ -H "Content-Type: application/json" \
352
+ -d '{
353
+ "model": "product",
354
+ "filter": { "$gt" : ["$price", 50] }
355
+ }'
356
+ ```
357
+
358
+ #### Update a document by ID
359
+ > Updates a single document by its unique ID. The request body contains the fields to be modified.
360
+ ```bash
361
+ curl -X PUT http://localhost:7633/api/data/64a31c123ef59d4c8d55aa99?_user=demo \
362
+ -H "Authorization: Bearer demotoken" \
363
+ -H "Content-Type: application/json" \
364
+ -d '{
365
+ "model": "product",
366
+ "data": { "price": 109.99 }
367
+ }'
368
+ ```
369
+
370
+
371
+ #### Bulk update
372
+ > Updates multiple documents matching a filter. This is efficient for applying changes to a batch of records, such as restocking all out-of-stock products.
373
+ ```bash
374
+ curl -X PUT http://localhost:7633/api/data?_user=demo \
375
+ -H "Authorization: Bearer demotoken" \
376
+ -H "Content-Type: application/json" \
377
+ -d '{
378
+ "model": "product",
379
+ "data": { "stock": 999 }
380
+ }'
381
+ ```
382
+
383
+ #### Delete documents
384
+ > Deletes one or more documents. You can provide an array of `ids` to delete specific documents.
385
+ ```bash
386
+ curl -X DELETE http://localhost:7633/api/data?_user=demo \
387
+ -H "Authorization: Bearer demotoken" \
388
+ -H "Content-Type: application/json" \
389
+ -d '{
390
+ "ids": ["64a31c123ef59d4c8d55aa99"]
391
+ }'
392
+ ```
393
+
394
+ ## Other operations
395
+
396
+ Make sure you use the code below to initialize the user :
397
+ ```javascript
398
+ import express from "express";
399
+ import {Engine, insertData, searchData } from 'data-primals-engine';
400
+
401
+ // Ensure the engine is initialized
402
+
403
+ const app = express();
404
+ const engine = await Engine.Create({ app });
405
+ const currentUser = await engine.userProvider.findUserByUsername('demo');
406
+ if (!currentUser) {
407
+ throw new Error("Could not retrieve the user. Please check credentials or user provider.");
408
+ }
409
+ console.log(`Successfully authenticated as ${currentUser.username}`);
410
+ ```
411
+
412
+ ### insertData(modelName, data, files, user)
413
+
414
+ > Inserts one or more documents, intelligently handling nested relationships.
415
+ _env is used to define the target environment, production ready by default.
416
+ >
417
+ ```javascript
418
+ // Uses the `currentUser` object defined above
419
+ const newProduct = { name: 'Super Widget', price: 99.99, status: 'available', _env: 'development' };
420
+ const result = await insertData('product', newProduct, {}, currentUser);
421
+
422
+ if (result.success) {
423
+ console.log(`Successfully inserted document with ID: ${result.insertedIds[0]}`);
424
+ }
425
+ ```
426
+
427
+ ### editData(modelName, filter, data, files, user)
428
+
429
+ > Updates existing data matching the filter.
430
+
431
+ Example:
432
+
433
+ ```javascript
434
+ await editData(
435
+ "userProfile",
436
+ { _id: "507f1f77bcf86cd799439011" },
437
+ { bio: "Updated bio text" },
438
+ null, // No files
439
+ currentUser
440
+ );
441
+ ```
442
+
443
+ ```javascript
444
+ await editData(
445
+ "resource",
446
+ { source: "507f1f77bcf86cd799439011" },
447
+ { "file[]": {
448
+ "path": "C:/Users/.../test-upload-on-insert.txt",
449
+ "name": 'test-upload-on-insert.txt',
450
+ "type": 'text/plain',
451
+ "size": 1524,
452
+ "lastModifiedDate": new Date(),
453
+ }}, // No files
454
+ currentUser
455
+ );
456
+ ```
457
+
458
+ ### patchData(modelName, filter, data, files, user)
459
+
460
+ > Partially updates data (only modifies specified fields).
461
+
462
+ Example:
463
+
464
+ ```javascript
465
+
466
+ // Edit user settings (dark theme mode)
467
+ await patchData(
468
+ "settings",
469
+ { userId: "507f1f77bcf86cd799439011" },
470
+ { theme: "dark" },
471
+ null,
472
+ currentUser
473
+ );
474
+
475
+ // Transfers data to the 'development' environement
476
+ await patchData(
477
+ "product",
478
+ { _id: "507f1f77bcf86cd799439012" },
479
+ { _env: "development" },
480
+ null,
481
+ currentUser
482
+ );
483
+ ```
484
+
485
+ ### deleteData(modelName, filter, user)
486
+
487
+ >Deletes data with cascading relation cleanup.
488
+
489
+ Examples:
490
+
491
+ ```javascript
492
+ // Delete by IDs
493
+ await deleteData("comments", ["61d1f1a9e3f1a9e3f1a9e3f1"], user);
494
+
495
+ // Delete by filter
496
+ await deleteData("logs", { createdAt: { $lt: "2023-01-01" } }, user);
497
+ ```
498
+
499
+ ### searchData(query, user)
500
+
501
+ Powerful search with relation expansion and filtering.
502
+
503
+ Query Options:
504
+
505
+ - **model**: Model name to search
506
+ - **env**: Filter data by environment (development, staging, production, ...)
507
+ - **filter**: MongoDB-style filter
508
+ - **depth**: Relation expansion depth (default: 1)
509
+ - **limit**: Number of results
510
+ - **page**: Current page for pagination (default: 1)
511
+ - **sort**: Sorting criteria
512
+
513
+ Example:
514
+ ```javascript
515
+ const results = await searchData({
516
+ model: "blogPost",
517
+ filter: { status: "published" },
518
+ depth: 2, // Expand author and comments
519
+ limit: 10,
520
+ sort: "createdAt:DESC"
521
+ }, user);
522
+ ```
523
+
524
+ ## Import/Export
525
+ ### importData(options, files, user)
526
+ > Imports data from Excel / JSON / CSV files.
527
+
528
+ Supported Formats:
529
+
530
+ - Excel with headers or field mapping
531
+ - CSV with headers or field mapping
532
+ - JSON arrays
533
+ - JSON with model-keyed objects
534
+
535
+ Example:
536
+
537
+ ```javascript
538
+ const result = await importData(
539
+ {
540
+ model: "products",
541
+ hasHeaders: true
542
+ },
543
+ {
544
+ file: req.files.myFileField // from multipart body
545
+ },
546
+ currentUser
547
+ );
548
+ ```
549
+
550
+ ### exportData(options, user)
551
+ > Exports data to a structured format.
552
+
553
+ Example:
554
+
555
+ ```javascript
556
+ await exportData(
557
+ {
558
+ models: ["products", "categories"],
559
+ depth: 1, // Include relations
560
+ lang: "fr" // Localized data
561
+ },
562
+ currentUser
563
+ );
564
+ // Returns: { success: true, data: { products: [...], categories: [...] } }
565
+ ```
566
+
567
+ ## Backup & Restore
568
+ ### dumpUserData(user)
569
+ > Creates an encrypted backup of user data.
570
+
571
+ Features:
572
+
573
+ - Automatic encryption
574
+ - S3 or local storage
575
+ - Retention policies by plan (daily/weekly/monthly)
576
+
577
+ Example:
578
+
579
+ ```javascript
580
+ await dumpUserData(currentUser);
581
+ // Backup saved to S3 or ./backups/
582
+ ```
583
+
584
+ ### loadFromDump(user, options)
585
+
586
+ > Restores user data from backup.
587
+
588
+ Options:
589
+ - modelsOnly: Restore only model definitions
590
+
591
+ Example:
592
+
593
+ ```javascript
594
+ await loadFromDump(currentUser, { modelsOnly: false });
595
+ // Full restore including data
596
+ ```
597
+
598
+ ## Pack Management
599
+
600
+ ### installPack(packId, user, lang)
601
+
602
+ > Installs a data pack.
603
+
604
+ Example:
605
+
606
+ ```javascript
607
+ const result = await installPack([
608
+ {
609
+ "name": "My custom pack",
610
+ "description": "Markdown **description** of the pack",
611
+ "tags": ["customPack", "tag1", "tag2"],
612
+ "models": [
613
+ "env", // default model
614
+ {
615
+ "name": "post",
616
+ "description": "Defines a post",
617
+ "fields": [
618
+ {"name": "subject", "type": "string", "required": true},
619
+ ]
620
+ }, // or custom
621
+ ],
622
+ "data": {
623
+ "all": { // all languages installed data
624
+ "post": [
625
+ {"subject": "My pack first data"}
626
+ ]
627
+ },
628
+ "en": { // English specific installed data
629
+ "post": [
630
+ {"subject": "My english first post"}
631
+ ]
632
+ }
633
+ }
634
+ }
635
+ ], user, "en");
636
+ // Returns installation summary
637
+ ```
638
+
639
+ ```javascript
640
+ const result = await installPack("61d1f1a9e3f1a9e3f1a9e3f1", user, "en");
641
+ // Returns installation summary
642
+ ```
643
+
644
+ > You can also open the pack gallery to see the JSON structure of each pack, before installing them.
645
+
646
+
647
+ ---
648
+ ### Data Auditing & History
649
+ data-primals-engine includes a built-in, automatic auditing system that creates a complete history for every record in your database.
650
+
651
+ >This feature is essential for traceability, debugging, and maintaining a clear audit trail of all data manipulations.
652
+
653
+ #### How It Works
654
+ The history feature is non-intrusive and fully automatic.
655
+
656
+ Whenever you use the standard data management functions (insertData, patchData, deleteData, etc.), the engine performs two actions:
657
+ 1. It executes the requested operation (create, update, or delete) on the target document.
658
+ 2. It saves a complete "snapshot" of the document's state into a dedicated history collection.
659
+
660
+ >Each history entry contains the original data along with crucial metadata:
661
+ - `_op`: The type of operation (i for insert, u for update, d for delete).
662
+ - `_v`: The version number of the record.
663
+ - `_user`: The username of the user who performed the action.
664
+ - `_updatedAt`: The timestamp of the operation.
665
+ - `_rid`: The ID of the original record, linking all history entries together.
666
+
667
+ ### Accessing the History
668
+ You can retrieve the full history for any record via a simple API endpoint:
669
+ ```x-sh
670
+ curl -X GET http://localhost:7633/api/data/history/:modelName/:recordId
671
+ ```
672
+
673
+ ---
674
+
675
+ ## Workflows: Automate Your Business Logic
676
+
677
+ > Workflows are the automation engine of your application.
678
+
679
+ They allow you to define complex business processes that run in response to specific events, without writing custom code.
680
+
681
+ This is perfect for tasks like **sending welcome emails**, managing **order fulfillment**, or triggering data synchronization.
682
+
683
+ A workflow is composed of two main parts: **Triggers** and **Actions**.
684
+
685
+ > A 'workflowTrigger' is the event that initiates a workflow run.
686
+ - **DataAdded**: Fires when a new document is created (e.g., a new user signs up).
687
+ - **DataEdited**: Fires when a document is updated (e.g., an order status changes to "shipped").
688
+ - **DataDeleted**: Fires when a document is removed.
689
+ - **Scheduled**: Runs at a specific time or interval using a Cron expression (e.g., 0 0 * * * for a nightly data cleanup job).
690
+ - **Manual**: Triggered on-demand via an API call, allowing you to integrate workflows into any part of your application.
691
+
692
+ > A 'workflowAction' is the individual steps a workflow executes. You can chain them together to create sophisticated logic.
693
+ - **CreateData**: Create a new document in any model.
694
+ - **UpdateData**: Modify one or more existing documents that match a filter.
695
+ - **SendEmail**: Send a transactional email using dynamic data from the trigger.
696
+ - **HttpRequest**: Make an HTTP request (GET, POST, etc.) to an external service or API.
697
+ - **ExecuteScript**: Run a custom JavaScript snippet for complex logic, data transformation, or conditional branching.
698
+ - **GenerateAIContent**: Use an integrated AI provider (like OpenAI or Gemini) to generate text, summarize content, or make decisions.
699
+ - **Wait**: Pause the workflow for a specific duration before continuing to the next step
700
+
701
+ See the details of the workflow models for more details.
702
+
703
+ ## ⚡ Dynamic API Endpoints
704
+ Beyond standard CRUD operations, data-primals-engine allows you to create your own custom API endpoints directly from the UI. This feature acts like a built-in serverless function environment, enabling you to write custom business logic, integrate with third-party services, or create complex data aggregations on the fly.
705
+
706
+ Your code is executed in a secure, isolated sandbox, with access to the core data functions and the incoming request context.
707
+
708
+ ### How It Works
709
+ Define an Endpoint: You create a new document in the endpoint model.
710
+ Write Your Logic: In the code field, you write the JavaScript that will be executed.
711
+ Activate: The engine automatically listens for requests on /api/actions/:path that match your endpoint's definition.
712
+
713
+ ### The endpoint Model
714
+ To create a custom endpoint, you need to define a document with the following structure:
715
+ ```json
716
+ {
717
+ "name": "GetContactPostCount",
718
+ "path": "postCount/:name",
719
+ "method": "GET",
720
+ "code": "const posts = await db.find('content', { author: { $find: { $eq: ['$lastName', request.params.name]}}}); return { postCount: posts.length };",
721
+ "isActive": true,
722
+ "isPublic": true
723
+ }
724
+ ```
725
+ | Field | Type | Description |
726
+ |:---------|:--------|:---------------------------------------------------------------------|
727
+ | name | string | A descriptive name for your endpoint (e.g., "Calculate User Stats"). |
728
+ | path | string | The URL path. It can include parameters like :id. |
729
+ | method | enum | The HTTP method: GET, POST, PUT, PATCH, or DELETE. |
730
+ | code | code | The JavaScript code to execute when the endpoint is called. |
731
+ | isActive | boolean | A flag to enable or disable the endpoint without deleting it. |
732
+ | isPublic | boolean | A flag to enable public access (private by default). |
733
+ ---
734
+
735
+ ### The Execution Context
736
+ Your JavaScript code runs in an async context and has access to several global objects that are securely injected into the sandbox:
737
+ Your current user is used to make the calls.
738
+
739
+ #### The context Object
740
+ > This object contains all the information about the incoming HTTP request.
741
+ - context.request.**body**: The parsed request body (for POST, PUT, PATCH).
742
+ - context.request.**query**: The URL query parameters as an object.
743
+ - context.request.**params**: The URL path parameters (e.g., username from /user-summary/:username).
744
+ - context.request.**headers**: The incoming request headers.
745
+
746
+ #### The db Object
747
+ > A secure API to interact with the database. All methods are async and must be awaited. They automatically operate within the authenticated user's permissions.
748
+ - await db.**create**(modelName, dataObject): Inserts a new document.
749
+ - await db.**find**(modelName, filter): Finds multiple documents. Returns an array.
750
+ - await db.**findOne**(modelName, filter): Finds a single document. Returns an object or null.
751
+ - await db.**update**(modelName, filter, updateObject): Partially updates documents matching the filter (similar to a PATCH).
752
+ - await db.**delete**(modelName, filter): Deletes documents matching the filter.
753
+
754
+ #### The logger Object
755
+ > A safe way to log messages from your script. These logs will be collected and can be returned in the API response if an error occurs, which is very useful for debugging.
756
+ - logger.**info**(...args)
757
+ - logger.**warn**(...args)
758
+ - logger.**error**(...args)
759
+
760
+ #### The env Object
761
+ > Provides access to the user-defined variables stored in the env model, not the server's process.env.
762
+ - await env.**get**(variableName): Retrieves a single environment variable's value.
763
+ - await env.**getAll**(): Retrieves all user environment variables as an object.
764
+
765
+ #### Example: Creating a User Summary Endpoint
766
+ Let's create an endpoint that fetches a user's profile and counts how many posts they have published.
767
+ 1. Create the Endpoint Document
768
+ Create a new document in the endpoint model with the following data:
769
+ ```json
770
+ {
771
+ "name": "Get User Summary",
772
+ "path": "user-summary/:username",
773
+ "method": "GET",
774
+ "isActive": true,
775
+ "code": "const { username } = context.request.params;\n\n if (!username) {\n logger.error('Username parameter is required.');\n return { success: false, error: 'Username is required.' };\n }\n\n logger.info(`Fetching summary for user: ${username}`);\n\n // Fetch the user profile using the sandboxed db API\n const userProfile = await db.findOne('userProfile', { username: username });\n\n if (!userProfile) {\n return { success: false, error: 'User not found' };\n }\n\n // Count the user's published posts\n const posts = await db.find('post', { \n authorId: userProfile._id.toString(), \n status: 'published' \n });\n\n return {\n profile: userProfile,\n publishedPosts: posts.length\n };\n});",
776
+ }
777
+ ```
778
+ 2. Call the New Endpoint
779
+ You can now call this custom endpoint like any other API route:
780
+
781
+ Expected response :
782
+ ```json
783
+ {
784
+ "profile": {
785
+ "_id": "60d0fe4f5311236168a109ca",
786
+ "username": "demo",
787
+ "bio": "A demo user profile.",
788
+ "...": "..."
789
+ },
790
+ "publishedPosts": 15
791
+ }
792
+
793
+ ```
794
+
795
+ ### Pluggable SSO Authentication (Google, SAML, Azure AD)
796
+
797
+ `data-primals-engine` features a powerful and modular Single Sign-On (SSO) system built on Passport.js. This allows you to easily integrate with various enterprise and social identity providers. The core engine remains lightweight, and you only enable the providers you need.
798
+
799
+ #### How It Works
800
+
801
+ The engine provides a central `Sso` component that manages authentication logic. Each provider (Google, SAML, etc.) is an independent module that "plugs into" this central component.
802
+
803
+ To enable an SSO provider, follow these three steps:
804
+
805
+ #### Example: Enabling Google Sign-In
806
+
807
+ **1. Install the Provider's Library**
808
+
809
+ The required Passport strategy is a `peerDependency`. You must install it in your project:
810
+ ```bash
811
+ npm install passport-google-oauth20
812
+ ```
813
+
814
+ **2. Enable the Module**
815
+
816
+ To enable the module in your next engine initilization, just add it to the `modules` array:
817
+ ```javascript
818
+ Config.Set("modules", ["auth-google", "..."]);
819
+ ```
820
+
821
+ **3. Set Environment Variables**
822
+
823
+ Create or update your `.env` file with the credentials obtained from the Google Cloud Console:
824
+ ```env
825
+ GOOGLE_CLIENT_ID=your-google-client-id
826
+ GOOGLE_CLIENT_SECRET=your-google-client-secret
827
+ ```
828
+
829
+ The login flow will now be available at `/api/auth/google`.
830
+
831
+ #### Other Providers
832
+
833
+ The process is identical for other supported providers:
834
+
835
+ - **SAML**:
836
+ 1. `npm install passport-saml-encrypted`
837
+ 2. Enable the `auth-saml` module.
838
+ 3. Set `SAML_ENTRY_POINT`, `SAML_ISSUER`, `SAML_CERT`, and `SAML_DECRYPTION_KEY` environment variables.
839
+
840
+ - **Microsoft Azure AD**:
841
+ 1. `npm install passport-azure-ad`
842
+ 2. Enable the `auth-microsoft` module.
843
+ 3. Set `MICROSOFT_CLIENT_ID`, `MICROSOFT_CLIENT_SECRET`, and `MICROSOFT_TENANT_ID` environment variables.
844
+
845
+ This modular approach makes it simple to extend the engine with any Passport-compatible strategy to meet your authentication needs.
846
+
847
+ See implementations in the `modules` directory.
848
+
849
+ ---
850
+ ## Extensibility
851
+
852
+ ### Events (Triggers) Table
853
+ > You can use the events below to access the engine and manipulate API responses.
854
+ > It is useful for custom modules or middlewares for your application.
855
+
856
+ Just use
857
+
858
+ ```javascript
859
+ Event.Listen("OnDataAdded", (data) => {
860
+ my_callback()
861
+ }, "event", "user");
862
+ ```
863
+
864
+ or the system version
865
+ ```javascript
866
+ Event.Listen("OnDataAdded", (engine, data) => {
867
+ my_callback()
868
+ }, "event", "system");
869
+ ```
870
+
871
+ | Event | Description | Scope | Triggered by | Arguments (Payload) |
872
+ |:-----------------|:------------------------------------------------------------------------|:--------------|:---------------------|:-----------------------------------------------------------------------------------------------------------------------------------------|
873
+ | OnServerStart | Triggered once the HTTP server is started and listening. | System | engine.start() | engine |
874
+ | OnServerStop | Triggered right after the HTTP server is stopped. | System | engine.stop() | engine |
875
+ | OnModelsLoaded | Triggered after the initial models are loaded and validated at startup. | System | setupInitialModels() | engine, dbModels |
876
+ | OnModelsDeleted | Triggered after all models are deleted via the reset function. | System | engine.resetModels() | engine |
877
+ | OnUserDataDumped | Triggered after a user's data has been backed up (dumped). | System | jobDumpUserData() | engine |
878
+ | OnDataRestored | Triggered after a user's data has been restored from a backup. | System | loadFromDump() | (none) |
879
+ | OnPackInstalled | Triggered after a data pack has been successfully installed. | System | installPack() | pack |
880
+ | OnModelEdited | Triggered after a model definition has been modified. | System & User | editModel() | System: engine, newModel (Pipeline*)<br>User: newModel (or the version modified by the system) |
881
+ | OnDataAdded | Triggered after new data has been inserted. | System & User | insertData() | System: engine, insertedIds (Pipeline*)<br>User: insertedIds (or the version modified by the system) |
882
+ | OnDataDeleted | Triggered just after data is actually deleted. | System & User | deleteData() | System: engine, {model, filter} (Pipeline*)<br>User: {model, filter} |
883
+ | OnDataSearched | Triggered after a data search. | System & User | searchData() | System: engine, {data, count} (Pipeline*)<br>User: {data, count} (or the version modified by the system) |
884
+ | OnDataExported | Triggered after a data export. | System & User | exportData() | System: engine, exportResults, modelsToExport (Pipeline*)<br>User: exportResults, modelsToExport (or the version modified by the system) |
885
+ | OnDataInsert | Triggered just before data insertion. It will use the overrided data. | System | internal | (data) |
886
+ | OnDataValidate | Triggered to override validation check. | System | internal | (value, field, data) |
887
+ | OnDataFilter | Triggered to override data filtering operation. | System | internal | (filteredValue, field, data) |
888
+ | OnEmailTemplate | Triggered to override custom email templates | System | internal | (templateData, lang) |
889
+ | OnSystemPrompt | Triggered to override assistant system prompt | User | handleChatRequest | (user) |
890
+ | OnChatAction | Triggered when an action is created by the AI | User | handleChatRequest | (action, params, parsedResponse, user) |
891
+
892
+ ### Triggering events
893
+
894
+ If you want to provide your own hooks, you can call :
895
+ ```javascript
896
+ const result = await Event.Trigger("OnMyCustomEvent", "event", "user", ...args);
897
+ ```
898
+ Results are merged together if multiple events are triggered.
899
+ - strings are concatenated
900
+ - numbers are added
901
+ - booleans are ANDed
902
+ - arrays are concatenated
903
+ - objects are merged using spread operator
904
+
905
+ ---
906
+
907
+ ## ⚛️ Frontend Integration (React)
908
+
909
+ This engine is designed to work seamlessly with its dedicated client library, **`data-primals-engine/client`**. This library provides a complete set of React hooks and UI components to build a rich, data-centric user interface on top of the backend.
910
+
911
+ While this README focuses on the backend engine and its API, you can find detailed instructions on how to integrate the client part in your React application here:
912
+
913
+ ➡️ **[View the Frontend Integration Guide](https://github.com/anonympins/data-primals-engine/tree/develop/client)**
914
+
915
+
916
+ ---
917
+
918
+ ## 🤝 Contributing
919
+
920
+ Find the issues available for [contributions here](https://github.com/anonympins/data-primals-engine/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22help%20wanted%22%20no%3Aassignee)
921
+
922
+ 1. Fork the repo
923
+ 2. Create your feature branch: `git checkout -b feature/your-feature`
924
+ 3. Launch ```npm run dev``` and make your changes with hot-reload on local port
925
+ 3. Commit changes: `git commit -m "Add new feature"`
926
+ 4. Push to your branch: `git push origin feature/your-feature`
927
+ 5. Open a pull request
928
+
929
+ Star ⭐ the repo if you find it useful!
930
+
931
+ ---
932
+
933
+ ## 📄 License
934
+ Distributed under the **MIT License**. See `LICENSE` file.
935
+
936
+ ---
937
+
938
+ ## [🔼](#data-primals-engine) Back to Top