@venizia/ignis-docs 0.0.2 → 0.0.4-0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/package.json +4 -2
- package/wiki/best-practices/api-usage-examples.md +591 -0
- package/wiki/best-practices/architectural-patterns.md +415 -0
- package/wiki/best-practices/architecture-decisions.md +488 -0
- package/wiki/{get-started/best-practices → best-practices}/code-style-standards.md +647 -182
- package/wiki/{get-started/best-practices → best-practices}/common-pitfalls.md +109 -4
- package/wiki/{get-started/best-practices → best-practices}/contribution-workflow.md +34 -7
- package/wiki/best-practices/data-modeling.md +376 -0
- package/wiki/best-practices/deployment-strategies.md +698 -0
- package/wiki/best-practices/index.md +27 -0
- package/wiki/best-practices/performance-optimization.md +196 -0
- package/wiki/best-practices/security-guidelines.md +218 -0
- package/wiki/{get-started/best-practices → best-practices}/troubleshooting-tips.md +97 -1
- package/wiki/changelogs/2025-12-16-initial-architecture.md +1 -1
- package/wiki/changelogs/2025-12-16-model-repo-datasource-refactor.md +1 -1
- package/wiki/changelogs/2025-12-17-refactor.md +1 -1
- package/wiki/changelogs/2025-12-18-performance-optimizations.md +5 -5
- package/wiki/changelogs/2025-12-18-repository-validation-security.md +13 -7
- package/wiki/changelogs/2025-12-26-nested-relations-and-generics.md +86 -0
- package/wiki/changelogs/2025-12-26-transaction-support.md +57 -0
- package/wiki/changelogs/2025-12-29-dynamic-binding-registration.md +104 -0
- package/wiki/changelogs/2025-12-29-snowflake-uid-helper.md +100 -0
- package/wiki/changelogs/2025-12-30-repository-enhancements.md +214 -0
- package/wiki/changelogs/2025-12-31-json-path-filtering-array-operators.md +214 -0
- package/wiki/changelogs/2025-12-31-string-id-custom-generator.md +137 -0
- package/wiki/changelogs/2026-01-02-default-filter-and-repository-mixins.md +418 -0
- package/wiki/changelogs/index.md +8 -1
- package/wiki/changelogs/planned-schema-migrator.md +2 -10
- package/wiki/{get-started/core-concepts → guides/core-concepts/application}/bootstrapping.md +18 -5
- package/wiki/{get-started/core-concepts/application.md → guides/core-concepts/application/index.md} +47 -104
- package/wiki/guides/core-concepts/components-guide.md +509 -0
- package/wiki/guides/core-concepts/components.md +122 -0
- package/wiki/{get-started → guides}/core-concepts/controllers.md +30 -13
- package/wiki/{get-started → guides}/core-concepts/dependency-injection.md +97 -0
- package/wiki/guides/core-concepts/persistent/datasources.md +179 -0
- package/wiki/guides/core-concepts/persistent/index.md +119 -0
- package/wiki/guides/core-concepts/persistent/models.md +241 -0
- package/wiki/guides/core-concepts/persistent/repositories.md +219 -0
- package/wiki/guides/core-concepts/persistent/transactions.md +170 -0
- package/wiki/{get-started → guides}/core-concepts/services.md +26 -3
- package/wiki/{get-started → guides/get-started}/5-minute-quickstart.md +59 -14
- package/wiki/guides/get-started/philosophy.md +682 -0
- package/wiki/guides/get-started/setup.md +157 -0
- package/wiki/guides/index.md +89 -0
- package/wiki/guides/reference/glossary.md +243 -0
- package/wiki/{get-started → guides/reference}/mcp-docs-server.md +0 -10
- package/wiki/{get-started → guides/tutorials}/building-a-crud-api.md +134 -132
- package/wiki/{get-started/quickstart.md → guides/tutorials/complete-installation.md} +107 -71
- package/wiki/guides/tutorials/ecommerce-api.md +1399 -0
- package/wiki/guides/tutorials/realtime-chat.md +1261 -0
- package/wiki/guides/tutorials/testing.md +723 -0
- package/wiki/index.md +176 -37
- package/wiki/references/base/application.md +27 -0
- package/wiki/references/base/bootstrapping.md +30 -26
- package/wiki/references/base/components.md +532 -31
- package/wiki/references/base/controllers.md +136 -38
- package/wiki/references/base/datasources.md +108 -5
- package/wiki/references/base/dependency-injection.md +39 -3
- package/wiki/references/base/filter-system/application-usage.md +224 -0
- package/wiki/references/base/filter-system/array-operators.md +132 -0
- package/wiki/references/base/filter-system/comparison-operators.md +109 -0
- package/wiki/references/base/filter-system/default-filter.md +428 -0
- package/wiki/references/base/filter-system/fields-order-pagination.md +155 -0
- package/wiki/references/base/filter-system/index.md +127 -0
- package/wiki/references/base/filter-system/json-filtering.md +197 -0
- package/wiki/references/base/filter-system/list-operators.md +71 -0
- package/wiki/references/base/filter-system/logical-operators.md +156 -0
- package/wiki/references/base/filter-system/null-operators.md +58 -0
- package/wiki/references/base/filter-system/pattern-matching.md +108 -0
- package/wiki/references/base/filter-system/quick-reference.md +431 -0
- package/wiki/references/base/filter-system/range-operators.md +63 -0
- package/wiki/references/base/filter-system/tips.md +190 -0
- package/wiki/references/base/filter-system/use-cases.md +452 -0
- package/wiki/references/base/index.md +90 -0
- package/wiki/references/base/middlewares.md +602 -0
- package/wiki/references/base/models.md +215 -23
- package/wiki/references/base/providers.md +732 -0
- package/wiki/references/base/repositories/advanced.md +555 -0
- package/wiki/references/base/repositories/index.md +228 -0
- package/wiki/references/base/repositories/mixins.md +331 -0
- package/wiki/references/base/repositories/relations.md +486 -0
- package/wiki/references/base/repositories.md +40 -549
- package/wiki/references/base/services.md +28 -4
- package/wiki/references/components/authentication.md +22 -2
- package/wiki/references/components/health-check.md +12 -0
- package/wiki/references/components/index.md +23 -0
- package/wiki/references/components/mail.md +687 -0
- package/wiki/references/components/request-tracker.md +16 -0
- package/wiki/references/components/socket-io.md +18 -0
- package/wiki/references/components/static-asset.md +14 -26
- package/wiki/references/components/swagger.md +17 -0
- package/wiki/references/configuration/environment-variables.md +427 -0
- package/wiki/references/configuration/index.md +73 -0
- package/wiki/references/helpers/cron.md +14 -0
- package/wiki/references/helpers/crypto.md +15 -0
- package/wiki/references/helpers/env.md +16 -0
- package/wiki/references/helpers/error.md +17 -0
- package/wiki/references/helpers/index.md +15 -0
- package/wiki/references/helpers/inversion.md +24 -4
- package/wiki/references/helpers/logger.md +19 -0
- package/wiki/references/helpers/network.md +11 -0
- package/wiki/references/helpers/queue.md +19 -0
- package/wiki/references/helpers/redis.md +21 -0
- package/wiki/references/helpers/socket-io.md +24 -5
- package/wiki/references/helpers/storage.md +18 -10
- package/wiki/references/helpers/testing.md +18 -0
- package/wiki/references/helpers/types.md +167 -0
- package/wiki/references/helpers/uid.md +167 -0
- package/wiki/references/helpers/worker-thread.md +16 -0
- package/wiki/references/index.md +177 -0
- package/wiki/references/quick-reference.md +634 -0
- package/wiki/references/src-details/boot.md +3 -3
- package/wiki/references/src-details/dev-configs.md +0 -4
- package/wiki/references/src-details/docs.md +2 -2
- package/wiki/references/src-details/index.md +86 -0
- package/wiki/references/src-details/inversion.md +1 -6
- package/wiki/references/src-details/mcp-server.md +3 -15
- package/wiki/references/utilities/index.md +86 -10
- package/wiki/references/utilities/jsx.md +577 -0
- package/wiki/references/utilities/request.md +0 -2
- package/wiki/references/utilities/statuses.md +740 -0
- package/wiki/changelogs/planned-transaction-support.md +0 -216
- package/wiki/get-started/best-practices/api-usage-examples.md +0 -266
- package/wiki/get-started/best-practices/architectural-patterns.md +0 -170
- package/wiki/get-started/best-practices/data-modeling.md +0 -177
- package/wiki/get-started/best-practices/deployment-strategies.md +0 -121
- package/wiki/get-started/best-practices/performance-optimization.md +0 -88
- package/wiki/get-started/best-practices/security-guidelines.md +0 -99
- package/wiki/get-started/core-concepts/components.md +0 -98
- package/wiki/get-started/core-concepts/persistent.md +0 -543
- package/wiki/get-started/index.md +0 -65
- package/wiki/get-started/philosophy.md +0 -296
- package/wiki/get-started/prerequisites.md +0 -113
|
@@ -0,0 +1,452 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Use Case Gallery
|
|
3
|
+
description: Real-world filter examples with corresponding SQL
|
|
4
|
+
difficulty: intermediate
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Use Case Gallery
|
|
8
|
+
|
|
9
|
+
Real-world examples of filter usage with corresponding SQL.
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
## E-commerce Product Search
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
const products = await productRepo.find({
|
|
16
|
+
filter: {
|
|
17
|
+
where: {
|
|
18
|
+
category: 'electronics',
|
|
19
|
+
price: { between: [100, 500] },
|
|
20
|
+
quantity: { gt: 0 },
|
|
21
|
+
status: 'active',
|
|
22
|
+
},
|
|
23
|
+
order: ['rating DESC', 'reviewCount DESC'],
|
|
24
|
+
fields: ['id', 'name', 'price', 'rating', 'imageUrl'],
|
|
25
|
+
limit: 24,
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
// SQL:
|
|
30
|
+
// SELECT "id", "name", "price", "rating", "image_url"
|
|
31
|
+
// FROM "Product"
|
|
32
|
+
// WHERE "category" = 'electronics'
|
|
33
|
+
// AND "price" BETWEEN 100 AND 500
|
|
34
|
+
// AND "quantity" > 0
|
|
35
|
+
// AND "status" = 'active'
|
|
36
|
+
// ORDER BY "rating" DESC, "review_count" DESC
|
|
37
|
+
// LIMIT 24
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
## Admin Dashboard: Recent Users
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
const thirtyDaysAgo = new Date();
|
|
45
|
+
thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
|
|
46
|
+
|
|
47
|
+
const recentUsers = await userRepo.find({
|
|
48
|
+
filter: {
|
|
49
|
+
where: {
|
|
50
|
+
createdAt: { gte: thirtyDaysAgo },
|
|
51
|
+
status: { nin: ['banned', 'suspended'] },
|
|
52
|
+
emailVerifiedAt: { isn: null },
|
|
53
|
+
},
|
|
54
|
+
order: ['createdAt DESC'],
|
|
55
|
+
fields: ['id', 'email', 'name', 'createdAt', 'status'],
|
|
56
|
+
limit: 50,
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// SQL:
|
|
61
|
+
// SELECT "id", "email", "name", "created_at", "status"
|
|
62
|
+
// FROM "User"
|
|
63
|
+
// WHERE "created_at" >= '2024-12-01T00:00:00.000Z'
|
|
64
|
+
// AND "status" NOT IN ('banned', 'suspended')
|
|
65
|
+
// AND "email_verified_at" IS NOT NULL
|
|
66
|
+
// ORDER BY "created_at" DESC
|
|
67
|
+
// LIMIT 50
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
## Task Management: Priority Tags
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
const priorityTasks = await taskRepo.find({
|
|
75
|
+
filter: {
|
|
76
|
+
where: {
|
|
77
|
+
status: { nin: ['completed', 'cancelled'] },
|
|
78
|
+
tags: { overlaps: ['urgent', 'high-priority'] },
|
|
79
|
+
assigneeId: currentUserId,
|
|
80
|
+
},
|
|
81
|
+
order: ['dueDate ASC', 'createdAt ASC'],
|
|
82
|
+
include: [{ relation: 'project' }],
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
// SQL:
|
|
87
|
+
// SELECT "Task".*
|
|
88
|
+
// FROM "Task"
|
|
89
|
+
// WHERE "status" NOT IN ('completed', 'cancelled')
|
|
90
|
+
// AND "tags"::text[] && ARRAY['urgent', 'high-priority']::text[]
|
|
91
|
+
// AND "assignee_id" = 'user-123'
|
|
92
|
+
// ORDER BY "due_date" ASC, "created_at" ASC
|
|
93
|
+
//
|
|
94
|
+
// -- Separate query for relation:
|
|
95
|
+
// SELECT * FROM "Project" WHERE "id" IN (...)
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
## Soft Delete Handling
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
// Find active records (soft delete pattern)
|
|
103
|
+
const activeRecords = await repo.find({
|
|
104
|
+
filter: {
|
|
105
|
+
where: { deletedAt: { is: null } },
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
// SQL:
|
|
110
|
+
// SELECT * FROM "Record" WHERE "deleted_at" IS NULL
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
// Find ONLY soft-deleted records
|
|
115
|
+
const deletedRecords = await repo.find({
|
|
116
|
+
filter: {
|
|
117
|
+
where: { deletedAt: { isn: null } },
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
// SQL:
|
|
122
|
+
// SELECT * FROM "Record" WHERE "deleted_at" IS NOT NULL
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
## Complex Authorization Filter
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
const getAuthorizedFilter = (user: User): TWhere<TDocumentSchema> => {
|
|
130
|
+
if (user.role === 'admin') {
|
|
131
|
+
return { deletedAt: { is: null } };
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return {
|
|
135
|
+
deletedAt: { is: null },
|
|
136
|
+
or: [
|
|
137
|
+
{ ownerId: user.id },
|
|
138
|
+
{ isPublic: true },
|
|
139
|
+
{ sharedWithTeams: { overlaps: user.teamIds } },
|
|
140
|
+
{ sharedWithUsers: { contains: [user.id] } },
|
|
141
|
+
],
|
|
142
|
+
};
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
const documents = await documentRepo.find({
|
|
146
|
+
filter: {
|
|
147
|
+
where: getAuthorizedFilter(currentUser),
|
|
148
|
+
order: ['updatedAt DESC'],
|
|
149
|
+
limit: 100,
|
|
150
|
+
},
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
// SQL (for admin):
|
|
154
|
+
// SELECT *
|
|
155
|
+
// FROM "Document"
|
|
156
|
+
// WHERE "deleted_at" IS NULL
|
|
157
|
+
// ORDER BY "updated_at" DESC
|
|
158
|
+
// LIMIT 100
|
|
159
|
+
|
|
160
|
+
// SQL (for regular user):
|
|
161
|
+
// SELECT *
|
|
162
|
+
// FROM "Document"
|
|
163
|
+
// WHERE "deleted_at" IS NULL
|
|
164
|
+
// AND (
|
|
165
|
+
// "owner_id" = 'user-123'
|
|
166
|
+
// OR "is_public" = true
|
|
167
|
+
// OR "shared_with_teams"::text[] && ARRAY['team-1', 'team-2']::text[]
|
|
168
|
+
// OR "shared_with_users"::text[] @> ARRAY['user-123']::text[]
|
|
169
|
+
// )
|
|
170
|
+
// ORDER BY "updated_at" DESC
|
|
171
|
+
// LIMIT 100
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
## Full-Text Search with Metadata
|
|
176
|
+
|
|
177
|
+
```typescript
|
|
178
|
+
const searchProducts = async (query: string, filters: {
|
|
179
|
+
minRating?: number;
|
|
180
|
+
maxPrice?: number;
|
|
181
|
+
features?: string[];
|
|
182
|
+
}) => {
|
|
183
|
+
const where: TWhere<TProductSchema> = {
|
|
184
|
+
status: 'active',
|
|
185
|
+
deletedAt: { is: null },
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
if (query) {
|
|
189
|
+
where.or = [
|
|
190
|
+
{ name: { ilike: `%${query}%` } },
|
|
191
|
+
{ description: { ilike: `%${query}%` } },
|
|
192
|
+
{ 'metadata.keywords': { ilike: `%${query}%` } },
|
|
193
|
+
];
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (filters.minRating) {
|
|
197
|
+
where.rating = { gte: filters.minRating };
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (filters.maxPrice) {
|
|
201
|
+
where.price = { lte: filters.maxPrice };
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (filters.features?.length) {
|
|
205
|
+
where['metadata.features'] = { contains: filters.features };
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return productRepo.find({
|
|
209
|
+
filter: {
|
|
210
|
+
where,
|
|
211
|
+
order: ['rating DESC', 'createdAt DESC'],
|
|
212
|
+
limit: 50,
|
|
213
|
+
},
|
|
214
|
+
});
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
// Example: searchProducts('wireless', { minRating: 4, maxPrice: 200, features: ['bluetooth'] })
|
|
218
|
+
//
|
|
219
|
+
// SQL:
|
|
220
|
+
// SELECT *
|
|
221
|
+
// FROM "Product"
|
|
222
|
+
// WHERE "status" = 'active'
|
|
223
|
+
// AND "deleted_at" IS NULL
|
|
224
|
+
// AND (
|
|
225
|
+
// "name" ILIKE '%wireless%'
|
|
226
|
+
// OR "description" ILIKE '%wireless%'
|
|
227
|
+
// OR "metadata" #>> '{keywords}' ILIKE '%wireless%'
|
|
228
|
+
// )
|
|
229
|
+
// AND "rating" >= 4
|
|
230
|
+
// AND "price" <= 200
|
|
231
|
+
// AND "metadata" #>> '{features}' @> '["bluetooth"]'
|
|
232
|
+
// ORDER BY "rating" DESC, "created_at" DESC
|
|
233
|
+
// LIMIT 50
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
## Massive Filter Example
|
|
238
|
+
|
|
239
|
+
```typescript
|
|
240
|
+
const massiveFilter: TFilter<TProductSchema> = {
|
|
241
|
+
where: {
|
|
242
|
+
status: 'active',
|
|
243
|
+
deletedAt: { is: null },
|
|
244
|
+
price: { gte: 50, lte: 500 },
|
|
245
|
+
quantity: { gt: 0 },
|
|
246
|
+
tags: { contains: ['electronics', 'portable'] },
|
|
247
|
+
'metadata.priority': { gte: 3 },
|
|
248
|
+
'metadata.features.wireless': true,
|
|
249
|
+
or: [
|
|
250
|
+
{ rating: { gte: 4.5 } },
|
|
251
|
+
{
|
|
252
|
+
and: [
|
|
253
|
+
{ isFeatured: true },
|
|
254
|
+
{ 'metadata.promotion.active': true },
|
|
255
|
+
{ 'metadata.promotion.discount': { gte: 20 } },
|
|
256
|
+
]
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
createdAt: { gte: new Date('2024-12-01') },
|
|
260
|
+
'metadata.isNewArrival': true,
|
|
261
|
+
},
|
|
262
|
+
],
|
|
263
|
+
category: { nin: ['discontinued', 'recalled'] },
|
|
264
|
+
suppliers: { overlaps: ['supplier-a', 'supplier-b'] },
|
|
265
|
+
},
|
|
266
|
+
fields: ['id', 'name', 'price', 'rating', 'tags', 'metadata'],
|
|
267
|
+
order: ['metadata.priority DESC', 'rating DESC', 'createdAt DESC'],
|
|
268
|
+
limit: 20,
|
|
269
|
+
skip: 0,
|
|
270
|
+
include: [
|
|
271
|
+
{ relation: 'category' },
|
|
272
|
+
{
|
|
273
|
+
relation: 'reviews',
|
|
274
|
+
scope: {
|
|
275
|
+
where: { rating: { gte: 4 } },
|
|
276
|
+
order: ['createdAt DESC'],
|
|
277
|
+
limit: 5,
|
|
278
|
+
},
|
|
279
|
+
},
|
|
280
|
+
],
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
const products = await productRepo.find({ filter: massiveFilter });
|
|
284
|
+
|
|
285
|
+
// SQL:
|
|
286
|
+
// SELECT "id", "name", "price", "rating", "tags", "metadata"
|
|
287
|
+
// FROM "Product"
|
|
288
|
+
// WHERE "status" = 'active'
|
|
289
|
+
// AND "deleted_at" IS NULL
|
|
290
|
+
// AND "price" >= 50 AND "price" <= 500
|
|
291
|
+
// AND "quantity" > 0
|
|
292
|
+
// AND "tags"::text[] @> ARRAY['electronics', 'portable']::text[]
|
|
293
|
+
// AND CASE
|
|
294
|
+
// WHEN ("metadata" #>> '{priority}') ~ '^-?[0-9]+(\.[0-9]+)?$'
|
|
295
|
+
// THEN ("metadata" #>> '{priority}')::numeric ELSE NULL
|
|
296
|
+
// END >= 3
|
|
297
|
+
// AND "metadata" #>> '{features,wireless}' = 'true'
|
|
298
|
+
// AND (
|
|
299
|
+
// "rating" >= 4.5
|
|
300
|
+
// OR (
|
|
301
|
+
// "is_featured" = true
|
|
302
|
+
// AND "metadata" #>> '{promotion,active}' = 'true'
|
|
303
|
+
// AND CASE
|
|
304
|
+
// WHEN ("metadata" #>> '{promotion,discount}') ~ '^-?[0-9]+(\.[0-9]+)?$'
|
|
305
|
+
// THEN ("metadata" #>> '{promotion,discount}')::numeric ELSE NULL
|
|
306
|
+
// END >= 20
|
|
307
|
+
// )
|
|
308
|
+
// OR (
|
|
309
|
+
// "created_at" >= '2024-12-01T00:00:00.000Z'
|
|
310
|
+
// AND "metadata" #>> '{isNewArrival}' = 'true'
|
|
311
|
+
// )
|
|
312
|
+
// )
|
|
313
|
+
// AND "category" NOT IN ('discontinued', 'recalled')
|
|
314
|
+
// AND "suppliers"::text[] && ARRAY['supplier-a', 'supplier-b']::text[]
|
|
315
|
+
// ORDER BY "metadata" #> '{priority}' DESC, "rating" DESC, "created_at" DESC
|
|
316
|
+
// LIMIT 20 OFFSET 0
|
|
317
|
+
//
|
|
318
|
+
// -- Separate query for category relation:
|
|
319
|
+
// SELECT * FROM "Category" WHERE "id" IN (...)
|
|
320
|
+
//
|
|
321
|
+
// -- Separate query for reviews relation:
|
|
322
|
+
// SELECT * FROM "Review"
|
|
323
|
+
// WHERE "product_id" IN (...) AND "rating" >= 4
|
|
324
|
+
// ORDER BY "created_at" DESC
|
|
325
|
+
// LIMIT 5
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
## Date Range Queries
|
|
330
|
+
|
|
331
|
+
```typescript
|
|
332
|
+
// Events this week
|
|
333
|
+
const startOfWeek = new Date('2024-12-29');
|
|
334
|
+
const endOfWeek = new Date('2025-01-04');
|
|
335
|
+
|
|
336
|
+
const weekEvents = await eventRepo.find({
|
|
337
|
+
filter: {
|
|
338
|
+
where: {
|
|
339
|
+
eventDate: { between: [startOfWeek, endOfWeek] }
|
|
340
|
+
},
|
|
341
|
+
order: ['eventDate ASC']
|
|
342
|
+
}
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
// SQL:
|
|
346
|
+
// SELECT *
|
|
347
|
+
// FROM "Event"
|
|
348
|
+
// WHERE "event_date" BETWEEN '2024-12-29' AND '2025-01-04'
|
|
349
|
+
// ORDER BY "event_date" ASC
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
```typescript
|
|
353
|
+
// Orders in the last 7 days
|
|
354
|
+
const sevenDaysAgo = new Date();
|
|
355
|
+
sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7);
|
|
356
|
+
|
|
357
|
+
const recentOrders = await orderRepo.find({
|
|
358
|
+
filter: {
|
|
359
|
+
where: {
|
|
360
|
+
createdAt: { gte: sevenDaysAgo },
|
|
361
|
+
status: { in: ['completed', 'shipped'] },
|
|
362
|
+
total: { gte: 100 }
|
|
363
|
+
},
|
|
364
|
+
order: ['total DESC'],
|
|
365
|
+
limit: 100
|
|
366
|
+
}
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
// SQL:
|
|
370
|
+
// SELECT *
|
|
371
|
+
// FROM "Order"
|
|
372
|
+
// WHERE "created_at" >= '2024-12-24T00:00:00.000Z'
|
|
373
|
+
// AND "status" IN ('completed', 'shipped')
|
|
374
|
+
// AND "total" >= 100
|
|
375
|
+
// ORDER BY "total" DESC
|
|
376
|
+
// LIMIT 100
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
|
|
380
|
+
## Multi-Tenant Data Isolation
|
|
381
|
+
|
|
382
|
+
```typescript
|
|
383
|
+
const getTenantProducts = async (tenantId: string, filter: TFilter<TProductSchema>) => {
|
|
384
|
+
return productRepo.find({
|
|
385
|
+
filter: {
|
|
386
|
+
...filter,
|
|
387
|
+
where: {
|
|
388
|
+
...filter.where,
|
|
389
|
+
tenantId, // Always enforce tenant isolation
|
|
390
|
+
deletedAt: { is: null },
|
|
391
|
+
},
|
|
392
|
+
},
|
|
393
|
+
});
|
|
394
|
+
};
|
|
395
|
+
|
|
396
|
+
// Usage
|
|
397
|
+
await getTenantProducts('tenant-abc', {
|
|
398
|
+
where: { category: 'electronics' },
|
|
399
|
+
order: ['createdAt DESC'],
|
|
400
|
+
limit: 20
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
// SQL:
|
|
404
|
+
// SELECT *
|
|
405
|
+
// FROM "Product"
|
|
406
|
+
// WHERE "category" = 'electronics'
|
|
407
|
+
// AND "tenant_id" = 'tenant-abc'
|
|
408
|
+
// AND "deleted_at" IS NULL
|
|
409
|
+
// ORDER BY "created_at" DESC
|
|
410
|
+
// LIMIT 20
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
## Inventory Low Stock Alert
|
|
415
|
+
|
|
416
|
+
```typescript
|
|
417
|
+
const lowStockProducts = await productRepo.find({
|
|
418
|
+
filter: {
|
|
419
|
+
where: {
|
|
420
|
+
status: 'active',
|
|
421
|
+
quantity: { lte: 10 },
|
|
422
|
+
'metadata.reorderPoint': { isn: null },
|
|
423
|
+
or: [
|
|
424
|
+
{ quantity: { lt: 5 } }, // Critical: below 5
|
|
425
|
+
{
|
|
426
|
+
and: [
|
|
427
|
+
{ quantity: { lte: 10 } },
|
|
428
|
+
{ 'metadata.fastMoving': true }
|
|
429
|
+
]
|
|
430
|
+
}
|
|
431
|
+
]
|
|
432
|
+
},
|
|
433
|
+
order: ['quantity ASC'],
|
|
434
|
+
fields: ['id', 'name', 'quantity', 'metadata']
|
|
435
|
+
}
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
// SQL:
|
|
439
|
+
// SELECT "id", "name", "quantity", "metadata"
|
|
440
|
+
// FROM "Product"
|
|
441
|
+
// WHERE "status" = 'active'
|
|
442
|
+
// AND "quantity" <= 10
|
|
443
|
+
// AND "metadata" #>> '{reorderPoint}' IS NOT NULL
|
|
444
|
+
// AND (
|
|
445
|
+
// "quantity" < 5
|
|
446
|
+
// OR (
|
|
447
|
+
// "quantity" <= 10
|
|
448
|
+
// AND "metadata" #>> '{fastMoving}' = 'true'
|
|
449
|
+
// )
|
|
450
|
+
// )
|
|
451
|
+
// ORDER BY "quantity" ASC
|
|
452
|
+
```
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# Base Abstractions
|
|
2
|
+
|
|
3
|
+
Core classes that power every Ignis application - from the Application entry point to Repositories for data access.
|
|
4
|
+
|
|
5
|
+
## Quick Reference
|
|
6
|
+
|
|
7
|
+
| Class | Purpose | Extends |
|
|
8
|
+
|-------|---------|---------|
|
|
9
|
+
| `BaseApplication` | Application entry point, DI container | `AbstractApplication` |
|
|
10
|
+
| `BaseController` | HTTP route handlers | - |
|
|
11
|
+
| `BaseService` | Business logic layer | - |
|
|
12
|
+
| `BaseProvider` | Factory pattern for runtime instantiation | `BaseHelper` |
|
|
13
|
+
| `BaseComponent` | Pluggable feature modules | - |
|
|
14
|
+
| `BaseDataSource` | Database connections | - |
|
|
15
|
+
| `BaseEntity` | Model definitions | - |
|
|
16
|
+
| `DefaultCRUDRepository` | Full CRUD operations | `PersistableRepository` |
|
|
17
|
+
| `ReadableRepository` | Read-only operations | `AbstractRepository` |
|
|
18
|
+
|
|
19
|
+
## Architecture
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
23
|
+
│ BaseApplication │
|
|
24
|
+
│ (DI Container + Lifecycle + Server Management) │
|
|
25
|
+
├─────────────────────────────────────────────────────────────┤
|
|
26
|
+
│ │
|
|
27
|
+
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
|
|
28
|
+
│ │ BaseController│ │ BaseService │ │BaseComponent │ │
|
|
29
|
+
│ │ (HTTP Layer) │ │(Business Logic)│ │ (Plugins) │ │
|
|
30
|
+
│ └──────┬───────┘ └──────┬───────┘ └──────────────┘ │
|
|
31
|
+
│ │ │ │
|
|
32
|
+
│ └────────┬─────────┘ │
|
|
33
|
+
│ ▼ │
|
|
34
|
+
│ ┌──────────────────┐ │
|
|
35
|
+
│ │DefaultCRUDRepository│ │
|
|
36
|
+
│ │ (Data Access) │ │
|
|
37
|
+
│ └────────┬──────────┘ │
|
|
38
|
+
│ │ │
|
|
39
|
+
│ ┌────────┴────────┐ │
|
|
40
|
+
│ ▼ ▼ │
|
|
41
|
+
│ ┌────────────┐ ┌────────────┐ │
|
|
42
|
+
│ │BaseDataSource│ │ BaseEntity │ │
|
|
43
|
+
│ │(Connection) │ │ (Schema) │ │
|
|
44
|
+
│ └─────────────┘ └────────────┘ │
|
|
45
|
+
│ │
|
|
46
|
+
└──────────────────────────────────────────────────────────────┘
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## What's in This Section
|
|
50
|
+
|
|
51
|
+
### Core Application
|
|
52
|
+
- [Application](./application.md) - `BaseApplication` class, resource registration, lifecycle hooks
|
|
53
|
+
- [Bootstrapping](./bootstrapping.md) - Startup sequence, `initialize()` flow
|
|
54
|
+
|
|
55
|
+
### HTTP Layer
|
|
56
|
+
- [Controllers](./controllers.md) - Route handlers, decorators, request/response handling
|
|
57
|
+
- [Middlewares](./middlewares.md) - Built-in middlewares for error handling, logging, and request processing
|
|
58
|
+
- [Services](./services.md) - Business logic, injectable services
|
|
59
|
+
|
|
60
|
+
### Dependency Injection
|
|
61
|
+
- [Dependency Injection](./dependency-injection.md) - Container, bindings, `@inject` patterns
|
|
62
|
+
- [Providers](./providers.md) - Factory pattern for configuration-driven instantiation
|
|
63
|
+
- [Components](./components.md) - Pluggable modules, component lifecycle
|
|
64
|
+
|
|
65
|
+
### Data Layer
|
|
66
|
+
- [Models & Enrichers](./models.md) - `BaseEntity`, schema definitions, enrichers
|
|
67
|
+
- [DataSources](./datasources.md) - Database connections, auto-discovery
|
|
68
|
+
- [Repositories](./repositories/) - CRUD operations, filtering, relations
|
|
69
|
+
- [Filter System](./filter-system/) - Query filter types and operators
|
|
70
|
+
|
|
71
|
+
## Class Hierarchy
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
AbstractApplication
|
|
75
|
+
└── BaseApplication ──────► Your Application
|
|
76
|
+
|
|
77
|
+
AbstractRepository
|
|
78
|
+
├── ReadableRepository
|
|
79
|
+
│ └── PersistableRepository
|
|
80
|
+
│ └── DefaultCRUDRepository ──────► Your Repository
|
|
81
|
+
│
|
|
82
|
+
BaseController ──────► Your Controller
|
|
83
|
+
BaseService ──────► Your Service
|
|
84
|
+
BaseProvider ──────► Your Provider
|
|
85
|
+
BaseComponent ──────► Your Component
|
|
86
|
+
BaseDataSource ──────► Your DataSource
|
|
87
|
+
BaseEntity ──────► Your Model
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
> **Related:** [Core Concepts Guide](../../guides/core-concepts/application/) | [Persistent Layer Guide](../../guides/core-concepts/persistent/)
|