@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.
Files changed (123) hide show
  1. package/mcp-server/dist/common/config.d.ts +27 -0
  2. package/mcp-server/dist/common/config.d.ts.map +1 -0
  3. package/mcp-server/dist/common/config.js +27 -0
  4. package/mcp-server/dist/common/config.js.map +1 -0
  5. package/mcp-server/dist/common/index.d.ts +3 -0
  6. package/mcp-server/dist/common/index.d.ts.map +1 -0
  7. package/mcp-server/dist/common/index.js +19 -0
  8. package/mcp-server/dist/common/index.js.map +1 -0
  9. package/mcp-server/dist/common/paths.d.ts +13 -0
  10. package/mcp-server/dist/common/paths.d.ts.map +1 -0
  11. package/mcp-server/dist/common/paths.js +23 -0
  12. package/mcp-server/dist/common/paths.js.map +1 -0
  13. package/mcp-server/dist/helpers/docs.helper.d.ts +81 -0
  14. package/mcp-server/dist/helpers/docs.helper.d.ts.map +1 -0
  15. package/mcp-server/dist/helpers/docs.helper.js +171 -0
  16. package/mcp-server/dist/helpers/docs.helper.js.map +1 -0
  17. package/mcp-server/dist/helpers/index.d.ts +3 -0
  18. package/mcp-server/dist/helpers/index.d.ts.map +1 -0
  19. package/mcp-server/dist/helpers/index.js +19 -0
  20. package/mcp-server/dist/helpers/index.js.map +1 -0
  21. package/mcp-server/dist/helpers/logger.helper.d.ts +7 -0
  22. package/mcp-server/dist/helpers/logger.helper.d.ts.map +1 -0
  23. package/mcp-server/dist/helpers/logger.helper.js +22 -0
  24. package/mcp-server/dist/helpers/logger.helper.js.map +1 -0
  25. package/mcp-server/dist/index.d.ts +3 -0
  26. package/mcp-server/dist/index.d.ts.map +1 -0
  27. package/mcp-server/dist/index.js +62 -0
  28. package/mcp-server/dist/index.js.map +1 -0
  29. package/mcp-server/dist/tools/base.tool.d.ts +98 -0
  30. package/mcp-server/dist/tools/base.tool.d.ts.map +1 -0
  31. package/mcp-server/dist/tools/base.tool.js +47 -0
  32. package/mcp-server/dist/tools/base.tool.js.map +1 -0
  33. package/mcp-server/dist/tools/get-doc-content.tool.d.ts +30 -0
  34. package/mcp-server/dist/tools/get-doc-content.tool.d.ts.map +1 -0
  35. package/mcp-server/dist/tools/get-doc-content.tool.js +127 -0
  36. package/mcp-server/dist/tools/get-doc-content.tool.js.map +1 -0
  37. package/mcp-server/dist/tools/get-doc-metadata.tool.d.ts +40 -0
  38. package/mcp-server/dist/tools/get-doc-metadata.tool.d.ts.map +1 -0
  39. package/mcp-server/dist/tools/get-doc-metadata.tool.js +121 -0
  40. package/mcp-server/dist/tools/get-doc-metadata.tool.js.map +1 -0
  41. package/mcp-server/dist/tools/index.d.ts +8 -0
  42. package/mcp-server/dist/tools/index.d.ts.map +1 -0
  43. package/mcp-server/dist/tools/index.js +18 -0
  44. package/mcp-server/dist/tools/index.js.map +1 -0
  45. package/mcp-server/dist/tools/list-categories.tool.d.ts +20 -0
  46. package/mcp-server/dist/tools/list-categories.tool.d.ts.map +1 -0
  47. package/mcp-server/dist/tools/list-categories.tool.js +105 -0
  48. package/mcp-server/dist/tools/list-categories.tool.js.map +1 -0
  49. package/mcp-server/dist/tools/list-docs.tool.d.ts +32 -0
  50. package/mcp-server/dist/tools/list-docs.tool.d.ts.map +1 -0
  51. package/mcp-server/dist/tools/list-docs.tool.js +121 -0
  52. package/mcp-server/dist/tools/list-docs.tool.js.map +1 -0
  53. package/mcp-server/dist/tools/search-docs.tool.d.ts +32 -0
  54. package/mcp-server/dist/tools/search-docs.tool.d.ts.map +1 -0
  55. package/mcp-server/dist/tools/search-docs.tool.js +120 -0
  56. package/mcp-server/dist/tools/search-docs.tool.js.map +1 -0
  57. package/package.json +102 -0
  58. package/wiki/get-started/5-minute-quickstart.md +266 -0
  59. package/wiki/get-started/best-practices/api-usage-examples.md +222 -0
  60. package/wiki/get-started/best-practices/architectural-patterns.md +129 -0
  61. package/wiki/get-started/best-practices/code-style-standards.md +122 -0
  62. package/wiki/get-started/best-practices/common-pitfalls.md +136 -0
  63. package/wiki/get-started/best-practices/contribution-workflow.md +145 -0
  64. package/wiki/get-started/best-practices/deployment-strategies.md +121 -0
  65. package/wiki/get-started/best-practices/performance-optimization.md +88 -0
  66. package/wiki/get-started/best-practices/security-guidelines.md +97 -0
  67. package/wiki/get-started/best-practices/troubleshooting-tips.md +100 -0
  68. package/wiki/get-started/building-a-crud-api.md +717 -0
  69. package/wiki/get-started/core-concepts/application.md +168 -0
  70. package/wiki/get-started/core-concepts/components.md +96 -0
  71. package/wiki/get-started/core-concepts/controllers.md +441 -0
  72. package/wiki/get-started/core-concepts/dependency-injection.md +160 -0
  73. package/wiki/get-started/core-concepts/persistent.md +591 -0
  74. package/wiki/get-started/core-concepts/services.md +88 -0
  75. package/wiki/get-started/index.md +65 -0
  76. package/wiki/get-started/mcp-docs-server.md +840 -0
  77. package/wiki/get-started/philosophy.md +123 -0
  78. package/wiki/get-started/prerequisites.md +113 -0
  79. package/wiki/get-started/quickstart.md +382 -0
  80. package/wiki/index.md +48 -0
  81. package/wiki/references/base/application.md +67 -0
  82. package/wiki/references/base/components.md +80 -0
  83. package/wiki/references/base/controllers.md +361 -0
  84. package/wiki/references/base/datasources.md +105 -0
  85. package/wiki/references/base/dependency-injection.md +83 -0
  86. package/wiki/references/base/models.md +104 -0
  87. package/wiki/references/base/repositories.md +118 -0
  88. package/wiki/references/base/services.md +97 -0
  89. package/wiki/references/components/authentication.md +224 -0
  90. package/wiki/references/components/health-check.md +190 -0
  91. package/wiki/references/components/index.md +61 -0
  92. package/wiki/references/components/request-tracker.md +35 -0
  93. package/wiki/references/components/socket-io.md +127 -0
  94. package/wiki/references/components/swagger.md +175 -0
  95. package/wiki/references/helpers/cron.md +94 -0
  96. package/wiki/references/helpers/crypto.md +117 -0
  97. package/wiki/references/helpers/env.md +67 -0
  98. package/wiki/references/helpers/error.md +80 -0
  99. package/wiki/references/helpers/index.md +21 -0
  100. package/wiki/references/helpers/inversion.md +141 -0
  101. package/wiki/references/helpers/logger.md +98 -0
  102. package/wiki/references/helpers/network.md +143 -0
  103. package/wiki/references/helpers/queue.md +131 -0
  104. package/wiki/references/helpers/redis.md +121 -0
  105. package/wiki/references/helpers/socket-io.md +103 -0
  106. package/wiki/references/helpers/storage.md +130 -0
  107. package/wiki/references/helpers/testing.md +115 -0
  108. package/wiki/references/helpers/worker-thread.md +162 -0
  109. package/wiki/references/src-details/core.md +249 -0
  110. package/wiki/references/src-details/dev-configs.md +302 -0
  111. package/wiki/references/src-details/docs.md +61 -0
  112. package/wiki/references/src-details/helpers.md +211 -0
  113. package/wiki/references/src-details/inversion.md +345 -0
  114. package/wiki/references/src-details/mcp-server.md +726 -0
  115. package/wiki/references/utilities/crypto.md +39 -0
  116. package/wiki/references/utilities/date.md +72 -0
  117. package/wiki/references/utilities/index.md +12 -0
  118. package/wiki/references/utilities/module.md +40 -0
  119. package/wiki/references/utilities/parse.md +68 -0
  120. package/wiki/references/utilities/performance.md +64 -0
  121. package/wiki/references/utilities/promise.md +83 -0
  122. package/wiki/references/utilities/request.md +66 -0
  123. package/wiki/references/utilities/schema.md +88 -0
