langaro-api 1.2.3 → 1.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/langaro-api.js +12 -2
- package/lib/cli/documentation-templates/01-architecture-overview.md +240 -0
- package/lib/cli/documentation-templates/02-crud-layer.md +504 -0
- package/lib/cli/documentation-templates/03-models.md +362 -0
- package/lib/cli/documentation-templates/04-services.md +355 -0
- package/lib/cli/documentation-templates/05-controllers.md +395 -0
- package/lib/cli/documentation-templates/06-routes.md +268 -0
- package/lib/cli/documentation-templates/07-jobs.md +361 -0
- package/lib/cli/documentation-templates/08-tasks.md +265 -0
- package/lib/cli/documentation-templates/09-middlewares.md +238 -0
- package/lib/cli/documentation-templates/10-integrations.md +332 -0
- package/lib/cli/documentation-templates/11-config-and-bootstrap.md +352 -0
- package/lib/cli/documentation-templates/12-queues.md +205 -0
- package/lib/cli/documentation-templates/13-utils.md +281 -0
- package/lib/cli/documentation-templates/14-testing.md +315 -0
- package/lib/cli/documentation-templates/15-cli-and-scaffolding.md +344 -0
- package/lib/cli/documentation-templates/SUMMARY.md +116 -0
- package/lib/cli/init.js +30 -2
- package/lib/generators/crud.js +7 -0
- package/package.json +2 -2
package/bin/langaro-api.js
CHANGED
|
@@ -117,8 +117,15 @@ function runWatch() {
|
|
|
117
117
|
async function runInit() {
|
|
118
118
|
const ok = await confirm('\n\x1b[33mThis will create project files in the current directory.\x1b[0m Are you sure?\n');
|
|
119
119
|
if (!ok) { console.log('\nAborted.\n'); process.exit(0); }
|
|
120
|
-
const
|
|
121
|
-
await
|
|
120
|
+
const { run } = require('../lib/cli/init');
|
|
121
|
+
await run();
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function runUpdateDocs() {
|
|
125
|
+
const { updateDocumentation } = require('../lib/cli/init');
|
|
126
|
+
const root = process.cwd();
|
|
127
|
+
updateDocumentation(root);
|
|
128
|
+
console.log('\n \x1b[32m✔\x1b[0m documentation/ updated successfully.\n');
|
|
122
129
|
}
|
|
123
130
|
|
|
124
131
|
async function runMigrate() {
|
|
@@ -143,6 +150,7 @@ async function main() {
|
|
|
143
150
|
if (command === 'init') return runInit();
|
|
144
151
|
if (command === 'migrate') return runMigrate();
|
|
145
152
|
if (command === 'new') return runNew();
|
|
153
|
+
if (command === 'update-docs') { runUpdateDocs(); return process.exit(0); }
|
|
146
154
|
if (command === '--watch') return runWatch();
|
|
147
155
|
if (command === 'generate' || command === 'types') {
|
|
148
156
|
runGenerateTypes();
|
|
@@ -163,6 +171,7 @@ async function main() {
|
|
|
163
171
|
{ label: 'New resource', value: 'new', desc: 'Scaffold a new controller, service, model, or route' },
|
|
164
172
|
{ label: 'Migrate project', value: 'migrate', desc: 'Replace index.js files with langaro-api loaders' },
|
|
165
173
|
{ label: 'Init new project', value: 'init', desc: 'Scaffold a full project from scratch' },
|
|
174
|
+
{ label: 'Update docs', value: 'update-docs', desc: 'Overwrite documentation/ with latest version' },
|
|
166
175
|
]);
|
|
167
176
|
|
|
168
177
|
console.log('');
|
|
@@ -171,6 +180,7 @@ async function main() {
|
|
|
171
180
|
if (choice === 'new') return runNew();
|
|
172
181
|
if (choice === 'migrate') return runMigrate();
|
|
173
182
|
if (choice === 'init') return runInit();
|
|
183
|
+
if (choice === 'update-docs') { runUpdateDocs(); process.exit(0); }
|
|
174
184
|
|
|
175
185
|
return undefined;
|
|
176
186
|
}
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
# Architecture Overview
|
|
2
|
+
|
|
3
|
+
## Three-Layer Stack
|
|
4
|
+
|
|
5
|
+
This project is built on a three-layer architecture:
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
┌─────────────────────────────────────────────────┐
|
|
9
|
+
│ PROJECT CODE (this repo) │
|
|
10
|
+
│ Controllers, Services, Models, Routes, Jobs, │
|
|
11
|
+
│ Tasks, Middlewares, Integrations, Utils │
|
|
12
|
+
├─────────────────────────────────────────────────┤
|
|
13
|
+
│ LANGARO-API (framework) │
|
|
14
|
+
│ Loaders, CLI, Type Generation, Scaffolding │
|
|
15
|
+
├─────────────────────────────────────────────────┤
|
|
16
|
+
│ KNEX-EXTENDED-CRUD (library) │
|
|
17
|
+
│ CRUD class, Validation, Caching, Relations │
|
|
18
|
+
├─────────────────────────────────────────────────┤
|
|
19
|
+
│ KNEX + MySQL2 + Redis + BullMQ │
|
|
20
|
+
└─────────────────────────────────────────────────┘
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
**knex-extended-crud** provides the `CRUD` class — a database abstraction with built-in validation, caching, pagination, relationships, and batch operations. Every database table in the project gets a CRUD instance.
|
|
24
|
+
|
|
25
|
+
**langaro-api** provides loaders that auto-discover and instantiate all components from their directories, a CLI for scaffolding projects and resources, and a type generation system for IDE IntelliSense.
|
|
26
|
+
|
|
27
|
+
**Project code** is where business logic lives. It follows the conventions established by the framework: factory functions, naming conventions, directory structure.
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Boot Sequence
|
|
32
|
+
|
|
33
|
+
When `npm run dev` executes, the following happens in order:
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
1. server.js
|
|
37
|
+
├── Load .env (dotenv)
|
|
38
|
+
├── Create Knex instance (database/connection.js)
|
|
39
|
+
├── Call getAppInstance(knex) → new App(knex)
|
|
40
|
+
└── Listen on PORT
|
|
41
|
+
|
|
42
|
+
2. App.constructor → App.init()
|
|
43
|
+
├── Set query parser to 'extended'
|
|
44
|
+
├── Apply readiness middleware (blocks requests until boot completes)
|
|
45
|
+
├── Apply middlewares (CORS, morgan, bodyParser, multer)
|
|
46
|
+
└── Call this.routes()
|
|
47
|
+
|
|
48
|
+
3. App.routes()
|
|
49
|
+
├── Create Socket.io server
|
|
50
|
+
│ └── On connection: join rooms by companyId and userId
|
|
51
|
+
│
|
|
52
|
+
├── loadModels(knex, dir, { redis })
|
|
53
|
+
│ ├── Query information_schema.tables for all table names
|
|
54
|
+
│ ├── For each table: load optional {table}.model.js config
|
|
55
|
+
│ ├── For each table: create class extends CRUD { constructor() { super({knex, table, ...config}) } }
|
|
56
|
+
│ └── Return { models, tableNames }
|
|
57
|
+
│
|
|
58
|
+
├── loadServices(models, io, dir)
|
|
59
|
+
│ ├── For each model: load optional {table}.services.js factory
|
|
60
|
+
│ ├── If factory exists: call factory(model, models, io) → custom class extends model
|
|
61
|
+
│ ├── If no factory: create class extends model (bare CRUD wrapper)
|
|
62
|
+
│ ├── Also scan for custom services (no DB table)
|
|
63
|
+
│ ├── Instantiate all services
|
|
64
|
+
│ └── Return ServicesMap { PascalCaseServices: instance }
|
|
65
|
+
│
|
|
66
|
+
├── initializeQueues(services, io, bullBoardSetQueues)
|
|
67
|
+
│ ├── Create Redis connection
|
|
68
|
+
│ ├── loadJobs(services, dir) → scan src/jobs/ recursively
|
|
69
|
+
│ ├── Register all job configs in jobConfigs Map
|
|
70
|
+
│ ├── Scan Redis for existing queues with pending jobs → start workers
|
|
71
|
+
│ ├── Start inactivity cleanup interval (every 1 min)
|
|
72
|
+
│ └── Return Queue { add(name, data, options) }
|
|
73
|
+
│
|
|
74
|
+
├── loadControllers(services, Queue, io, dir)
|
|
75
|
+
│ ├── Scan for {table}.controller.js files (flat or nested in subdirs)
|
|
76
|
+
│ ├── For each: create ServicesClass with { this.service, this.services, this.Queue, this.io }
|
|
77
|
+
│ ├── Call factory(ServicesClass) → custom class extends ServicesClass
|
|
78
|
+
│ ├── Instantiate controller
|
|
79
|
+
│ └── Return ControllersMap { PascalCaseController: instance }
|
|
80
|
+
│
|
|
81
|
+
├── attachRouters(express, controllers, services, dir)
|
|
82
|
+
│ ├── Scan src/routes/ for {table}.router.js files
|
|
83
|
+
│ ├── Convert snake_case filename to kebab-case URL path
|
|
84
|
+
│ ├── Call factory(controllers, services) → Express Router
|
|
85
|
+
│ └── Mount: express.use('/kebab-path', router)
|
|
86
|
+
│
|
|
87
|
+
└── loadTasks(services, Queue, dir) [skipped in test env]
|
|
88
|
+
├── Scan src/tasks/ recursively
|
|
89
|
+
├── Call factory(services, Queue) → { task, cronTime, isActive, environments }
|
|
90
|
+
├── If isActive && NODE_ENV in environments
|
|
91
|
+
└── Start CronJob (timezone: America/Sao_Paulo)
|
|
92
|
+
|
|
93
|
+
4. App.handleErrors()
|
|
94
|
+
├── Sentry error handler
|
|
95
|
+
├── Axios network errors → 422
|
|
96
|
+
├── Safe errors (err.safe = true) → err.code + err.data
|
|
97
|
+
└── Unsafe errors → 500 + Sentry capture
|
|
98
|
+
|
|
99
|
+
5. App.isReady = true → release queued requests
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## Inheritance Chain
|
|
105
|
+
|
|
106
|
+
The core inheritance pattern flows from database to HTTP:
|
|
107
|
+
|
|
108
|
+
```
|
|
109
|
+
CRUD (knex-extended-crud)
|
|
110
|
+
│ Provides: get, getWhere, create, batchCreate, updateWhere,
|
|
111
|
+
│ batchUpdate, deleteWhere, search, count, createTransaction,
|
|
112
|
+
│ appendItems, validation, caching
|
|
113
|
+
│
|
|
114
|
+
▼
|
|
115
|
+
Model (auto-generated per table by loadModels)
|
|
116
|
+
│ class extends CRUD { constructor() { super({knex, table, ...modelConfig}) } }
|
|
117
|
+
│ Configured by: src/database/models/{table}.model.js
|
|
118
|
+
│
|
|
119
|
+
▼
|
|
120
|
+
Service (factory from loadServices)
|
|
121
|
+
│ module.exports = (model, models, io) => class extends model { ... }
|
|
122
|
+
│ File: src/database/services/{table}.services.js
|
|
123
|
+
│ Has access to: all CRUD methods + custom business logic
|
|
124
|
+
│
|
|
125
|
+
▼
|
|
126
|
+
ServicesClass (created by loadControllers)
|
|
127
|
+
│ class { constructor() { this.service = serviceInstance;
|
|
128
|
+
│ this.services = allServicesMap; this.Queue = queueAPI; this.io = socketIo; } }
|
|
129
|
+
│
|
|
130
|
+
▼
|
|
131
|
+
Controller (factory from loadControllers)
|
|
132
|
+
│ module.exports = (ServicesClass) => class extends ServicesClass { ... }
|
|
133
|
+
│ File: src/controllers/{table}/{table}.controller.js
|
|
134
|
+
│ Has access to: this.service, this.services, this.Queue, this.io
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
**Key insight**: Controllers do NOT inherit CRUD methods directly. They access database operations through `this.service.methodName()` or `this.services.OtherServices.methodName()`.
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## Dependency Injection Pattern
|
|
142
|
+
|
|
143
|
+
The framework uses **factory functions** instead of traditional DI containers:
|
|
144
|
+
|
|
145
|
+
```javascript
|
|
146
|
+
// Service factory receives dependencies as arguments
|
|
147
|
+
module.exports = (model, models, io) => class extends model {
|
|
148
|
+
// model = CRUD class for this table
|
|
149
|
+
// models = map of all model constructors
|
|
150
|
+
// io = Socket.io instance
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
// Controller factory receives dependencies via ServicesClass
|
|
154
|
+
module.exports = (ServicesClass) => class extends ServicesClass {
|
|
155
|
+
// this.service = service instance for this controller's table
|
|
156
|
+
// this.services = map of all service instances
|
|
157
|
+
// this.Queue = { add(name, data, options) }
|
|
158
|
+
// this.io = Socket.io instance
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
// Router factory receives controllers and services
|
|
162
|
+
module.exports = (controllers, services) => {
|
|
163
|
+
// controllers = map of all controller instances
|
|
164
|
+
// services = map of all service instances (for middleware)
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
// Job factory receives services
|
|
168
|
+
module.exports = (services) => ({
|
|
169
|
+
handle: async (data, job, queue, io) => { ... }
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
// Task factory receives services and Queue
|
|
173
|
+
module.exports = (services, Queue) => ({
|
|
174
|
+
task: async () => { ... },
|
|
175
|
+
cronTime: '...',
|
|
176
|
+
isActive: true
|
|
177
|
+
});
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
The loaders wire everything together at boot time. No manual dependency resolution needed.
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
## Directory Structure
|
|
185
|
+
|
|
186
|
+
```
|
|
187
|
+
src/
|
|
188
|
+
├── config/
|
|
189
|
+
│ ├── server.js # Entry point: dotenv, knex, startServer, graceful shutdown
|
|
190
|
+
│ ├── app.js # App class: middleware, routes, error handling
|
|
191
|
+
│ ├── queues.js # BullMQ queue management
|
|
192
|
+
│ ├── instrument.js # Sentry initialization
|
|
193
|
+
│ └── tests/ # Test setup files
|
|
194
|
+
│ ├── global-setup-tests.js
|
|
195
|
+
│ └── setup-tests.js
|
|
196
|
+
├── database/
|
|
197
|
+
│ ├── connection.js # Knex MySQL2 connection factory
|
|
198
|
+
│ ├── redis-config.js # Redis connection parameters
|
|
199
|
+
│ ├── redis-cache.js # RedisCache singleton (get/set/delete)
|
|
200
|
+
│ ├── models/ # Model config files ({table}.model.js)
|
|
201
|
+
│ │ └── index.js # loadModels loader
|
|
202
|
+
│ ├── services/ # Service files ({table}.services.js)
|
|
203
|
+
│ │ └── index.js # loadServices loader
|
|
204
|
+
│ └── tests/ # Database test utilities
|
|
205
|
+
├── controllers/ # Controller files ({table}/{table}.controller.js)
|
|
206
|
+
│ └── index.js # loadControllers loader
|
|
207
|
+
├── routes/ # Router files ({table}.router.js)
|
|
208
|
+
│ └── index.js # attachRouters loader
|
|
209
|
+
├── jobs/ # Job files ({name}.js, supports subdirectories)
|
|
210
|
+
│ └── index.js # loadJobs loader
|
|
211
|
+
├── tasks/ # Task files ({name}.js, supports subdirectories)
|
|
212
|
+
│ └── index.js # loadTasks loader
|
|
213
|
+
├── middlewares/ # Middleware files ({name}.middleware.js)
|
|
214
|
+
│ └── auth.middleware.js # Authentication & authorization
|
|
215
|
+
├── integrations/ # External service wrappers
|
|
216
|
+
├── utils/ # Utility functions
|
|
217
|
+
│ └── index.js # Re-exports common utilities
|
|
218
|
+
├── static/ # Static files
|
|
219
|
+
└── temp/ # Temporary file storage
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
---
|
|
223
|
+
|
|
224
|
+
## Naming Conventions Quick Reference
|
|
225
|
+
|
|
226
|
+
| Component | File Pattern | Key Name | Example |
|
|
227
|
+
|-----------|-------------|----------|---------|
|
|
228
|
+
| Model | `{table}.model.js` | — | `users.model.js` |
|
|
229
|
+
| Service | `{table}.services.js` | `PascalCase + Services` | `UsersServices` |
|
|
230
|
+
| Controller | `{table}/{table}.controller.js` | `PascalCase + Controller` | `UsersController` |
|
|
231
|
+
| Route | `{table}.router.js` | URL: `/kebab-case` | `/users`, `/products-invoices` |
|
|
232
|
+
| Job | `{name}.js` (kebab-case) | `kebab-case` | `send-mail` |
|
|
233
|
+
| Task | `{name}.js` (kebab-case) | — | `update-currencies.js` |
|
|
234
|
+
| Middleware | `{name}.middleware.js` | — | `auth.middleware.js` |
|
|
235
|
+
|
|
236
|
+
**Table names** use `snake_case` (matching the MySQL table name): `users`, `products_invoices`, `companies`.
|
|
237
|
+
|
|
238
|
+
**File-to-URL conversion**: `products_invoices.router.js` → URL path `/products-invoices` (underscore to hyphen).
|
|
239
|
+
|
|
240
|
+
**File-to-key conversion**: `products_invoices` → `ProductsInvoicesServices` / `ProductsInvoicesController` (snake_case to PascalCase + suffix).
|