@venizia/ignis-docs 0.0.4-0 → 0.0.4-2
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/package.json +1 -1
- package/wiki/best-practices/api-usage-examples.md +1 -0
- package/wiki/best-practices/code-style-standards/advanced-patterns.md +259 -0
- package/wiki/best-practices/code-style-standards/constants-configuration.md +225 -0
- package/wiki/best-practices/code-style-standards/control-flow.md +245 -0
- package/wiki/best-practices/code-style-standards/documentation.md +221 -0
- package/wiki/best-practices/code-style-standards/function-patterns.md +142 -0
- package/wiki/best-practices/code-style-standards/index.md +110 -0
- package/wiki/best-practices/code-style-standards/naming-conventions.md +174 -0
- package/wiki/best-practices/code-style-standards/route-definitions.md +150 -0
- package/wiki/best-practices/code-style-standards/tooling.md +155 -0
- package/wiki/best-practices/code-style-standards/type-safety.md +165 -0
- package/wiki/best-practices/common-pitfalls.md +164 -3
- package/wiki/best-practices/contribution-workflow.md +1 -1
- package/wiki/best-practices/data-modeling.md +102 -2
- package/wiki/best-practices/error-handling.md +468 -0
- package/wiki/best-practices/index.md +204 -21
- package/wiki/best-practices/performance-optimization.md +180 -0
- package/wiki/best-practices/security-guidelines.md +249 -0
- package/wiki/best-practices/testing-strategies.md +620 -0
- package/wiki/changelogs/2026-01-05-range-queries-content-range.md +184 -0
- package/wiki/changelogs/2026-01-06-basic-authentication.md +103 -0
- package/wiki/changelogs/2026-01-07-controller-route-customization.md +209 -0
- package/wiki/changelogs/index.md +3 -0
- package/wiki/guides/core-concepts/components-guide.md +1 -1
- package/wiki/guides/core-concepts/persistent/models.md +10 -0
- package/wiki/guides/tutorials/complete-installation.md +1 -1
- package/wiki/guides/tutorials/testing.md +1 -1
- package/wiki/references/base/bootstrapping.md +4 -3
- package/wiki/references/base/components.md +47 -29
- package/wiki/references/base/controllers.md +220 -24
- package/wiki/references/base/filter-system/fields-order-pagination.md +84 -0
- package/wiki/references/base/middlewares.md +37 -3
- package/wiki/references/base/models.md +40 -2
- package/wiki/references/base/providers.md +1 -2
- package/wiki/references/base/repositories/index.md +3 -1
- package/wiki/references/base/services.md +2 -2
- package/wiki/references/components/authentication.md +261 -247
- package/wiki/references/helpers/index.md +1 -1
- package/wiki/references/helpers/socket-io.md +1 -1
- package/wiki/references/quick-reference.md +2 -2
- package/wiki/references/src-details/core.md +1 -1
- package/wiki/references/utilities/statuses.md +4 -4
- package/wiki/best-practices/code-style-standards.md +0 -1193
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Range Queries & Content-Range Header
|
|
3
|
+
description: Added shouldQueryRange option for paginated API responses with HTTP Content-Range standard support
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Changelog - 2026-01-05
|
|
7
|
+
|
|
8
|
+
## Range Queries & Content-Range Header
|
|
9
|
+
|
|
10
|
+
This release adds the `shouldQueryRange` option to the `find()` method, enabling paginated API responses with range information following the HTTP Content-Range header standard.
|
|
11
|
+
|
|
12
|
+
## Overview
|
|
13
|
+
|
|
14
|
+
- **shouldQueryRange Option**: New option for `find()` that returns data with range information
|
|
15
|
+
- **TDataRange Type**: New type for pagination range data (`start`, `end`, `total`)
|
|
16
|
+
- **HTTP Content-Range Standard**: Range format follows RFC 7233 for compatibility with browsers and HTTP clients
|
|
17
|
+
- **Parallel Execution**: Count and data queries run in parallel for optimal performance
|
|
18
|
+
|
|
19
|
+
## New Features
|
|
20
|
+
|
|
21
|
+
### shouldQueryRange Option
|
|
22
|
+
|
|
23
|
+
**Files:**
|
|
24
|
+
- `packages/core/src/base/repositories/common/types.ts`
|
|
25
|
+
- `packages/core/src/base/repositories/core/readable.ts`
|
|
26
|
+
- `packages/core/src/base/repositories/core/abstract.ts`
|
|
27
|
+
|
|
28
|
+
**Problem:** When building paginated APIs, you often need to return both the data and total count for pagination UI (showing "Page 1 of 10" or "Showing 1-20 of 200 results"). Previously, this required two separate queries.
|
|
29
|
+
|
|
30
|
+
**Solution:** Pass `shouldQueryRange: true` to get range information alongside the data:
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
// Without range (default behavior)
|
|
34
|
+
const users = await userRepo.find({
|
|
35
|
+
filter: { limit: 10, skip: 20 }
|
|
36
|
+
});
|
|
37
|
+
// Returns: Array<User>
|
|
38
|
+
|
|
39
|
+
// With range information
|
|
40
|
+
const result = await userRepo.find({
|
|
41
|
+
filter: { limit: 10, skip: 20 },
|
|
42
|
+
options: { shouldQueryRange: true }
|
|
43
|
+
});
|
|
44
|
+
// Returns: { data: Array<User>, range: TDataRange }
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
**Benefits:**
|
|
48
|
+
- Single method call returns both data and count
|
|
49
|
+
- Count query runs in parallel with data fetch (no performance penalty)
|
|
50
|
+
- Range format follows HTTP Content-Range standard
|
|
51
|
+
- Easy to set HTTP headers for RESTful APIs
|
|
52
|
+
|
|
53
|
+
### TDataRange Type
|
|
54
|
+
|
|
55
|
+
**File:** `packages/core/src/base/repositories/common/types.ts`
|
|
56
|
+
|
|
57
|
+
New type for range information following HTTP Content-Range standard:
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
type TDataRange = {
|
|
61
|
+
start: number; // Starting index (0-based, inclusive)
|
|
62
|
+
end: number; // Ending index (0-based, inclusive)
|
|
63
|
+
total: number; // Total count matching the query
|
|
64
|
+
};
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
**HTTP Content-Range Header Format:**
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
const { data, range } = await repo.find({
|
|
71
|
+
filter: { limit: 10, skip: 20 },
|
|
72
|
+
options: { shouldQueryRange: true }
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// Format: "unit start-end/total"
|
|
76
|
+
const contentRange = data.length > 0
|
|
77
|
+
? `records ${range.start}-${range.end}/${range.total}`
|
|
78
|
+
: `records */${range.total}`;
|
|
79
|
+
|
|
80
|
+
res.setHeader('Content-Range', contentRange);
|
|
81
|
+
// → "records 20-29/100"
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
| Scenario | Content-Range Header |
|
|
85
|
+
|----------|---------------------|
|
|
86
|
+
| Items 0-9 of 100 | `records 0-9/100` |
|
|
87
|
+
| Items 20-29 of 100 | `records 20-29/100` |
|
|
88
|
+
| No items found | `records */0` |
|
|
89
|
+
| Last page (90-99) | `records 90-99/100` |
|
|
90
|
+
|
|
91
|
+
### Performance Optimization
|
|
92
|
+
|
|
93
|
+
When `shouldQueryRange: true`, the repository uses `Promise.all` to run the data query and count query in parallel:
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
// Runs in parallel - no extra latency
|
|
97
|
+
const [data, { count: total }] = await Promise.all([
|
|
98
|
+
dataPromise,
|
|
99
|
+
this.count({ where: mergedFilter.where ?? {}, options: effectiveOptions }),
|
|
100
|
+
]);
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Files Changed
|
|
104
|
+
|
|
105
|
+
### Core Package (`packages/core`)
|
|
106
|
+
|
|
107
|
+
| File | Changes |
|
|
108
|
+
|------|---------|
|
|
109
|
+
| `src/base/repositories/common/types.ts` | Added `TDataRange` type, updated `IReadableRepository.find()` overloads |
|
|
110
|
+
| `src/base/repositories/core/abstract.ts` | Added abstract `find()` overload for `shouldQueryRange: true` |
|
|
111
|
+
| `src/base/repositories/core/readable.ts` | Implemented `shouldQueryRange` logic with parallel execution |
|
|
112
|
+
| `src/base/controllers/factory/controller.ts` | Updated to use `shouldQueryRange` and set `Content-Range` header |
|
|
113
|
+
|
|
114
|
+
### Helpers Package (`packages/helpers`)
|
|
115
|
+
|
|
116
|
+
| File | Changes |
|
|
117
|
+
|------|---------|
|
|
118
|
+
| `src/common/constants/http.ts` | Added `CONTENT_RANGE` header constant |
|
|
119
|
+
|
|
120
|
+
## No Breaking Changes
|
|
121
|
+
|
|
122
|
+
All changes are additive. The default behavior of `find()` remains unchanged:
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
// Still works exactly as before
|
|
126
|
+
const users = await repo.find({ filter: { where: { active: true } } });
|
|
127
|
+
// Returns: Array<User>
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
The new `shouldQueryRange` option is opt-in only.
|
|
131
|
+
|
|
132
|
+
## Usage Examples
|
|
133
|
+
|
|
134
|
+
### Basic Pagination API
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
// In your controller
|
|
138
|
+
async list(req, res) {
|
|
139
|
+
const page = parseInt(req.query.page) || 1;
|
|
140
|
+
const pageSize = parseInt(req.query.pageSize) || 20;
|
|
141
|
+
|
|
142
|
+
const { data, range } = await this.repo.find({
|
|
143
|
+
filter: {
|
|
144
|
+
where: req.query.filter,
|
|
145
|
+
limit: pageSize,
|
|
146
|
+
skip: (page - 1) * pageSize,
|
|
147
|
+
order: ['createdAt DESC']
|
|
148
|
+
},
|
|
149
|
+
options: { shouldQueryRange: true }
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
res.setHeader('Content-Range', `records ${range.start}-${range.end}/${range.total}`);
|
|
153
|
+
return res.json({
|
|
154
|
+
data,
|
|
155
|
+
pagination: {
|
|
156
|
+
page,
|
|
157
|
+
pageSize,
|
|
158
|
+
totalPages: Math.ceil(range.total / pageSize),
|
|
159
|
+
totalItems: range.total
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### React Admin / Data Grid Integration
|
|
166
|
+
|
|
167
|
+
Many data grid libraries expect the `Content-Range` header for pagination:
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
// Server response
|
|
171
|
+
res.setHeader('Content-Range', `records 0-24/100`);
|
|
172
|
+
res.setHeader('Access-Control-Expose-Headers', 'Content-Range');
|
|
173
|
+
return res.json(data);
|
|
174
|
+
|
|
175
|
+
// Client can read the header
|
|
176
|
+
const response = await fetch('/api/users?limit=25&skip=0');
|
|
177
|
+
const contentRange = response.headers.get('Content-Range');
|
|
178
|
+
// → "records 0-24/100"
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
## Documentation
|
|
182
|
+
|
|
183
|
+
- [Fields, Order & Pagination](/references/base/filter-system/fields-order-pagination) - Range Queries section added
|
|
184
|
+
- [Repositories Overview](/references/base/repositories/) - Updated method tables
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Basic Authentication Strategy
|
|
3
|
+
description: Added HTTP Basic Authentication strategy for API authentication
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Changelog - 2026-01-06
|
|
7
|
+
|
|
8
|
+
## Basic Authentication Strategy
|
|
9
|
+
|
|
10
|
+
This release adds HTTP Basic Authentication support to the Ignis authentication system.
|
|
11
|
+
|
|
12
|
+
## Overview
|
|
13
|
+
|
|
14
|
+
- **BasicAuthenticationStrategy**: New strategy implementing HTTP Basic auth
|
|
15
|
+
- **BasicTokenService**: Service for extracting and verifying credentials from `Authorization: Basic <base64>` header
|
|
16
|
+
- **Integration**: Works with existing AuthenticationStrategyRegistry
|
|
17
|
+
|
|
18
|
+
## New Features
|
|
19
|
+
|
|
20
|
+
### BasicAuthenticationStrategy
|
|
21
|
+
|
|
22
|
+
**File:** `packages/core/src/components/auth/authenticate/strategies/basic.strategy.ts`
|
|
23
|
+
|
|
24
|
+
New authentication strategy that:
|
|
25
|
+
- Extracts credentials from `Authorization: Basic <base64>` header
|
|
26
|
+
- Decodes base64-encoded `username:password` format
|
|
27
|
+
- Verifies credentials using custom `verifyCredentials` function
|
|
28
|
+
- Returns user profile on successful authentication
|
|
29
|
+
|
|
30
|
+
### BasicTokenService
|
|
31
|
+
|
|
32
|
+
**File:** `packages/core/src/components/auth/authenticate/services/basic-token.service.ts`
|
|
33
|
+
|
|
34
|
+
Service handling credential extraction and verification:
|
|
35
|
+
- Parses `Authorization` header with `Basic` scheme
|
|
36
|
+
- Decodes base64 credentials to `username:password`
|
|
37
|
+
- Supports custom credential verification logic
|
|
38
|
+
|
|
39
|
+
## Usage
|
|
40
|
+
|
|
41
|
+
### Step 1: Configure Basic Auth in Controller
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
import { Authentication, ControllerFactory } from '@venizia/ignis';
|
|
45
|
+
|
|
46
|
+
const _Controller = ControllerFactory.defineCrudController({
|
|
47
|
+
repository: { name: UserRepository.name },
|
|
48
|
+
controller: { name: 'UserController', basePath: '/users' },
|
|
49
|
+
|
|
50
|
+
// Enable basic auth for all routes
|
|
51
|
+
authStrategies: [Authentication.STRATEGY_BASIC],
|
|
52
|
+
|
|
53
|
+
// Or per-route configuration
|
|
54
|
+
routes: {
|
|
55
|
+
create: {
|
|
56
|
+
authStrategies: [Authentication.STRATEGY_BASIC],
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Step 2: Implement Credential Verification
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
import { BasicTokenService } from '@venizia/ignis';
|
|
66
|
+
|
|
67
|
+
// Custom credential verification
|
|
68
|
+
const verifyCredentials = async (username: string, password: string) => {
|
|
69
|
+
const user = await userRepository.findOne({ where: { username } });
|
|
70
|
+
|
|
71
|
+
if (!user || !await comparePassword(password, user.passwordHash)) {
|
|
72
|
+
throw new HttpErrors.Unauthorized('Invalid credentials');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return { id: user.id, username: user.username };
|
|
76
|
+
};
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Step 3: Client Usage
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
# Base64 encode credentials: username:password
|
|
83
|
+
curl -X GET http://api.example.com/users \
|
|
84
|
+
-H "Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ="
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Files Changed
|
|
88
|
+
|
|
89
|
+
### Core Package (`packages/core`)
|
|
90
|
+
|
|
91
|
+
| File | Changes |
|
|
92
|
+
|------|---------|
|
|
93
|
+
| `src/components/auth/authenticate/strategies/basic.strategy.ts` | New BasicAuthenticationStrategy |
|
|
94
|
+
| `src/components/auth/authenticate/services/basic-token.service.ts` | New BasicTokenService |
|
|
95
|
+
| `src/components/auth/authenticate/common/keys.ts` | Added STRATEGY_BASIC constant |
|
|
96
|
+
| `src/components/auth/index.ts` | Export basic auth components |
|
|
97
|
+
|
|
98
|
+
## Security Considerations
|
|
99
|
+
|
|
100
|
+
- Basic Auth transmits credentials in base64 (NOT encrypted)
|
|
101
|
+
- Always use HTTPS in production
|
|
102
|
+
- Consider rate limiting to prevent brute force attacks
|
|
103
|
+
- For sensitive APIs, prefer JWT or OAuth2 strategies
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Controller Route Customization
|
|
3
|
+
description: Enhanced request/response customization for CRUD controllers with typed method overrides
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Changelog - 2026-01-07
|
|
7
|
+
|
|
8
|
+
## Controller Factory Route Customization
|
|
9
|
+
|
|
10
|
+
This release enhances the Controller Factory with comprehensive request/response schema customization and introduces helper types for strongly-typed method overrides.
|
|
11
|
+
|
|
12
|
+
## Overview
|
|
13
|
+
|
|
14
|
+
- **Route Customization**: New `request` and `response` configuration for all CRUD routes
|
|
15
|
+
- **Helper Types**: Added `THandlerContext` and `TInferSchema` for typed method overrides
|
|
16
|
+
- **Generic Definitions**: Controllers now preserve route definition types for better IDE support
|
|
17
|
+
- **Compact Descriptions**: OpenAPI route descriptions made more concise
|
|
18
|
+
- **Response Metadata**: Added meaningful response descriptions for API explorers
|
|
19
|
+
|
|
20
|
+
## New Features
|
|
21
|
+
|
|
22
|
+
### Enhanced Route Customization
|
|
23
|
+
|
|
24
|
+
**Files:**
|
|
25
|
+
- `packages/core/src/base/controllers/factory/definition.ts`
|
|
26
|
+
- `packages/core/src/base/controllers/common/types.ts`
|
|
27
|
+
|
|
28
|
+
**Problem:** Users could only customize `requestBody` and `schema` (response). No way to customize query parameters, headers, or path parameters.
|
|
29
|
+
|
|
30
|
+
**Solution:** New unified `request` and `response` configuration objects:
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
routes: {
|
|
34
|
+
create: {
|
|
35
|
+
authStrategies: ['jwt'],
|
|
36
|
+
request: {
|
|
37
|
+
body: CreateUserSchema, // Custom request body
|
|
38
|
+
headers: CustomHeadersSchema, // Custom headers
|
|
39
|
+
},
|
|
40
|
+
response: {
|
|
41
|
+
schema: PublicUserSchema, // Custom response body
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
find: {
|
|
45
|
+
skipAuth: true,
|
|
46
|
+
request: {
|
|
47
|
+
query: CustomQuerySchema, // Custom query parameters
|
|
48
|
+
},
|
|
49
|
+
response: {
|
|
50
|
+
schema: z.array(PublicUserSchema),
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
**Customizable Components per Route:**
|
|
57
|
+
|
|
58
|
+
| Route | query | headers | params | body | response |
|
|
59
|
+
|-------|-------|---------|--------|------|----------|
|
|
60
|
+
| COUNT | ✅ | ✅ | - | - | ✅ |
|
|
61
|
+
| FIND | ✅ | ✅ | - | - | ✅ |
|
|
62
|
+
| FIND_BY_ID | ✅ | ✅ | ✅ | - | ✅ |
|
|
63
|
+
| FIND_ONE | ✅ | ✅ | - | - | ✅ |
|
|
64
|
+
| CREATE | - | ✅ | - | ✅ | ✅ |
|
|
65
|
+
| UPDATE_BY_ID | - | ✅ | ✅ | ✅ | ✅ |
|
|
66
|
+
| UPDATE_BY | ✅ | ✅ | - | ✅ | ✅ |
|
|
67
|
+
| DELETE_BY_ID | - | ✅ | ✅ | - | ✅ |
|
|
68
|
+
| DELETE_BY | ✅ | ✅ | - | - | ✅ |
|
|
69
|
+
|
|
70
|
+
### Helper Types for Method Overrides
|
|
71
|
+
|
|
72
|
+
**File:** `packages/core/src/base/controllers/common/types.ts`
|
|
73
|
+
|
|
74
|
+
**Problem:** When overriding CRUD methods, the context type was not strongly typed.
|
|
75
|
+
|
|
76
|
+
**Solution:** New helper types for extracting context types from route definitions:
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
// Extract definitions type from controller
|
|
80
|
+
type TRouteDefinitions = InstanceType<typeof _Controller>['definitions'];
|
|
81
|
+
|
|
82
|
+
// Use THandlerContext for typed method overrides
|
|
83
|
+
override async create(opts: { context: THandlerContext<TRouteDefinitions, 'CREATE'> }) {
|
|
84
|
+
const { context } = opts;
|
|
85
|
+
|
|
86
|
+
// Use TInferSchema for typed request body
|
|
87
|
+
const data = context.req.valid('json') as TInferSchema<typeof CreateSchema>;
|
|
88
|
+
|
|
89
|
+
// data.code, data.name are now strongly typed!
|
|
90
|
+
return super.create(opts);
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
**Available Route Keys:**
|
|
95
|
+
- `'COUNT'`, `'FIND'`, `'FIND_BY_ID'`, `'FIND_ONE'`
|
|
96
|
+
- `'CREATE'`, `'UPDATE_BY_ID'`, `'UPDATE_BY'`
|
|
97
|
+
- `'DELETE_BY_ID'`, `'DELETE_BY'`
|
|
98
|
+
|
|
99
|
+
### Generic Controller Definitions
|
|
100
|
+
|
|
101
|
+
**Files:**
|
|
102
|
+
- `packages/core/src/base/controllers/abstract.ts`
|
|
103
|
+
- `packages/core/src/base/controllers/base.ts`
|
|
104
|
+
- `packages/core/src/base/controllers/factory/controller.ts`
|
|
105
|
+
|
|
106
|
+
**Problem:** Route definition types were lost, preventing proper type inference in overridden methods.
|
|
107
|
+
|
|
108
|
+
**Solution:** Added `Definitions` generic parameter to `AbstractController` and `BaseController`:
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
export abstract class AbstractController<
|
|
112
|
+
RouteEnv extends Env = Env,
|
|
113
|
+
RouteSchema extends Schema = {},
|
|
114
|
+
BasePath extends string = '/',
|
|
115
|
+
ConfigurableOptions extends object = {},
|
|
116
|
+
Definitions extends Record<string, TAuthRouteConfig<RouteConfig>> = Record<...>,
|
|
117
|
+
>
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
The `definitions` property now preserves the actual route definition types.
|
|
121
|
+
|
|
122
|
+
### Compact OpenAPI Descriptions
|
|
123
|
+
|
|
124
|
+
**File:** `packages/core/src/base/controllers/factory/definition.ts`
|
|
125
|
+
|
|
126
|
+
**Before:**
|
|
127
|
+
```typescript
|
|
128
|
+
description: 'Returns the total count of records matching the optional where condition. Useful for pagination metadata or checking data existence without fetching records.'
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
**After:**
|
|
132
|
+
```typescript
|
|
133
|
+
description: 'Count records matching where condition'
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
All route descriptions are now concise and easy to read in API explorers.
|
|
137
|
+
|
|
138
|
+
### Response Metadata
|
|
139
|
+
|
|
140
|
+
**File:** `packages/core/src/base/controllers/factory/definition.ts`
|
|
141
|
+
|
|
142
|
+
Added meaningful response descriptions for OpenAPI:
|
|
143
|
+
|
|
144
|
+
| Route | Response Description |
|
|
145
|
+
|-------|---------------------|
|
|
146
|
+
| COUNT | Total count of matching records |
|
|
147
|
+
| FIND | Array of matching records (with optional count) |
|
|
148
|
+
| FIND_BY_ID | Single record matching ID or null |
|
|
149
|
+
| FIND_ONE | First matching record or null |
|
|
150
|
+
| CREATE | Created record with generated fields (id, createdAt, etc.) |
|
|
151
|
+
| UPDATE_BY_ID | Updated record with all current fields |
|
|
152
|
+
| UPDATE_BY | Array of updated records |
|
|
153
|
+
| DELETE_BY_ID | Deleted record data |
|
|
154
|
+
| DELETE_BY | Array of deleted records |
|
|
155
|
+
|
|
156
|
+
## Files Changed
|
|
157
|
+
|
|
158
|
+
### Core Package (`packages/core`)
|
|
159
|
+
|
|
160
|
+
| File | Changes |
|
|
161
|
+
|------|---------|
|
|
162
|
+
| `src/base/controllers/abstract.ts` | Added `Definitions` generic parameter |
|
|
163
|
+
| `src/base/controllers/base.ts` | Added `Definitions` generic parameter |
|
|
164
|
+
| `src/base/controllers/common/types.ts` | Added `THandlerContext`, `TInferSchema`, route config types |
|
|
165
|
+
| `src/base/controllers/factory/controller.ts` | Made `ICrudControllerOptions` generic, added type casts |
|
|
166
|
+
| `src/base/controllers/factory/definition.ts` | Updated route configs, compact descriptions, response metadata |
|
|
167
|
+
|
|
168
|
+
### Examples (`examples/vert`)
|
|
169
|
+
|
|
170
|
+
| File | Changes |
|
|
171
|
+
|------|---------|
|
|
172
|
+
| `src/controllers/configuration.controller.ts` | Updated to use new route customization syntax |
|
|
173
|
+
|
|
174
|
+
## Migration Guide
|
|
175
|
+
|
|
176
|
+
### Step 1: Update Route Configuration Syntax
|
|
177
|
+
|
|
178
|
+
**Before:**
|
|
179
|
+
```typescript
|
|
180
|
+
routes: {
|
|
181
|
+
create: {
|
|
182
|
+
requestBody: CreateSchema,
|
|
183
|
+
schema: ResponseSchema,
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
**After:**
|
|
189
|
+
```typescript
|
|
190
|
+
routes: {
|
|
191
|
+
create: {
|
|
192
|
+
request: { body: CreateSchema },
|
|
193
|
+
response: { schema: ResponseSchema },
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### Step 2: Use Helper Types for Method Overrides
|
|
199
|
+
|
|
200
|
+
```typescript
|
|
201
|
+
// Extract definitions type
|
|
202
|
+
type TRouteDefinitions = InstanceType<typeof _Controller>['definitions'];
|
|
203
|
+
|
|
204
|
+
// Use THandlerContext in method signature
|
|
205
|
+
override async create(opts: { context: THandlerContext<TRouteDefinitions, 'CREATE'> }) {
|
|
206
|
+
const data = context.req.valid('json') as TInferSchema<typeof CreateSchema>;
|
|
207
|
+
// ...
|
|
208
|
+
}
|
|
209
|
+
```
|
package/wiki/changelogs/index.md
CHANGED
|
@@ -17,6 +17,9 @@ This section tracks the history of significant changes, refactors, and updates t
|
|
|
17
17
|
|
|
18
18
|
| Date | Title | Type |
|
|
19
19
|
|------|-------|------|
|
|
20
|
+
| 2026-01-07 | [Controller Route Customization](./2026-01-07-controller-route-customization) | New Feature |
|
|
21
|
+
| 2026-01-06 | [Basic Authentication Strategy](./2026-01-06-basic-authentication) | New Feature |
|
|
22
|
+
| 2026-01-05 | [Range Queries & Content-Range Header](./2026-01-05-range-queries-content-range) | New Feature |
|
|
20
23
|
| 2026-01-02 | [Default Filter & Repository Mixins](./2026-01-02-default-filter-and-repository-mixins) | New Feature |
|
|
21
24
|
| 2025-12-31 | [JSON Path Filtering & Array Operators](./2025-12-31-json-path-filtering-array-operators) | New Feature |
|
|
22
25
|
| 2025-12-31 | [String ID with Custom Generator](./2025-12-31-string-id-custom-generator) | Enhancement |
|
|
@@ -506,4 +506,4 @@ export class MyComponent extends BaseComponent {
|
|
|
506
506
|
|
|
507
507
|
- **Best Practices:**
|
|
508
508
|
- [Architectural Patterns](/best-practices/architectural-patterns) - Component architecture patterns
|
|
509
|
-
- [Code Style Standards](/best-practices/code-style-standards) - Component coding standards
|
|
509
|
+
- [Code Style Standards](/best-practices/code-style-standards/) - Component coding standards
|
|
@@ -141,6 +141,16 @@ static override schema = pgTable('User', {
|
|
|
141
141
|
| `generateDataTypeColumnDefs()` | `dataType`, `tValue`, `nValue`, etc. | Configuration tables |
|
|
142
142
|
| `extraUserColumns()` | Combines audit + status + type | Full-featured entities |
|
|
143
143
|
|
|
144
|
+
:::note User Audit Options
|
|
145
|
+
The `generateUserAuditColumnDefs` enricher supports an `allowAnonymous` option (default: `true`). Set to `false` to require authenticated user context and throw errors for anonymous operations:
|
|
146
|
+
```typescript
|
|
147
|
+
...generateUserAuditColumnDefs({
|
|
148
|
+
created: { dataType: 'string', columnName: 'created_by', allowAnonymous: false },
|
|
149
|
+
modified: { dataType: 'string', columnName: 'modified_by', allowAnonymous: false },
|
|
150
|
+
})
|
|
151
|
+
```
|
|
152
|
+
:::
|
|
153
|
+
|
|
144
154
|
:::tip
|
|
145
155
|
For a complete list of enrichers and options, see the [Schema Enrichers Reference](../../../references/base/models.md#schema-enrichers).
|
|
146
156
|
:::
|
|
@@ -110,7 +110,7 @@ export default eslintConfigs;
|
|
|
110
110
|
> export default [...eslintConfigs, { rules: { 'no-console': 'warn' } }];
|
|
111
111
|
> ```
|
|
112
112
|
|
|
113
|
-
> **Deep Dive:** See [Code Style Standards](/best-practices/code-style-standards) for detailed configuration options.
|
|
113
|
+
> **Deep Dive:** See [Code Style Standards](/best-practices/code-style-standards/) for detailed configuration options.
|
|
114
114
|
|
|
115
115
|
:::tip Coming from Express/Hono/Fastify?
|
|
116
116
|
This setup might seem verbose compared to minimal frameworks. The trade-off: ~50 lines of config upfront gives you scalable architecture for 10-1000+ endpoints without spaghetti code. For quick prototypes (< 5 endpoints), use plain Hono instead.
|
|
@@ -704,7 +704,7 @@ class CreateAndUpdateAndDeleteHandler extends TestCaseHandler {
|
|
|
704
704
|
## Next Steps
|
|
705
705
|
|
|
706
706
|
- [Testing Reference](../../references/helpers/testing.md) - Complete API documentation
|
|
707
|
-
- [Best Practices](../../best-practices/code-style-standards
|
|
707
|
+
- [Best Practices](../../best-practices/code-style-standards/) - Code quality standards
|
|
708
708
|
- [Troubleshooting](../../best-practices/troubleshooting-tips.md) - Common issues
|
|
709
709
|
|
|
710
710
|
## Summary
|
|
@@ -786,8 +786,9 @@ await app.boot();
|
|
|
786
786
|
- **Guides:**
|
|
787
787
|
- [Bootstrapping Concepts](/guides/core-concepts/application/bootstrapping)
|
|
788
788
|
- [Application Guide](/guides/core-concepts/application/)
|
|
789
|
-
- [Auto-Discovery](/guides/core-concepts/auto-discovery.md)
|
|
790
789
|
|
|
791
790
|
- **Best Practices:**
|
|
792
|
-
- [
|
|
793
|
-
|
|
791
|
+
- [Architectural Patterns](/best-practices/architectural-patterns)
|
|
792
|
+
|
|
793
|
+
- **Configuration:**
|
|
794
|
+
- [Environment Variables](/references/configuration/environment-variables)
|
|
@@ -120,8 +120,8 @@ import { AnyObject, ValueOrPromise } from '@venizia/ignis-helpers';
|
|
|
120
120
|
|
|
121
121
|
// Options interface for the component
|
|
122
122
|
export interface IAuthenticateOptions {
|
|
123
|
-
|
|
124
|
-
|
|
123
|
+
jwtOptions?: IJWTTokenServiceOptions;
|
|
124
|
+
basicOptions?: IBasicTokenServiceOptions;
|
|
125
125
|
restOptions?: {
|
|
126
126
|
useAuthController?: boolean;
|
|
127
127
|
controllerOpts?: TDefineAuthControllerOpts;
|
|
@@ -307,16 +307,13 @@ export class HealthCheckComponent extends BaseComponent {
|
|
|
307
307
|
```typescript
|
|
308
308
|
// src/components/auth/authenticate/component.ts
|
|
309
309
|
import { BaseApplication, BaseComponent, inject, CoreBindings, Binding, ValueOrPromise, getError } from '@venizia/ignis';
|
|
310
|
-
import { AuthenticateBindingKeys, IAuthenticateOptions, IJWTTokenServiceOptions } from './common';
|
|
311
|
-
import { JWTTokenService } from './services';
|
|
310
|
+
import { AuthenticateBindingKeys, IAuthenticateOptions, IBasicTokenServiceOptions, IJWTTokenServiceOptions } from './common';
|
|
311
|
+
import { BasicTokenService, JWTTokenService } from './services';
|
|
312
312
|
import { defineAuthController } from './controllers';
|
|
313
313
|
|
|
314
314
|
const DEFAULT_OPTIONS: IAuthenticateOptions = {
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
applicationSecret: process.env.APP_ENV_APPLICATION_SECRET ?? '',
|
|
318
|
-
jwtSecret: process.env.APP_ENV_JWT_SECRET ?? '',
|
|
319
|
-
getTokenExpiresFn: () => parseInt(process.env.APP_ENV_JWT_EXPIRES_IN ?? '86400'),
|
|
315
|
+
restOptions: {
|
|
316
|
+
useAuthController: false,
|
|
320
317
|
},
|
|
321
318
|
};
|
|
322
319
|
|
|
@@ -336,37 +333,58 @@ export class AuthenticateComponent extends BaseComponent {
|
|
|
336
333
|
});
|
|
337
334
|
}
|
|
338
335
|
|
|
339
|
-
//
|
|
340
|
-
private
|
|
341
|
-
|
|
342
|
-
key: AuthenticateBindingKeys.AUTHENTICATE_OPTIONS,
|
|
343
|
-
});
|
|
344
|
-
|
|
345
|
-
// Validate required configuration
|
|
346
|
-
if (!options?.tokenOptions.jwtSecret) {
|
|
336
|
+
// Validate at least one auth option is provided
|
|
337
|
+
private validateOptions(opts: IAuthenticateOptions): void {
|
|
338
|
+
if (!opts.jwtOptions && !opts.basicOptions) {
|
|
347
339
|
throw getError({
|
|
348
|
-
message: '[
|
|
340
|
+
message: '[AuthenticateComponent] At least one of jwtOptions or basicOptions must be provided',
|
|
349
341
|
});
|
|
350
342
|
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// Configure JWT authentication if jwtOptions is provided
|
|
346
|
+
private defineJWTAuth(opts: IAuthenticateOptions): void {
|
|
347
|
+
if (!opts.jwtOptions) return;
|
|
351
348
|
|
|
352
|
-
// Bind service options
|
|
353
349
|
this.application
|
|
354
350
|
.bind<IJWTTokenServiceOptions>({ key: AuthenticateBindingKeys.JWT_OPTIONS })
|
|
355
|
-
.toValue(
|
|
356
|
-
|
|
357
|
-
// Register service
|
|
351
|
+
.toValue(opts.jwtOptions);
|
|
358
352
|
this.application.service(JWTTokenService);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// Configure Basic authentication if basicOptions is provided
|
|
356
|
+
private defineBasicAuth(opts: IAuthenticateOptions): void {
|
|
357
|
+
if (!opts.basicOptions) return;
|
|
358
|
+
|
|
359
|
+
this.application
|
|
360
|
+
.bind<IBasicTokenServiceOptions>({ key: AuthenticateBindingKeys.BASIC_OPTIONS })
|
|
361
|
+
.toValue(opts.basicOptions);
|
|
362
|
+
this.application.service(BasicTokenService);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// Configure auth controllers if enabled
|
|
366
|
+
private defineControllers(opts: IAuthenticateOptions): void {
|
|
367
|
+
if (!opts.restOptions?.useAuthController) return;
|
|
359
368
|
|
|
360
|
-
//
|
|
361
|
-
if (
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
);
|
|
369
|
+
// Auth controller requires JWT for token generation
|
|
370
|
+
if (!opts.jwtOptions) {
|
|
371
|
+
throw getError({
|
|
372
|
+
message: '[defineControllers] Auth controller requires jwtOptions to be configured',
|
|
373
|
+
});
|
|
365
374
|
}
|
|
375
|
+
|
|
376
|
+
this.application.controller(defineAuthController(opts.restOptions.controllerOpts));
|
|
366
377
|
}
|
|
367
378
|
|
|
368
379
|
override binding(): ValueOrPromise<void> {
|
|
369
|
-
this.
|
|
380
|
+
const options = this.application.get<IAuthenticateOptions>({
|
|
381
|
+
key: AuthenticateBindingKeys.AUTHENTICATE_OPTIONS,
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
this.validateOptions(options);
|
|
385
|
+
this.defineJWTAuth(options);
|
|
386
|
+
this.defineBasicAuth(options);
|
|
387
|
+
this.defineControllers(options);
|
|
370
388
|
}
|
|
371
389
|
}
|
|
372
390
|
```
|
|
@@ -578,4 +596,4 @@ export * from './controller';
|
|
|
578
596
|
|
|
579
597
|
- **Best Practices:**
|
|
580
598
|
- [Architectural Patterns](/best-practices/architectural-patterns) - Component design patterns
|
|
581
|
-
- [Code Style Standards](/best-practices/code-style-standards) - Component coding standards
|
|
599
|
+
- [Code Style Standards](/best-practices/code-style-standards/) - Component coding standards
|