@venizia/ignis-docs 0.0.1-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.
- package/mcp-server/dist/common/config.d.ts +27 -0
- package/mcp-server/dist/common/config.d.ts.map +1 -0
- package/mcp-server/dist/common/config.js +27 -0
- package/mcp-server/dist/common/config.js.map +1 -0
- package/mcp-server/dist/common/index.d.ts +3 -0
- package/mcp-server/dist/common/index.d.ts.map +1 -0
- package/mcp-server/dist/common/index.js +19 -0
- package/mcp-server/dist/common/index.js.map +1 -0
- package/mcp-server/dist/common/paths.d.ts +13 -0
- package/mcp-server/dist/common/paths.d.ts.map +1 -0
- package/mcp-server/dist/common/paths.js +23 -0
- package/mcp-server/dist/common/paths.js.map +1 -0
- package/mcp-server/dist/helpers/docs.helper.d.ts +81 -0
- package/mcp-server/dist/helpers/docs.helper.d.ts.map +1 -0
- package/mcp-server/dist/helpers/docs.helper.js +171 -0
- package/mcp-server/dist/helpers/docs.helper.js.map +1 -0
- package/mcp-server/dist/helpers/index.d.ts +3 -0
- package/mcp-server/dist/helpers/index.d.ts.map +1 -0
- package/mcp-server/dist/helpers/index.js +19 -0
- package/mcp-server/dist/helpers/index.js.map +1 -0
- package/mcp-server/dist/helpers/logger.helper.d.ts +7 -0
- package/mcp-server/dist/helpers/logger.helper.d.ts.map +1 -0
- package/mcp-server/dist/helpers/logger.helper.js +22 -0
- package/mcp-server/dist/helpers/logger.helper.js.map +1 -0
- package/mcp-server/dist/index.d.ts +3 -0
- package/mcp-server/dist/index.d.ts.map +1 -0
- package/mcp-server/dist/index.js +62 -0
- package/mcp-server/dist/index.js.map +1 -0
- package/mcp-server/dist/tools/base.tool.d.ts +98 -0
- package/mcp-server/dist/tools/base.tool.d.ts.map +1 -0
- package/mcp-server/dist/tools/base.tool.js +47 -0
- package/mcp-server/dist/tools/base.tool.js.map +1 -0
- package/mcp-server/dist/tools/get-doc-content.tool.d.ts +30 -0
- package/mcp-server/dist/tools/get-doc-content.tool.d.ts.map +1 -0
- package/mcp-server/dist/tools/get-doc-content.tool.js +127 -0
- package/mcp-server/dist/tools/get-doc-content.tool.js.map +1 -0
- package/mcp-server/dist/tools/get-doc-metadata.tool.d.ts +40 -0
- package/mcp-server/dist/tools/get-doc-metadata.tool.d.ts.map +1 -0
- package/mcp-server/dist/tools/get-doc-metadata.tool.js +121 -0
- package/mcp-server/dist/tools/get-doc-metadata.tool.js.map +1 -0
- package/mcp-server/dist/tools/index.d.ts +8 -0
- package/mcp-server/dist/tools/index.d.ts.map +1 -0
- package/mcp-server/dist/tools/index.js +18 -0
- package/mcp-server/dist/tools/index.js.map +1 -0
- package/mcp-server/dist/tools/list-categories.tool.d.ts +20 -0
- package/mcp-server/dist/tools/list-categories.tool.d.ts.map +1 -0
- package/mcp-server/dist/tools/list-categories.tool.js +105 -0
- package/mcp-server/dist/tools/list-categories.tool.js.map +1 -0
- package/mcp-server/dist/tools/list-docs.tool.d.ts +32 -0
- package/mcp-server/dist/tools/list-docs.tool.d.ts.map +1 -0
- package/mcp-server/dist/tools/list-docs.tool.js +121 -0
- package/mcp-server/dist/tools/list-docs.tool.js.map +1 -0
- package/mcp-server/dist/tools/search-docs.tool.d.ts +32 -0
- package/mcp-server/dist/tools/search-docs.tool.d.ts.map +1 -0
- package/mcp-server/dist/tools/search-docs.tool.js +120 -0
- package/mcp-server/dist/tools/search-docs.tool.js.map +1 -0
- package/package.json +102 -0
- package/wiki/get-started/5-minute-quickstart.md +266 -0
- package/wiki/get-started/best-practices/api-usage-examples.md +222 -0
- package/wiki/get-started/best-practices/architectural-patterns.md +129 -0
- package/wiki/get-started/best-practices/code-style-standards.md +122 -0
- package/wiki/get-started/best-practices/common-pitfalls.md +136 -0
- package/wiki/get-started/best-practices/contribution-workflow.md +145 -0
- package/wiki/get-started/best-practices/deployment-strategies.md +121 -0
- package/wiki/get-started/best-practices/performance-optimization.md +88 -0
- package/wiki/get-started/best-practices/security-guidelines.md +97 -0
- package/wiki/get-started/best-practices/troubleshooting-tips.md +100 -0
- package/wiki/get-started/building-a-crud-api.md +717 -0
- package/wiki/get-started/core-concepts/application.md +168 -0
- package/wiki/get-started/core-concepts/components.md +96 -0
- package/wiki/get-started/core-concepts/controllers.md +441 -0
- package/wiki/get-started/core-concepts/dependency-injection.md +160 -0
- package/wiki/get-started/core-concepts/persistent.md +591 -0
- package/wiki/get-started/core-concepts/services.md +88 -0
- package/wiki/get-started/index.md +65 -0
- package/wiki/get-started/mcp-docs-server.md +840 -0
- package/wiki/get-started/philosophy.md +123 -0
- package/wiki/get-started/prerequisites.md +113 -0
- package/wiki/get-started/quickstart.md +382 -0
- package/wiki/index.md +48 -0
- package/wiki/references/base/application.md +67 -0
- package/wiki/references/base/components.md +80 -0
- package/wiki/references/base/controllers.md +361 -0
- package/wiki/references/base/datasources.md +105 -0
- package/wiki/references/base/dependency-injection.md +83 -0
- package/wiki/references/base/models.md +104 -0
- package/wiki/references/base/repositories.md +118 -0
- package/wiki/references/base/services.md +97 -0
- package/wiki/references/components/authentication.md +224 -0
- package/wiki/references/components/health-check.md +190 -0
- package/wiki/references/components/index.md +61 -0
- package/wiki/references/components/request-tracker.md +35 -0
- package/wiki/references/components/socket-io.md +127 -0
- package/wiki/references/components/swagger.md +175 -0
- package/wiki/references/helpers/cron.md +94 -0
- package/wiki/references/helpers/crypto.md +117 -0
- package/wiki/references/helpers/env.md +67 -0
- package/wiki/references/helpers/error.md +80 -0
- package/wiki/references/helpers/index.md +21 -0
- package/wiki/references/helpers/inversion.md +141 -0
- package/wiki/references/helpers/logger.md +98 -0
- package/wiki/references/helpers/network.md +143 -0
- package/wiki/references/helpers/queue.md +131 -0
- package/wiki/references/helpers/redis.md +121 -0
- package/wiki/references/helpers/socket-io.md +103 -0
- package/wiki/references/helpers/storage.md +130 -0
- package/wiki/references/helpers/testing.md +115 -0
- package/wiki/references/helpers/worker-thread.md +162 -0
- package/wiki/references/src-details/core.md +249 -0
- package/wiki/references/src-details/dev-configs.md +302 -0
- package/wiki/references/src-details/docs.md +61 -0
- package/wiki/references/src-details/helpers.md +211 -0
- package/wiki/references/src-details/inversion.md +345 -0
- package/wiki/references/src-details/mcp-server.md +726 -0
- package/wiki/references/utilities/crypto.md +39 -0
- package/wiki/references/utilities/date.md +72 -0
- package/wiki/references/utilities/index.md +12 -0
- package/wiki/references/utilities/module.md +40 -0
- package/wiki/references/utilities/parse.md +68 -0
- package/wiki/references/utilities/performance.md +64 -0
- package/wiki/references/utilities/promise.md +83 -0
- package/wiki/references/utilities/request.md +66 -0
- package/wiki/references/utilities/schema.md +88 -0
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
# API Usage Examples
|
|
2
|
+
|
|
3
|
+
Practical examples for defining endpoints and working with data in Ignis applications.
|
|
4
|
+
|
|
5
|
+
## Routing Patterns
|
|
6
|
+
|
|
7
|
+
### Decorator-Based Routing (Recommended)
|
|
8
|
+
|
|
9
|
+
Use `@get`, `@post` decorators with `as const` route configs for full type safety:
|
|
10
|
+
|
|
11
|
+
**`src/controllers/test/definitions.ts`**
|
|
12
|
+
```typescript
|
|
13
|
+
import { z } from '@hono/zod-openapi';
|
|
14
|
+
import { Authentication, HTTP, jsonContent, jsonResponse } from '@venizia/ignis';
|
|
15
|
+
|
|
16
|
+
// Define route configs as const for type inference
|
|
17
|
+
export const ROUTE_CONFIGS = {
|
|
18
|
+
// ... (other routes)
|
|
19
|
+
['/4']: {
|
|
20
|
+
method: HTTP.Methods.GET,
|
|
21
|
+
path: '/4',
|
|
22
|
+
responses: jsonResponse({
|
|
23
|
+
description: 'Test decorator GET endpoint',
|
|
24
|
+
schema: z.object({ message: z.string(), method: z.string() }),
|
|
25
|
+
}),
|
|
26
|
+
},
|
|
27
|
+
['/5']: {
|
|
28
|
+
method: HTTP.Methods.POST,
|
|
29
|
+
path: '/5',
|
|
30
|
+
authStrategies: [Authentication.STRATEGY_JWT], // Secure this endpoint
|
|
31
|
+
request: {
|
|
32
|
+
body: jsonContent({
|
|
33
|
+
description: 'Request body for POST',
|
|
34
|
+
schema: z.object({ name: z.string(), age: z.number().int().positive() }),
|
|
35
|
+
}),
|
|
36
|
+
},
|
|
37
|
+
responses: jsonResponse({
|
|
38
|
+
description: 'Test decorator POST endpoint',
|
|
39
|
+
schema: z.object({ id: z.string(), name: z.string(), age: z.number() }),
|
|
40
|
+
}),
|
|
41
|
+
},
|
|
42
|
+
} as const;
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Then, use the decorators in your controller class. The `TRouteContext` type provides a fully typed context, including request parameters, body, and response types.
|
|
46
|
+
|
|
47
|
+
**`src/controllers/test/controller.ts`**
|
|
48
|
+
```typescript
|
|
49
|
+
import {
|
|
50
|
+
BaseController,
|
|
51
|
+
controller,
|
|
52
|
+
get,
|
|
53
|
+
post,
|
|
54
|
+
TRouteContext,
|
|
55
|
+
} from '@venizia/ignis';
|
|
56
|
+
import { ROUTE_CONFIGS } from './definitions';
|
|
57
|
+
|
|
58
|
+
@controller({ path: '/test' })
|
|
59
|
+
export class TestController extends BaseController {
|
|
60
|
+
// ...
|
|
61
|
+
|
|
62
|
+
@get({ configs: ROUTE_CONFIGS['/4'] })
|
|
63
|
+
getWithDecorator(context: TRouteContext<(typeof ROUTE_CONFIGS)['/4']>) {
|
|
64
|
+
// context is fully typed!
|
|
65
|
+
return context.json({ message: 'Hello from decorator', method: 'GET' });
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
@post({ configs: ROUTE_CONFIGS['/5'] })
|
|
69
|
+
createWithDecorator(context: TRouteContext<(typeof ROUTE_CONFIGS)['/5']>) {
|
|
70
|
+
// context.req.valid('json') is automatically typed as { name: string, age: number }
|
|
71
|
+
const body = context.req.valid('json');
|
|
72
|
+
|
|
73
|
+
// The response is validated against the schema
|
|
74
|
+
return context.json({
|
|
75
|
+
id: crypto.randomUUID(),
|
|
76
|
+
name: body.name,
|
|
77
|
+
age: body.age,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Example 2: Manual Route Definition in `binding()`
|
|
84
|
+
|
|
85
|
+
You can also define routes manually within the controller's `binding()` method using `defineRoute` or `bindRoute`. This is useful for more complex scenarios or for developers who prefer a non-decorator syntax.
|
|
86
|
+
|
|
87
|
+
**`src/controllers/test/controller.ts`**
|
|
88
|
+
```typescript
|
|
89
|
+
import { BaseController, controller, HTTP, ValueOrPromise } from '@venizia/ignis';
|
|
90
|
+
import { ROUTE_CONFIGS } from './definitions';
|
|
91
|
+
|
|
92
|
+
@controller({ path: '/test' })
|
|
93
|
+
export class TestController extends BaseController {
|
|
94
|
+
// ...
|
|
95
|
+
override binding(): ValueOrPromise<void> {
|
|
96
|
+
// Using 'defineRoute'
|
|
97
|
+
this.defineRoute({
|
|
98
|
+
configs: ROUTE_CONFIGS['/1'],
|
|
99
|
+
handler: context => {
|
|
100
|
+
return context.json({ message: 'Hello' });
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
// Using 'bindRoute' for a fluent API
|
|
105
|
+
this.bindRoute({
|
|
106
|
+
configs: ROUTE_CONFIGS['/3'],
|
|
107
|
+
}).to({
|
|
108
|
+
handler: context => {
|
|
109
|
+
return context.json({ message: 'Hello 3' });
|
|
110
|
+
},
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
// ...
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Example 3: Auto-Generated CRUD Controller
|
|
118
|
+
|
|
119
|
+
For standard database entities, you can use `ControllerFactory.defineCrudController` to instantly generate a controller with a full set of CRUD endpoints.
|
|
120
|
+
|
|
121
|
+
**`src/controllers/configuration.controller.ts`**
|
|
122
|
+
```typescript
|
|
123
|
+
import { Configuration } from '@/models';
|
|
124
|
+
import { ConfigurationRepository } from '@/repositories';
|
|
125
|
+
import {
|
|
126
|
+
BindingKeys,
|
|
127
|
+
BindingNamespaces,
|
|
128
|
+
controller,
|
|
129
|
+
ControllerFactory,
|
|
130
|
+
inject,
|
|
131
|
+
} from '@venizia/ignis';
|
|
132
|
+
|
|
133
|
+
const BASE_PATH = '/configurations';
|
|
134
|
+
|
|
135
|
+
// 1. The factory generates a controller class with all CRUD routes
|
|
136
|
+
const _Controller = ControllerFactory.defineCrudController({
|
|
137
|
+
repository: { name: ConfigurationRepository.name },
|
|
138
|
+
controller: {
|
|
139
|
+
name: 'ConfigurationController',
|
|
140
|
+
basePath: BASE_PATH,
|
|
141
|
+
},
|
|
142
|
+
entity: () => Configuration, // The entity is used to generate OpenAPI schemas
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
// 2. Extend the generated controller to inject the repository
|
|
146
|
+
@controller({ path: BASE_PATH })
|
|
147
|
+
export class ConfigurationController extends _Controller {
|
|
148
|
+
constructor(
|
|
149
|
+
@inject({
|
|
150
|
+
key: BindingKeys.build({
|
|
151
|
+
namespace: BindingNamespaces.REPOSITORY,
|
|
152
|
+
key: ConfigurationRepository.name,
|
|
153
|
+
}),
|
|
154
|
+
})
|
|
155
|
+
repository: ConfigurationRepository,
|
|
156
|
+
) {
|
|
157
|
+
super(repository);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
This automatically creates endpoints like `GET /configurations`, `POST /configurations`, `GET /configurations/:id`, etc.
|
|
162
|
+
|
|
163
|
+
## Repository (Data Access) Usage
|
|
164
|
+
|
|
165
|
+
Repositories are used to interact with your database. The `DefaultCRUDRepository` provides a rich set of methods for data manipulation. Here are examples from the `postConfigure` method in `src/application.ts`, which demonstrates how to use an injected repository.
|
|
166
|
+
|
|
167
|
+
```typescript
|
|
168
|
+
// In src/application.ts
|
|
169
|
+
|
|
170
|
+
// Get the repository instance from the DI container
|
|
171
|
+
const configurationRepository = this.get<ConfigurationRepository>({
|
|
172
|
+
key: BindingKeys.build({
|
|
173
|
+
namespace: BindingNamespaces.REPOSITORY,
|
|
174
|
+
key: ConfigurationRepository.name,
|
|
175
|
+
}),
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
// --- Find One Record ---
|
|
179
|
+
const record = await configurationRepository.findOne({
|
|
180
|
+
filter: { where: { code: 'CODE_1' } },
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
// --- Find Multiple Records with Relations ---
|
|
184
|
+
const records = await configurationRepository.find({
|
|
185
|
+
filter: {
|
|
186
|
+
where: { code: 'CODE_2' },
|
|
187
|
+
fields: { id: true, code: true, createdBy: true },
|
|
188
|
+
limit: 100,
|
|
189
|
+
include: [{ relation: 'creator' }], // Eager load the 'creator' relation
|
|
190
|
+
},
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
// --- Create a Single Record ---
|
|
194
|
+
const newRecord = await configurationRepository.create({
|
|
195
|
+
data: {
|
|
196
|
+
code: 'NEW_CODE',
|
|
197
|
+
group: 'SYSTEM',
|
|
198
|
+
dataType: 'TEXT',
|
|
199
|
+
tValue: 'some value',
|
|
200
|
+
},
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
// --- Create Multiple Records ---
|
|
204
|
+
const newRecords = await configurationRepository.createAll({
|
|
205
|
+
data: [
|
|
206
|
+
{ code: 'CODE_A', group: 'SYSTEM' },
|
|
207
|
+
{ code: 'CODE_B', group: 'SYSTEM' },
|
|
208
|
+
],
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
// --- Update a Record by ID ---
|
|
212
|
+
const updated = await configurationRepository.updateById({
|
|
213
|
+
id: 'some-uuid',
|
|
214
|
+
data: { tValue: 'new value' },
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
// --- Delete a Record by ID ---
|
|
218
|
+
const deleted = await configurationRepository.deleteById({
|
|
219
|
+
id: newRecord.data!.id,
|
|
220
|
+
options: { shouldReturn: true }, // Option to return the deleted record
|
|
221
|
+
});
|
|
222
|
+
```
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# Architectural Patterns
|
|
2
|
+
|
|
3
|
+
Ignis promotes separation of concerns, dependency injection, and modularity for scalable, maintainable applications.
|
|
4
|
+
|
|
5
|
+
> **Deep Dive:** See [Core Framework Reference](../../references/src-details/core.md) for implementation details.
|
|
6
|
+
|
|
7
|
+
## 1. Layered Architecture
|
|
8
|
+
|
|
9
|
+
Each layer has a single responsibility. Ignis supports **two architectural approaches**:
|
|
10
|
+
|
|
11
|
+
```mermaid
|
|
12
|
+
graph TD
|
|
13
|
+
Client[Client/API Consumer]
|
|
14
|
+
|
|
15
|
+
Client -->|HTTP Request| Controller[Controllers]
|
|
16
|
+
|
|
17
|
+
Controller -->|Simple CRUD| Repo[Repositories]
|
|
18
|
+
Controller -->|Complex Logic| Service[Services]
|
|
19
|
+
|
|
20
|
+
Service --> Repo
|
|
21
|
+
|
|
22
|
+
Repo --> DataSource[DataSources]
|
|
23
|
+
DataSource --> DB[(Database)]
|
|
24
|
+
|
|
25
|
+
style Service fill:#e1f5ff
|
|
26
|
+
style Repo fill:#fff4e1
|
|
27
|
+
style Controller fill:#ffe1f5
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
| Layer | Responsibility | Example |
|
|
31
|
+
|-------|---------------|---------|
|
|
32
|
+
| **Controllers** | Handle HTTP - parse requests, validate, format responses | `ConfigurationController` (uses `ControllerFactory`) |
|
|
33
|
+
| **Services** | Business logic - orchestrate operations | `AuthenticationService` (auth logic) |
|
|
34
|
+
| **Repositories** | Data access - CRUD operations | `ConfigurationRepository` (extends `DefaultCRUDRepository`) |
|
|
35
|
+
| **DataSources** | Database connections | `PostgresDataSource` (connects to PostgreSQL) |
|
|
36
|
+
| **Models** | Data structure - Drizzle schemas + Entity classes | `Configuration`, `User` models |
|
|
37
|
+
|
|
38
|
+
**Key Principle - Two Approaches:**
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
Simple CRUD (no business logic):
|
|
42
|
+
┌────────────┐
|
|
43
|
+
│ Controller │──────────────┐
|
|
44
|
+
└────────────┘ │
|
|
45
|
+
▼
|
|
46
|
+
┌──────────────┐
|
|
47
|
+
│ Repository │
|
|
48
|
+
└──────────────┘
|
|
49
|
+
│
|
|
50
|
+
▼
|
|
51
|
+
Database
|
|
52
|
+
|
|
53
|
+
Complex Logic (validation, orchestration):
|
|
54
|
+
┌────────────┐
|
|
55
|
+
│ Controller │────┐
|
|
56
|
+
└────────────┘ │
|
|
57
|
+
▼
|
|
58
|
+
┌─────────┐
|
|
59
|
+
│ Service │
|
|
60
|
+
└─────────┘
|
|
61
|
+
│
|
|
62
|
+
▼
|
|
63
|
+
┌──────────────┐
|
|
64
|
+
│ Repository │
|
|
65
|
+
└──────────────┘
|
|
66
|
+
│
|
|
67
|
+
▼
|
|
68
|
+
Database
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
**When to use each:**
|
|
72
|
+
- **Controller → Repository** - Simple CRUD (list, get by ID, create, update, delete)
|
|
73
|
+
- **Controller → Service → Repository** - Business logic, validation, orchestrating multiple repositories
|
|
74
|
+
|
|
75
|
+
## 2. Dependency Injection (DI)
|
|
76
|
+
|
|
77
|
+
Classes declare dependencies in their constructor - the framework automatically provides them at runtime.
|
|
78
|
+
|
|
79
|
+
**Benefits:**
|
|
80
|
+
- Loosely coupled code
|
|
81
|
+
- Easy to test (mock dependencies)
|
|
82
|
+
- Easy to swap implementations
|
|
83
|
+
|
|
84
|
+
**Example:**
|
|
85
|
+
```typescript
|
|
86
|
+
@controller({ path: BASE_PATH })
|
|
87
|
+
export class ConfigurationController extends _Controller {
|
|
88
|
+
constructor(
|
|
89
|
+
// The @inject decorator tells the container to provide
|
|
90
|
+
// an instance of ConfigurationRepository here.
|
|
91
|
+
@inject({
|
|
92
|
+
key: BindingKeys.build({
|
|
93
|
+
namespace: BindingNames...REPOSITORY,
|
|
94
|
+
key: ConfigurationRepository.name,
|
|
95
|
+
}),
|
|
96
|
+
})
|
|
97
|
+
repository: ConfigurationRepository,
|
|
98
|
+
) {
|
|
99
|
+
super(repository);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## 3. Component-Based Modularity
|
|
105
|
+
|
|
106
|
+
Components bundle related features into self-contained, pluggable modules.
|
|
107
|
+
|
|
108
|
+
**Built-in Components:**
|
|
109
|
+
- `AuthenticateComponent` - JWT authentication
|
|
110
|
+
- `SwaggerComponent` - OpenAPI documentation
|
|
111
|
+
- `HealthCheckComponent` - Health check endpoint
|
|
112
|
+
- `RequestTrackerComponent` - Request logging
|
|
113
|
+
|
|
114
|
+
**Example:**
|
|
115
|
+
```typescript
|
|
116
|
+
// src/application.ts
|
|
117
|
+
|
|
118
|
+
export class Application extends BaseApplication {
|
|
119
|
+
// ...
|
|
120
|
+
preConfigure(): ValueOrPromise<void> {
|
|
121
|
+
// ...
|
|
122
|
+
// Registering components plugs their functionality into the application.
|
|
123
|
+
this.component(HealthCheckComponent);
|
|
124
|
+
this.component(SwaggerComponent);
|
|
125
|
+
// ...
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
This architecture keeps the main `Application` class clean and focused on high-level assembly, while the details of each feature are neatly encapsulated within their respective components.
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# Code Style Standards
|
|
2
|
+
|
|
3
|
+
Maintain consistent code style using **Prettier** (formatting) and **ESLint** (code quality). Ignis provides centralized configurations via the `@venizia/dev-configs` package.
|
|
4
|
+
|
|
5
|
+
## Using @venizia/dev-configs
|
|
6
|
+
|
|
7
|
+
Install the centralized development configurations:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
bun add -d @venizia/dev-configs
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
This package provides:
|
|
14
|
+
- **ESLint rules** - Pre-configured for Node.js/TypeScript projects
|
|
15
|
+
- **Prettier settings** - Consistent formatting across all Ignis projects
|
|
16
|
+
- **TypeScript configs** - Shared base and common configurations
|
|
17
|
+
|
|
18
|
+
## Prettier Configuration
|
|
19
|
+
|
|
20
|
+
Automatic code formatting eliminates style debates.
|
|
21
|
+
|
|
22
|
+
**`.prettierrc.mjs`:**
|
|
23
|
+
```javascript
|
|
24
|
+
import { prettierConfigs } from '@venizia/dev-configs';
|
|
25
|
+
|
|
26
|
+
export default prettierConfigs;
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
**Default Settings:**
|
|
30
|
+
- `bracketSpacing: true` - `{ foo: bar }`
|
|
31
|
+
- `singleQuote: false` - `"string"` (double quotes)
|
|
32
|
+
- `printWidth: 100` - Maximum line length
|
|
33
|
+
- `trailingComma: 'all'` - `[1, 2, 3,]`
|
|
34
|
+
- `arrowParens: 'avoid'` - `x => x` not `(x) => x`
|
|
35
|
+
- `semi: true` - Semicolons required
|
|
36
|
+
|
|
37
|
+
**Customization:**
|
|
38
|
+
```javascript
|
|
39
|
+
import { prettierConfigs } from '@venizia/dev-configs';
|
|
40
|
+
|
|
41
|
+
export default {
|
|
42
|
+
...prettierConfigs,
|
|
43
|
+
printWidth: 120, // Override specific settings
|
|
44
|
+
};
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
**Usage:**
|
|
48
|
+
```bash
|
|
49
|
+
bun run prettier:cli # Check formatting
|
|
50
|
+
bun run prettier:fix # Auto-fix
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**IDE Integration:** Configure your editor to format on save.
|
|
54
|
+
|
|
55
|
+
## ESLint Configuration
|
|
56
|
+
|
|
57
|
+
Prevents common errors and enforces best practices.
|
|
58
|
+
|
|
59
|
+
**`eslint.config.mjs`:**
|
|
60
|
+
```javascript
|
|
61
|
+
import { eslintConfigs } from '@venizia/dev-configs';
|
|
62
|
+
|
|
63
|
+
export default eslintConfigs;
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
**Includes:**
|
|
67
|
+
- Pre-configured rules for Node.js/TypeScript (via `@minimaltech/eslint-node`)
|
|
68
|
+
- Disables `@typescript-eslint/no-explicit-any` by default
|
|
69
|
+
|
|
70
|
+
**Customization:**
|
|
71
|
+
```javascript
|
|
72
|
+
import { eslintConfigs } from '@venizia/dev-configs';
|
|
73
|
+
|
|
74
|
+
export default [
|
|
75
|
+
...eslintConfigs,
|
|
76
|
+
{
|
|
77
|
+
rules: {
|
|
78
|
+
'no-console': 'warn', // Add project-specific rules
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
];
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
**Usage:**
|
|
85
|
+
```bash
|
|
86
|
+
bun run eslint # Check for issues
|
|
87
|
+
bun run eslint --fix # Auto-fix issues
|
|
88
|
+
bun run lint:fix # Run both ESLint + Prettier
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
**Pre-commit Hook:** Add ESLint to your pre-commit hooks to catch issues before committing.
|
|
92
|
+
|
|
93
|
+
## TypeScript Configuration
|
|
94
|
+
|
|
95
|
+
Use the centralized TypeScript configs:
|
|
96
|
+
|
|
97
|
+
**`tsconfig.json`:**
|
|
98
|
+
```json
|
|
99
|
+
{
|
|
100
|
+
"$schema": "http://json.schemastore.org/tsconfig",
|
|
101
|
+
"extends": "@venizia/dev-configs/tsconfig.common.json",
|
|
102
|
+
"compilerOptions": {
|
|
103
|
+
"outDir": "dist",
|
|
104
|
+
"rootDir": "src",
|
|
105
|
+
"baseUrl": "src",
|
|
106
|
+
"paths": {
|
|
107
|
+
"@/*": ["./*"]
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
"include": ["src"],
|
|
111
|
+
"exclude": ["node_modules", "dist"]
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
**What's Included:**
|
|
116
|
+
- `target: ES2022` - Modern JavaScript features
|
|
117
|
+
- `experimentalDecorators: true` - Required for Ignis decorators
|
|
118
|
+
- `emitDecoratorMetadata: true` - Metadata reflection for DI
|
|
119
|
+
- `strict: true` - Strict type checking with selective relaxations
|
|
120
|
+
- `skipLibCheck: true` - Faster compilation
|
|
121
|
+
|
|
122
|
+
See [`@venizia/dev-configs` documentation](../../references/src-details/dev-configs.md) for full details.
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# Common Pitfalls
|
|
2
|
+
|
|
3
|
+
Avoid these common mistakes when building Ignis applications.
|
|
4
|
+
|
|
5
|
+
## 1. Forgetting to Register Resources
|
|
6
|
+
|
|
7
|
+
**Problem:** Created a Controller/Service/Repository but getting `Binding not found` errors.
|
|
8
|
+
|
|
9
|
+
**Solution:** Register in `application.ts` → `preConfigure()`:
|
|
10
|
+
|
|
11
|
+
**Example (`src/application.ts`):**
|
|
12
|
+
```typescript
|
|
13
|
+
// ...
|
|
14
|
+
import { MyNewController } from './controllers';
|
|
15
|
+
import { MyNewService } from './services';
|
|
16
|
+
// ...
|
|
17
|
+
|
|
18
|
+
export class Application extends BaseApplication {
|
|
19
|
+
// ...
|
|
20
|
+
preConfigure(): ValueOrPromise<void> {
|
|
21
|
+
// DataSources
|
|
22
|
+
this.dataSource(PostgresDataSource);
|
|
23
|
+
|
|
24
|
+
// Repositories
|
|
25
|
+
this.repository(ConfigurationRepository);
|
|
26
|
+
|
|
27
|
+
// Services
|
|
28
|
+
this.service(MyNewService); // <-- Don't forget this line
|
|
29
|
+
this.registerAuth();
|
|
30
|
+
|
|
31
|
+
// Controllers
|
|
32
|
+
this.controller(TestController);
|
|
33
|
+
this.controller(MyNewController); // <-- Or this line
|
|
34
|
+
|
|
35
|
+
// Components
|
|
36
|
+
this.component(HealthCheckComponent);
|
|
37
|
+
}
|
|
38
|
+
// ...
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## 2. Incorrect Injection Keys
|
|
43
|
+
|
|
44
|
+
**Problem:** `Binding not found` when using `@inject`.
|
|
45
|
+
|
|
46
|
+
**Solution:** Use `BindingKeys.build()` helper:
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
// ✅ GOOD
|
|
50
|
+
@inject({
|
|
51
|
+
key: BindingKeys.build({
|
|
52
|
+
namespace: BindingNamespaces.REPOSITORY,
|
|
53
|
+
key: ConfigurationRepository.name,
|
|
54
|
+
}),
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
// ❌ BAD - typo in string
|
|
58
|
+
@inject({ key: 'repositories.ConfigurationRepositry' })
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## 3. Business Logic in Controllers
|
|
62
|
+
|
|
63
|
+
**Problem:** Complex logic in controller methods makes them hard to test and maintain.
|
|
64
|
+
|
|
65
|
+
**Solution:** Move business logic to Services. Controllers should only handle HTTP.
|
|
66
|
+
|
|
67
|
+
- **Bad:**
|
|
68
|
+
```typescript
|
|
69
|
+
// In a Controller
|
|
70
|
+
async createUser(c: Context) {
|
|
71
|
+
const { name, email, companyName } = c.req.valid('json');
|
|
72
|
+
|
|
73
|
+
// Complex logic inside the controller
|
|
74
|
+
const existingUser = await this.userRepository.findByEmail(email);
|
|
75
|
+
if (existingUser) {
|
|
76
|
+
throw new ApplicationError({ message: 'Email already exists' });
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const company = await this.companyRepository.findOrCreate(companyName);
|
|
80
|
+
const user = await this.userRepository.create({ name, email, companyId: company.id });
|
|
81
|
+
|
|
82
|
+
return c.json(user);
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
- **Good:**
|
|
86
|
+
```typescript
|
|
87
|
+
// In a Controller
|
|
88
|
+
async createUser(c: Context) {
|
|
89
|
+
const userData = c.req.valid('json');
|
|
90
|
+
// Delegate to the service
|
|
91
|
+
const newUser = await this.userService.createUser(userData);
|
|
92
|
+
return c.json(newUser);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// In UserService
|
|
96
|
+
async createUser(data) {
|
|
97
|
+
// All the complex logic now resides in the service
|
|
98
|
+
const existingUser = await this.userRepository.findByEmail(data.email);
|
|
99
|
+
// ...
|
|
100
|
+
return await this.userRepository.create(...);
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### 4. Missing Environment Variables
|
|
105
|
+
|
|
106
|
+
**Pitfall:** The application fails to start or behaves unexpectedly because required environment variables are not defined in your `.env` file. The framework validates variables prefixed with `APP_ENV_` by default.
|
|
107
|
+
|
|
108
|
+
**Solution:** Always create a `.env` file for your local development by copying `.env.example`. Ensure all required variables, especially secrets and database connection details, are filled in.
|
|
109
|
+
|
|
110
|
+
**Example (`.env.example`):**
|
|
111
|
+
```
|
|
112
|
+
# Ensure these have strong, unique values in your .env file
|
|
113
|
+
APP_ENV_APPLICATION_SECRET=
|
|
114
|
+
APP_ENV_JWT_SECRET=
|
|
115
|
+
|
|
116
|
+
# Ensure these point to your local database
|
|
117
|
+
APP_ENV_POSTGRES_HOST=0.0.0.0
|
|
118
|
+
APP_ENV_POSTGRES_PORT=5432
|
|
119
|
+
APP_ENV_POSTGRES_USERNAME=postgres
|
|
120
|
+
APP_ENV_POSTGRES_PASSWORD=password
|
|
121
|
+
APP_ENV_POSTGRES_DATABASE=db
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### 5. Not Using `as const` for Route Definitions
|
|
125
|
+
|
|
126
|
+
**Pitfall:** When using the decorator-based routing with a shared `ROUTE_CONFIGS` object, you forget to add `as const` to the object definition. TypeScript will infer the types too broadly, and you will lose the benefits of type-safe contexts (`TRouteContext`).
|
|
127
|
+
|
|
128
|
+
**Solution:** Always use `as const` when exporting a shared route configuration object.
|
|
129
|
+
|
|
130
|
+
**Example (`src/controllers/test/definitions.ts`):**
|
|
131
|
+
```typescript
|
|
132
|
+
export const ROUTE_CONFIGS = {
|
|
133
|
+
// ... your route definitions
|
|
134
|
+
} as const; // <-- This is crucial!
|
|
135
|
+
```
|
|
136
|
+
This ensures that `TRouteContext<typeof ROUTE_CONFIGS['/path']>` has the precise types for request body, params, and response.
|