@@ -0,0 +1,717 @@
1
+ # Building a CRUD API: A Step-by-Step Tutorial
2
+
3
+ Build a complete, database-backed REST API for managing todos. This guide covers Models, DataSources, Repositories, and Controllers - the core building blocks of Ignis applications.
4
+
5
+ ## Prerequisites
6
+
7
+ - ✅ Completed [Quickstart Guide](./quickstart.md)
8
+ - ✅ PostgreSQL installed and running
9
+ - ✅ Database created (see [Prerequisites](./prerequisites.md))
10
+
11
+ ## What You'll Build
12
+
13
+ **Components:**
14
+ - `Todo` Model - Data structure definition
15
+ - `PostgresDataSource` - Database connection
16
+ - `TodoRepository` - Data access layer
17
+ - `TodoController` - REST API endpoints
18
+
19
+ **Endpoints:**
20
+ - `POST /todos` - Create todo
21
+ - `GET /todos` - List all todos
22
+ - `GET /todos/:id` - Get single todo
23
+ - `PATCH /todos/:id` - Update todo
24
+ - `DELETE /todos/:id` - Delete todo
25
+
26
+ ### Architecture Flow
27
+
28
+ Here's how a request flows through your application:
29
+
30
+ ```
31
+ ┌─────────────────────────────────────────────────────────────┐
32
+ │ HTTP Request │
33
+ │ GET /api/todos/:id │
34
+ └────────────────────┬────────────────────────────────────────┘
35
+
36
+
37
+ ┌──────────────────┐
38
+ │ TodoController │ ← Handles HTTP, validates input
39
+ │ │
40
+ │ @get('/api/...')│
41
+ └─────────┬────────┘
42
+
43
+ │ calls repository.findById()
44
+
45
+ ┌──────────────────┐
46
+ │ TodoRepository │ ← Type-safe data access
47
+ │ │
48
+ │ findById(id) │
49
+ └─────────┬────────┘
50
+
51
+ │ uses dataSource.connector
52
+
53
+ ┌──────────────────┐
54
+ │PostgresDataSource│ ← Database connection
55
+ │ │
56
+ │ Drizzle ORM │
57
+ └─────────┬────────┘
58
+
59
+ │ executes SQL query
60
+
61
+ ┌──────────────────┐
62
+ │ PostgreSQL │ ← Actual database
63
+ │ │
64
+ │ Todo table │
65
+ └─────────┬────────┘
66
+
67
+ │ returns data
68
+
69
+ ┌──────────────────┐
70
+ │ JSON Response │
71
+ │ │
72
+ │ { id, title,..} │
73
+ └──────────────────┘
74
+ ```
75
+
76
+ **Key Points:**
77
+ 1. **Controller** - Entry point for HTTP requests
78
+ 2. **Repository** - Abstracts database operations (you could swap PostgreSQL for MySQL without changing controller)
79
+ 3. **DataSource** - Manages connection to database
80
+ 4. **Model** - Defines what the data looks like
81
+
82
+ This separation makes code:
83
+ - **Testable** - Mock repository in tests
84
+ - **Maintainable** - Clear responsibility for each layer
85
+ - **Flexible** - Change database without touching business logic
86
+
87
+ ## Step 0: Install Database Dependencies
88
+
89
+ ```bash
90
+ # Add database packages
91
+ bun add drizzle-orm drizzle-zod pg lodash
92
+
93
+ # Add dev dependencies for migrations
94
+ bun add -d drizzle-kit @types/pg @types/lodash
95
+ ```
96
+
97
+ ## Step 1: Define the Model
98
+
99
+ Models combine Drizzle ORM schemas with Entity classes to define your data structure.
100
+
101
+ Create `src/models/todo.model.ts`:
102
+
103
+ ```typescript
104
+ // src/models/todo.model.ts
105
+ import {
106
+ BaseEntity,
107
+ createRelations,
108
+ generateIdColumnDefs,
109
+ generateTzColumnDefs,
110
+ model,
111
+ TTableObject,
112
+ } from '@venizia/ignis';
113
+ import { boolean, pgTable, text } from 'drizzle-orm/pg-core';
114
+
115
+ // 1. Define the Drizzle schema for the 'Todo' table
116
+ // Note: Use string literal 'Todo' to avoid circular reference
117
+ export const todoTable = pgTable('Todo', {
118
+ ...generateIdColumnDefs({ id: { dataType: 'string' } }),
119
+ ...generateTzColumnDefs(),
120
+ title: text('title').notNull(),
121
+ description: text('description'),
122
+ isCompleted: boolean('is_completed').default(false),
123
+ });
124
+
125
+ // 2. Define relations (empty for now, but required)
126
+ export const todoRelations = createRelations({
127
+ source: todoTable,
128
+ relations: [],
129
+ });
130
+
131
+ // 3. Define the TypeScript type for a Todo object
132
+ export type TTodoSchema = typeof todoTable;
133
+ export type TTodo = TTableObject<TTodoSchema>;
134
+
135
+ // 4. Create the Entity class, decorated with @model
136
+ @model({ type: 'entity' })
137
+ export class Todo extends BaseEntity<TTodoSchema> {
138
+ static readonly TABLE_NAME = 'Todo';
139
+
140
+ constructor() {
141
+ super({ name: Todo.TABLE_NAME, schema: todoTable });
142
+ }
143
+ }
144
+ ```
145
+
146
+ **Schema Enrichers:**
147
+ - `generateIdColumnDefs()` - Adds `id` column (UUID primary key)
148
+ - `generateTzColumnDefs()` - Adds `createdAt` and `modifiedAt` timestamps
149
+
150
+ > **Deep Dive:** See [Models & Enrichers Reference](../references/base/models.md#schema-enrichers) for all available enrichers and options.
151
+
152
+ ## Step 2: Configure Database Connection
153
+
154
+ ### Understanding Environment Variables
155
+
156
+ Before we connect to the database, let's understand **environment variables**.
157
+
158
+ **What are they?**
159
+ Environment variables are configuration values stored outside your code. Think of them as settings that can change without modifying your source code.
160
+
161
+ **Why use them?**
162
+ ```typescript
163
+ // ❌ BAD: Hardcoded values
164
+ const password = "secret123"; // Now it's in Git history forever!
165
+
166
+ // ✅ GOOD: Environment variable
167
+ const password = process.env.DB_PASSWORD; // Value comes from .env file
168
+ ```
169
+
170
+ **Benefits:**
171
+ 1. **Security** - Never commit passwords to Git
172
+ 2. **Flexibility** - Different values for dev/staging/production
173
+ 3. **Team Collaboration** - Each developer has their own `.env` file
174
+
175
+ **The `APP_ENV_` prefix:**
176
+ Ignis uses `APP_ENV_` prefix for all its environment variables. This prevents conflicts with system variables (like `PATH`, `HOME`, etc.).
177
+
178
+ ### Create `.env` File
179
+
180
+ Create `.env` in your project root with your database credentials:
181
+
182
+ ```bash
183
+ # .env
184
+ APP_ENV_POSTGRES_HOST=localhost
185
+ APP_ENV_POSTGRES_PORT=5432
186
+ APP_ENV_POSTGRES_USERNAME=postgres
187
+ APP_ENV_POSTGRES_PASSWORD=your_password_here
188
+ APP_ENV_POSTGRES_DATABASE=todo_db
189
+ ```
190
+
191
+ **Replace these values:**
192
+ - `your_password_here` - Your PostgreSQL password (or leave blank if no password)
193
+ - `todo_db` - The database you created in [Prerequisites](./prerequisites.md#database-setup)
194
+
195
+ **Important:** Add `.env` to your `.gitignore`:
196
+ ```bash
197
+ echo ".env" >> .gitignore
198
+ ```
199
+
200
+ This prevents accidentally committing secrets to Git.
201
+
202
+ Create `src/datasources/postgres.datasource.ts`:
203
+
204
+ ```typescript
205
+ // src/datasources/postgres.datasource.ts
206
+ import { Todo, todoRelations, todoTable } from '@/models/todo.model';
207
+ import {
208
+ BaseDataSource,
209
+ datasource,
210
+ TNodePostgresConnector,
211
+ ValueOrPromise,
212
+ } from '@venizia/ignis';
213
+ import { drizzle } from 'drizzle-orm/node-postgres';
214
+ import { Pool } from 'pg';
215
+
216
+ interface IDSConfigs {
217
+ connection: {
218
+ host?: string;
219
+ port?: number;
220
+ user?: string;
221
+ password?: string;
222
+ database?: string;
223
+ };
224
+ }
225
+
226
+ @datasource()
227
+ export class PostgresDataSource extends BaseDataSource<TNodePostgresConnector, IDSConfigs> {
228
+ constructor() {
229
+ super({
230
+ name: PostgresDataSource.name,
231
+ driver: 'node-postgres',
232
+ config: {
233
+ connection: {
234
+ host: process.env.APP_ENV_POSTGRES_HOST,
235
+ port: +(process.env.APP_ENV_POSTGRES_PORT ?? 5432),
236
+ user: process.env.APP_ENV_POSTGRES_USERNAME,
237
+ password: process.env.APP_ENV_POSTGRES_PASSWORD,
238
+ database: process.env.APP_ENV_POSTGRES_DATABASE,
239
+ },
240
+ },
241
+ // Register all your models and their relations here
242
+ schema: Object.assign(
243
+ {},
244
+ { [Todo.TABLE_NAME]: todoTable },
245
+ todoRelations.relations,
246
+ ),
247
+ });
248
+ }
249
+
250
+ override configure(): ValueOrPromise<void> {
251
+ this.connector = drizzle({
252
+ client: new Pool(this.settings.connection),
253
+ schema: this.schema,
254
+ });
255
+ }
256
+
257
+ override async connect(): Promise<TNodePostgresConnector | undefined> {
258
+ await (this.connector.client as Pool).connect();
259
+ return this.connector;
260
+ }
261
+
262
+ override async disconnect(): Promise<void> {
263
+ await (this.connector.client as Pool).end();
264
+ }
265
+ }
266
+ ```
267
+
268
+ **Key Points:**
269
+ - Registers `todoTable` and `todoRelations` in the schema
270
+ - Uses environment variables for connection config
271
+ - Implements connection lifecycle methods
272
+
273
+ > **Deep Dive:** See [DataSources Reference](../references/base/datasources.md) for advanced configuration and multiple database support.
274
+
275
+ ## Step 3: Create the Repository
276
+
277
+ Repositories provide type-safe CRUD operations using `DefaultCRUDRepository`.
278
+
279
+ Create `src/repositories/todo.repository.ts`:
280
+
281
+ ```typescript
282
+ // src/repositories/todo.repository.ts
283
+ import { Todo, todoRelations, TTodoSchema } from '@/models/todo.model';
284
+ import {
285
+ DefaultCRUDRepository,
286
+ IDataSource,
287
+ inject,
288
+ repository,
289
+ } from '@venizia/ignis';
290
+
291
+ @repository()
292
+ export class TodoRepository extends DefaultCRUDRepository<TTodoSchema> {
293
+ constructor(
294
+ @inject({ key: 'datasources.PostgresDataSource' })
295
+ dataSource: IDataSource,
296
+ ) {
297
+ super({
298
+ dataSource,
299
+ entityClass: Todo,
300
+ relations: todoRelations.definitions,
301
+ });
302
+ }
303
+ }
304
+ ```
305
+
306
+ **Available Methods:**
307
+ `create()`, `find()`, `findOne()`, `findById()`, `updateById()`, `updateAll()`, `deleteById()`, `deleteAll()`, `count()`
308
+
309
+ > **Deep Dive:** See [Repositories Reference](../references/base/repositories.md) for query options and advanced filtering.
310
+
311
+ ## Step 4: Create the Controller
312
+
313
+ `ControllerFactory` generates a full CRUD controller with automatic validation and OpenAPI docs.
314
+
315
+ Create `src/controllers/todo.controller.ts`:
316
+
317
+ ```typescript
318
+ // src/controllers/todo.controller.ts
319
+ import { Todo } from '@/models/todo.model';
320
+ import { TodoRepository } from '@/repositories/todo.repository';
321
+ import {
322
+ BindingKeys,
323
+ BindingNamespaces,
324
+ controller,
325
+ ControllerFactory,
326
+ inject,
327
+ } from '@venizia/ignis';
328
+
329
+ const BASE_PATH = '/todos';
330
+
331
+ // 1. The factory generates a controller class with all CRUD routes
332
+ const _Controller = ControllerFactory.defineCrudController({
333
+ repository: { name: TodoRepository.name },
334
+ controller: {
335
+ name: 'TodoController',
336
+ basePath: BASE_PATH,
337
+ },
338
+ entity: () => Todo, // The entity is used to generate OpenAPI schemas
339
+ });
340
+
341
+ // 2. Extend the generated controller to inject the repository
342
+ @controller({ path: BASE_PATH })
343
+ export class TodoController extends _Controller {
344
+ constructor(
345
+ @inject({
346
+ key: BindingKeys.build({
347
+ namespace: BindingNamespaces.REPOSITORY,
348
+ key: TodoRepository.name,
349
+ }),
350
+ })
351
+ repository: TodoRepository,
352
+ ) {
353
+ super(repository);
354
+ }
355
+ }
356
+ ```
357
+
358
+ **Auto-generated Endpoints:**
359
+ | Method | Path | Description |
360
+ |--------|------|-------------|
361
+ | GET | `/todos` | List all todos |
362
+ | GET | `/todos/:id` | Get todo by ID |
363
+ | GET | `/todos/find-one` | Find one todo by filter |
364
+ | GET | `/todos/count` | Count todos |
365
+ | POST | `/todos` | Create todo |
366
+ | PATCH | `/todos/:id` | Update todo by ID |
367
+ | PATCH | `/todos` | Update multiple todos |
368
+ | DELETE | `/todos/:id` | Delete todo by ID |
369
+ | DELETE | `/todos` | Delete multiple todos |
370
+
371
+ > **Deep Dive:** See [ControllerFactory Reference](../references/base/controllers.md#controllerfactory) for customization options.
372
+
373
+ ## Step 5: Register Components
374
+
375
+ Update `src/application.ts` to register all components:
376
+
377
+ ```typescript
378
+ // src/application.ts
379
+ import { BaseApplication, IApplicationConfigs, IApplicationInfo, ValueOrPromise } from '@venizia/ignis';
380
+ import { HelloController } from './controllers/hello.controller';
381
+ import packageJson from '../package.json';
382
+
383
+ // Import our new components
384
+ import { PostgresDataSource } from './datasources/postgres.datasource';
385
+ import { TodoRepository } from './repositories/todo.repository';
386
+ import { TodoController } from './controllers/todo.controller';
387
+
388
+ export const appConfigs: IApplicationConfigs = {
389
+ host: process.env.HOST ?? '0.0.0.0',
390
+ port: +(process.env.PORT ?? 3000),
391
+ path: { base: '/api', isStrict: true },
392
+ };
393
+
394
+ export class Application extends BaseApplication {
395
+ override getAppInfo(): ValueOrPromise<IApplicationInfo> {
396
+ return packageJson;
397
+ }
398
+
399
+ staticConfigure(): void {}
400
+
401
+ setupMiddlewares(): ValueOrPromise<void> {}
402
+
403
+ preConfigure(): ValueOrPromise<void> {
404
+ // 1. Register datasource
405
+ this.dataSource(PostgresDataSource);
406
+
407
+ // 2. Register repository
408
+ this.repository(TodoRepository);
409
+
410
+ // 3. Register controllers
411
+ this.controller(HelloController);
412
+ this.controller(TodoController);
413
+ }
414
+
415
+ postConfigure(): ValueOrPromise<void> {}
416
+ }
417
+ ```
418
+
419
+ ## Step 6: Run Database Migration
420
+
421
+ ### Understanding Database Migrations
422
+
423
+ **The Problem:**
424
+ Your database is currently empty. It has no `Todo` table. But your code expects one!
425
+
426
+ ```typescript
427
+ // Your code says:
428
+ todoTable = pgTable('Todo', { id: ..., title: ..., ... });
429
+
430
+ // But PostgreSQL doesn't have a 'Todo' table yet!
431
+ ```
432
+
433
+ **What is a Migration?**
434
+ A migration is a script that creates or modifies database tables. Think of it as "Git commits for your database schema."
435
+
436
+ **Example Migration:**
437
+ ```sql
438
+ -- This is what Drizzle will generate and run for you
439
+ CREATE TABLE "Todo" (
440
+ "id" UUID PRIMARY KEY,
441
+ "title" TEXT NOT NULL,
442
+ "description" TEXT,
443
+ "is_completed" BOOLEAN DEFAULT false,
444
+ "created_at" TIMESTAMP DEFAULT NOW(),
445
+ "modified_at" TIMESTAMP DEFAULT NOW()
446
+ );
447
+ ```
448
+
449
+ **Why not create tables manually?**
450
+ - **Team Collaboration** - Everyone runs the same migrations, databases stay in sync
451
+ - **Version Control** - Schema changes are tracked in Git
452
+ - **Rollback** - Can undo changes if something breaks
453
+
454
+ ### Create Migration Config
455
+
456
+ Create `src/migration.ts`:
457
+
458
+ ```typescript
459
+ // src/migration.ts
460
+ import { defineConfig } from 'drizzle-kit';
461
+
462
+ export default defineConfig({
463
+ schema: './src/models/todo.model.ts', // Where your model definitions are
464
+ out: './migration', // Where to save generated SQL files
465
+ dialect: 'postgresql', // Database type
466
+ dbCredentials: {
467
+ // Use the same .env values as your datasource
468
+ host: process.env.APP_ENV_POSTGRES_HOST!,
469
+ port: +(process.env.APP_ENV_POSTGRES_PORT ?? 5432),
470
+ user: process.env.APP_ENV_POSTGRES_USERNAME,
471
+ password: process.env.APP_ENV_POSTGRES_PASSWORD,
472
+ database: process.env.APP_ENV_POSTGRES_DATABASE!,
473
+ },
474
+ });
475
+ ```
476
+
477
+ ### Run the Migration
478
+
479
+ Run the migration (using the script from [Quickstart](./quickstart.md#5-run-your-application)):
480
+
481
+ ```bash
482
+ bun run migrate:dev
483
+ ```
484
+
485
+ **What happens when you run this:**
486
+
487
+ 1. **Reads** `src/models/todo.model.ts` to see what your schema looks like
488
+ 2. **Generates SQL** to create the `Todo` table
489
+ 3. **Connects** to your PostgreSQL database
490
+ 4. **Executes** the SQL to create the table
491
+ 5. **Saves** migration files to `./migration/` folder (for version control)
492
+
493
+ **Expected output:**
494
+ ```
495
+ Reading schema...
496
+ Generating migration...
497
+ Executing migration...
498
+ ✓ Done!
499
+ ```
500
+
501
+ **Verify it worked:**
502
+ ```bash
503
+ psql -U postgres -d todo_db -c "\d Todo"
504
+ ```
505
+
506
+ You should see the `Todo` table structure with all your columns!
507
+
508
+ ## Step 7: Run and Test
509
+
510
+ Start your application:
511
+
512
+ ```bash
513
+ bun run server:dev
514
+ ```
515
+
516
+ Test the API endpoints:
517
+
518
+ ```bash
519
+ # Create a todo
520
+ curl -X POST http://localhost:3000/api/todos \
521
+ -H "Content-Type: application/json" \
522
+ -d '{"title":"Learn Ignis","description":"Complete tutorial"}'
523
+
524
+ # Get all todos
525
+ curl http://localhost:3000/api/todos
526
+
527
+ # Get todo by ID (replace {id} with actual ID from create response)
528
+ curl http://localhost:3000/api/todos/{id}
529
+
530
+ # Update todo
531
+ curl -X PATCH http://localhost:3000/api/todos/{id} \
532
+ -H "Content-Type: application/json" \
533
+ -d '{"isCompleted":true}'
534
+
535
+ # Delete todo
536
+ curl -X DELETE http://localhost:3000/api/todos/{id}
537
+ ```
538
+
539
+ **View API Documentation:**
540
+ Open `http://localhost:3000/docs` in your browser to see interactive Swagger UI.
541
+
542
+ 🎉 **Congratulations!** You've built a complete CRUD API with:
543
+ - ✅ Type-safe database operations
544
+ - ✅ Automatic request validation
545
+ - ✅ Auto-generated OpenAPI documentation
546
+ - ✅ Clean, maintainable architecture
547
+
548
+ ## What Could Go Wrong? Common Errors
549
+
550
+ ### Error: "Binding 'datasources.PostgresDataSource' not found"
551
+
552
+ **Cause:** Forgot to register DataSource in `application.ts`
553
+
554
+ **Fix:**
555
+ ```typescript
556
+ // In application.ts preConfigure():
557
+ this.dataSource(PostgresDataSource); // ← Make sure this is here!
558
+ ```
559
+
560
+ **Order matters:** DataSource must be registered before Repository.
561
+
562
+ ---
563
+
564
+ ### Error: "connection refused" or "ECONNREFUSED"
565
+
566
+ **Cause:** PostgreSQL isn't running, or wrong connection details in `.env`
567
+
568
+ **Fix:**
569
+ ```bash
570
+ # Check if PostgreSQL is running:
571
+ psql -U postgres -c "SELECT 1;"
572
+
573
+ # If not running, start it:
574
+ brew services start postgresql@14 # macOS
575
+ sudo service postgresql start # Linux
576
+ ```
577
+
578
+ **Verify `.env` values match your PostgreSQL setup.**
579
+
580
+ ---
581
+
582
+ ### Error: "relation 'Todo' does not exist"
583
+
584
+ **Cause:** Forgot to run database migration
585
+
586
+ **Fix:**
587
+ ```bash
588
+ bun run migrate:dev
589
+ ```
590
+
591
+ **Verify the table exists:**
592
+ ```bash
593
+ psql -U postgres -d todo_db -c "\dt"
594
+ ```
595
+
596
+ You should see `Todo` in the list.
597
+
598
+ ---
599
+
600
+ ### Error: 404 Not Found on `/api/todos`
601
+
602
+ **Cause:** Controller not registered or wrong path configuration
603
+
604
+ **Fix:**
605
+ ```typescript
606
+ // In application.ts preConfigure():
607
+ this.controller(TodoController); // ← Make sure this is here!
608
+
609
+ // Check appConfigs:
610
+ path: { base: '/api', isStrict: true }, // All routes start with /api
611
+ ```
612
+
613
+ **Debug:** Set `debug.showRoutes: true` in appConfigs to see all registered routes on startup.
614
+
615
+ ---
616
+
617
+ ### Error: "Invalid JSON" when creating todo
618
+
619
+ **Cause:** Missing `Content-Type: application/json` header
620
+
621
+ **Fix:**
622
+ ```bash
623
+ # Make sure you include the header:
624
+ curl -X POST http://localhost:3000/api/todos \
625
+ -H "Content-Type: application/json" \ # ← This line!
626
+ -d '{"title":"Learn Ignis"}'
627
+ ```
628
+
629
+ ---
630
+
631
+ ## Test Your Understanding: Build a Second Feature
632
+
633
+ Now that you've built the Todo API, try building a **User** feature on your own!
634
+
635
+ **Requirements:**
636
+ - Create `/api/users` endpoint
637
+ - Users should have: `id`, `email`, `name`, `createdAt`, `modifiedAt`
638
+ - Use `ControllerFactory` for CRUD operations
639
+
640
+ **Challenge checklist:**
641
+ - [ ] Create `src/models/user.model.ts`
642
+ - [ ] Update `PostgresDataSource` to include User schema
643
+ - [ ] Create `src/repositories/user.repository.ts`
644
+ - [ ] Create `src/controllers/user.controller.ts`
645
+ - [ ] Register repository and controller in `application.ts`
646
+ - [ ] Run migration: `bun run migrate:dev`
647
+ - [ ] Test with curl
648
+
649
+ **Hint:** Follow the exact same pattern as `Todo`. The only changes are the model name and fields!
650
+
651
+ **Solution:** If you get stuck, check the [API Usage Examples](./best-practices/api-usage-examples.md) guide.
652
+
653
+ ---
654
+
655
+ ## Next Steps
656
+
657
+ ### Adding Business Logic with Services
658
+
659
+ For complex validation or business rules, create a Service layer:
660
+
661
+ ```typescript
662
+ // src/services/todo.service.ts
663
+ import { BaseService, inject } from '@venizia/ignis';
664
+ import { TodoRepository } from '@/repositories/todo.repository';
665
+
666
+ export class TodoService extends BaseService {
667
+ constructor(
668
+ @inject({ key: 'repositories.TodoRepository' })
669
+ private todoRepository: TodoRepository,
670
+ ) {
671
+ super({ scope: TodoService.name });
672
+ }
673
+
674
+ async createTodo(data: any) {
675
+ // Business logic validation
676
+ if (data.title.length < 3) {
677
+ throw new Error('Title too short');
678
+ }
679
+
680
+ // Check for duplicates
681
+ const existing = await this.todoRepository.findOne({
682
+ filter: { where: { title: data.title } },
683
+ });
684
+ if (existing) {
685
+ throw new Error('Todo already exists');
686
+ }
687
+
688
+ return this.todoRepository.create({ data });
689
+ }
690
+ }
691
+ ```
692
+
693
+ Register in `application.ts`:
694
+ ```typescript
695
+ this.service(TodoService);
696
+ ```
697
+
698
+ > **Deep Dive:** See [Services Reference](./core-concepts/services.md) for best practices and advanced patterns.
699
+
700
+ ## Continue Your Journey
701
+
702
+ You now have a fully functional CRUD API! Here's what to explore next:
703
+
704
+ **Core Concepts:**
705
+ 1. [Application Architecture](./core-concepts/application.md) - Understand the framework structure
706
+ 2. [Dependency Injection](./core-concepts/dependency-injection.md) - Master DI patterns
707
+ 3. [Components](./core-concepts/components.md) - Build reusable modules
708
+
709
+ **Add Features:**
710
+ 1. [Authentication](../references/components/authentication.md) - Add JWT authentication
711
+ 2. [Custom Routes](./best-practices/api-usage-examples.md) - Beyond CRUD operations
712
+ 3. [Relationships](./core-concepts/persistent.md#querying-with-relations) - Link todos to users
713
+
714
+ **Production:**
715
+ 1. [Deployment Strategies](./best-practices/deployment-strategies.md) - Deploy your API
716
+ 2. [Performance Optimization](./best-practices/performance-optimization.md) - Make it faster
717
+ 3. [Security Guidelines](./best-practices/security-guidelines.md) - Secure your API