fragment-ts 1.0.31 → 1.0.32

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 (44) hide show
  1. package/API.md +752 -0
  2. package/DOCS.md +555 -0
  3. package/README.md +76 -339
  4. package/USAGE.md +309 -1306
  5. package/dist/cli/commands/init.command.js +1 -1
  6. package/dist/core/decorators/exception-filter.decorator.d.ts +5 -0
  7. package/dist/core/decorators/exception-filter.decorator.d.ts.map +1 -0
  8. package/dist/core/decorators/exception-filter.decorator.js +23 -0
  9. package/dist/core/decorators/exception-filter.decorator.js.map +1 -0
  10. package/dist/core/decorators/guard.decorator.d.ts +5 -0
  11. package/dist/core/decorators/guard.decorator.d.ts.map +1 -0
  12. package/dist/core/decorators/guard.decorator.js +23 -0
  13. package/dist/core/decorators/guard.decorator.js.map +1 -0
  14. package/dist/core/decorators/interceptor.decorator.d.ts +5 -0
  15. package/dist/core/decorators/interceptor.decorator.d.ts.map +1 -0
  16. package/dist/core/decorators/interceptor.decorator.js +23 -0
  17. package/dist/core/decorators/interceptor.decorator.js.map +1 -0
  18. package/dist/core/decorators/middleware.decorator.d.ts +8 -0
  19. package/dist/core/decorators/middleware.decorator.d.ts.map +1 -0
  20. package/dist/core/decorators/middleware.decorator.js +28 -0
  21. package/dist/core/decorators/middleware.decorator.js.map +1 -0
  22. package/dist/core/metadata/metadata-keys.d.ts +1 -0
  23. package/dist/core/metadata/metadata-keys.d.ts.map +1 -1
  24. package/dist/core/metadata/metadata-keys.js +1 -0
  25. package/dist/core/metadata/metadata-keys.js.map +1 -1
  26. package/dist/core/metadata/metadata-storage.d.ts +18 -0
  27. package/dist/core/metadata/metadata-storage.d.ts.map +1 -1
  28. package/dist/core/metadata/metadata-storage.js +82 -0
  29. package/dist/core/metadata/metadata-storage.js.map +1 -1
  30. package/dist/web/application.d.ts.map +1 -1
  31. package/dist/web/application.js +74 -5
  32. package/dist/web/application.js.map +1 -1
  33. package/dist/web/interfaces.d.ts +5 -1
  34. package/dist/web/interfaces.d.ts.map +1 -1
  35. package/package.json +1 -1
  36. package/src/cli/commands/init.command.ts +1 -1
  37. package/src/core/decorators/exception-filter.decorator.ts +20 -0
  38. package/src/core/decorators/guard.decorator.ts +20 -0
  39. package/src/core/decorators/interceptor.decorator.ts +20 -0
  40. package/src/core/decorators/middleware.decorator.ts +25 -0
  41. package/src/core/metadata/metadata-keys.ts +1 -0
  42. package/src/core/metadata/metadata-storage.ts +114 -0
  43. package/src/web/application.ts +141 -6
  44. package/src/web/interfaces.ts +11 -2
package/USAGE.md CHANGED
@@ -1,1439 +1,442 @@
1
- # Fragment Framework - Comprehensive Use Cases & Examples
1
+ # Usage Guide
2
2
 
3
- ## Table of Contents
3
+ This guide demonstrates practical usage patterns for Fragment TS framework.
4
4
 
5
- - [Quick Start](#quick-start)
6
- - [Basic REST API](#basic-rest-api)
7
- - [Authentication & Authorization](#authentication--authorization)
8
- - [Database Operations](#database-operations)
9
- - [AI-Powered Applications](#ai-powered-applications)
10
- - [Dependency Injection Patterns](#dependency-injection-patterns)
11
- - [Advanced Routing](#advanced-routing)
12
- - [Custom Middleware](#custom-middleware)
13
- - [Testing](#testing)
14
- - [Production Deployment](#production-deployment)
15
- - [CLI Reference](#cli-reference)
16
- - [Best Practices](#best-practices)
17
- - [Troubleshooting](#troubleshooting)
5
+ ## Project Structure
18
6
 
19
- ---
20
-
21
- ## Quick Start
22
-
23
- ### Installation & Project Initialization
24
-
25
- ```bash
26
- # Install Fragment CLI globally
27
- npm install -g fragment
28
-
29
- # Option 1: Initialize in current directory
30
- mkdir my-app && cd my-app
31
- fragment init .
32
-
33
- # Option 2: Create new directory and initialize
34
- fragment init my-app
35
- cd my-app
36
-
37
- # Option 3: Use specific template
38
- fragment init my-api --template=api
39
-
40
- # Option 4: Select features interactively
41
- fragment init my-app --features=auth,ai,database
42
-
43
- # Option 5: Skip npm install (install manually later)
44
- fragment init my-app --skip-install
45
- ```
46
-
47
- **Yes, `fragment init .` initializes in the current directory!**
48
-
49
- ### Start Development
50
-
51
- ```bash
52
- # Start with hot reload (auto-restarts on file changes)
53
- fragment serve
54
-
55
- # Start on custom port
56
- fragment serve --port=4000
57
-
58
- # Start without watch mode
59
- fragment serve --no-watch
60
-
61
- # Build for production
62
- fragment build
63
-
64
- # Run production build
65
- npm start
66
7
  ```
67
-
68
- ---
69
-
70
- ## Basic REST API
71
-
72
- ### Use Case 1: Simple CRUD API
73
-
74
- Create a complete REST API for managing products.
75
-
76
- ```bash
77
- # Generate complete resource (controller, service, entity, DTO, repository)
78
- fragment generate resource product
79
-
80
- # Or use shorthand
81
- fragment g resource product
8
+ src/
9
+ ├── app.ts # Application entry point
10
+ ├── config/
11
+ │ └── fragment.json # TypeORM configuration
12
+ ├── controllers/
13
+ │ ├── user.controller.ts
14
+ │ └── auth.controller.ts
15
+ ├── services/
16
+ │ ├── user.service.ts
17
+ │ └── auth.service.ts
18
+ ├── repositories/
19
+ │ └── user.repository.ts
20
+ ├── entities/
21
+ │ └── user.entity.ts
22
+ └── dtos/
23
+ └── create-user.dto.ts
82
24
  ```
83
25
 
84
- **Generated Files:**
26
+ ## Dependency Injection
85
27
 
86
- **src/controllers/product.controller.ts**
28
+ ### Basic Property Injection
87
29
 
88
30
  ```typescript
89
- import { Controller, Get, Post, Put, Delete, Body, Param } from "fragment-ts";
90
- import { ProductService } from "../services/product.service";
31
+ import { Service, Autowired } from 'fragment-ts';
32
+ import { UserRepository } from '../repositories/user.repository';
33
+ import { LoggerService } from './logger.service';
91
34
 
92
- @Controller("/products")
93
- export class ProductController {
94
- constructor(private productService: ProductService) {}
95
-
96
- @Get()
35
+ @Service()
36
+ export class UserService {
37
+ @Autowired()
38
+ private userRepository!: UserRepository;
39
+
40
+ @Autowired()
41
+ private logger!: LoggerService;
42
+
97
43
  async findAll() {
98
- return this.productService.findAll();
99
- }
100
-
101
- @Get("/:id")
102
- async findOne(@Param("id") id: string) {
103
- return this.productService.findOne(id);
104
- }
105
-
106
- @Post()
107
- async create(@Body() body: any) {
108
- return this.productService.create(body);
109
- }
110
-
111
- @Put("/:id")
112
- async update(@Param("id") id: string, @Body() body: any) {
113
- return this.productService.update(id, body);
114
- }
115
-
116
- @Delete("/:id")
117
- async delete(@Param("id") id: string) {
118
- return this.productService.delete(id);
44
+ this.logger.info('Fetching all users');
45
+ return this.userRepository.findAll();
119
46
  }
120
47
  }
121
48
  ```
122
49
 
123
- **src/services/product.service.ts**
50
+ ### Optional Dependencies
124
51
 
125
52
  ```typescript
126
- import { Service } from "fragment-ts";
127
- import { ProductRepository } from "../repositories/product.repository";
53
+ import { Service, Autowired, Optional } from 'fragment-ts';
54
+ import { CacheService } from './cache.service';
128
55
 
129
56
  @Service()
130
57
  export class ProductService {
131
- constructor(private productRepository: ProductRepository) {}
132
-
133
- async findAll() {
134
- return this.productRepository.findAll();
135
- }
136
-
137
- async findOne(id: string) {
138
- const product = await this.productRepository.findById(parseInt(id));
139
- if (!product) {
140
- throw new Error("Product not found");
58
+ @Autowired()
59
+ @Optional()
60
+ private cacheService?: CacheService;
61
+
62
+ async getProduct(id: string) {
63
+ if (this.cacheService) {
64
+ const cached = await this.cacheService.get(`product:${id}`);
65
+ if (cached) return cached;
141
66
  }
67
+
68
+ // Fetch from database
69
+ const product = await this.productRepository.findById(id);
70
+
71
+ if (this.cacheService) {
72
+ await this.cacheService.set(`product:${id}`, product);
73
+ }
74
+
142
75
  return product;
143
76
  }
144
-
145
- async create(data: any) {
146
- return this.productRepository.create(data);
147
- }
148
-
149
- async update(id: string, data: any) {
150
- return this.productRepository.update(parseInt(id), data);
151
- }
152
-
153
- async delete(id: string) {
154
- return this.productRepository.delete(parseInt(id));
155
- }
156
- }
157
- ```
158
-
159
- **src/entities/product.entity.ts**
160
-
161
- ```typescript
162
- import { Entity, PrimaryGeneratedColumn, Column } from "typeorm";
163
-
164
- @Entity()
165
- export class Product {
166
- @PrimaryGeneratedColumn()
167
- id: number;
168
-
169
- @Column()
170
- name: string;
171
-
172
- @Column("decimal", { precision: 10, scale: 2 })
173
- price: number;
174
-
175
- @Column({ nullable: true })
176
- description: string;
177
-
178
- @Column({ default: 0 })
179
- stock: number;
180
-
181
- @Column({ type: "timestamp", default: () => "CURRENT_TIMESTAMP" })
182
- createdAt: Date;
183
-
184
- @Column({ type: "timestamp", default: () => "CURRENT_TIMESTAMP" })
185
- updatedAt: Date;
186
77
  }
187
78
  ```
188
79
 
189
- **Testing the API:**
190
-
191
- ```bash
192
- # Start server
193
- fragment serve
194
-
195
- # Verify routes are registered
196
- fragment routes
197
-
198
- # Test endpoints
199
- curl http://localhost:3000/products
200
- curl -X POST http://localhost:3000/products \
201
- -H "Content-Type: application/json" \
202
- -d '{"name":"Laptop","price":999.99,"stock":10}'
203
- curl http://localhost:3000/products/1
204
- curl -X PUT http://localhost:3000/products/1 \
205
- -H "Content-Type: application/json" \
206
- -d '{"price":899.99}'
207
- curl -X DELETE http://localhost:3000/products/1
208
- ```
209
-
210
- ---
211
-
212
- ## Authentication & Authorization
213
-
214
- ### Use Case 2: User Authentication System
215
-
216
- Complete authentication with JWT tokens, password hashing, and role-based access.
217
-
218
- ```bash
219
- # Initialize with auth feature
220
- fragment init auth-api --features=auth,database
221
- cd auth-api
222
-
223
- # Generate user resource
224
- fragment generate resource user
225
- ```
226
-
227
- **src/entities/user.entity.ts**
80
+ ### Value Injection (Environment Variables)
228
81
 
229
82
  ```typescript
230
- import { Entity, PrimaryGeneratedColumn, Column } from "typeorm";
231
-
232
- @Entity()
233
- export class User {
234
- @PrimaryGeneratedColumn()
235
- id: number;
236
-
237
- @Column({ unique: true })
238
- email: string;
239
-
240
- @Column()
241
- password: string;
242
-
243
- @Column()
244
- name: string;
245
-
246
- @Column("simple-array", { default: "user" })
247
- roles: string[];
248
-
249
- @Column({ type: "timestamp", default: () => "CURRENT_TIMESTAMP" })
250
- createdAt: Date;
251
- }
252
- ```
253
-
254
- **src/services/auth.service.ts**
255
-
256
- ```typescript
257
- import { Service } from "fragment";
258
- import { AuthModule, NotFoundError, UnauthorizedError } from "fragment-ts";
259
- import { UserRepository } from "../repositories/user.repository";
83
+ import { Service, Value } from 'fragment-ts';
260
84
 
261
85
  @Service()
262
- export class AuthService {
263
- constructor(private userRepository: UserRepository) {}
264
-
265
- async register(email: string, password: string, name: string) {
266
- // Hash password
267
- const hashedPassword = await AuthModule.hashPassword(password);
268
-
269
- // Create user
270
- const user = await this.userRepository.create({
271
- email,
272
- password: hashedPassword,
273
- name,
274
- roles: ["user"],
275
- });
276
-
277
- // Generate token
278
- const token = AuthModule.generateToken({
279
- userId: user.id.toString(),
280
- email: user.email,
281
- roles: user.roles,
282
- });
283
-
284
- return { user: { id: user.id, email, name }, token };
285
- }
286
-
287
- async login(email: string, password: string) {
288
- // Find user
289
- const user = await this.userRepository.findByEmail(email);
290
- if (!user) {
291
- throw new UnauthorizedError("Invalid credentials");
292
- }
293
-
294
- // Verify password
295
- const isValid = await AuthModule.comparePassword(password, user.password);
296
- if (!isValid) {
297
- throw new UnauthorizedError("Invalid credentials");
298
- }
299
-
300
- // Generate token
301
- const token = AuthModule.generateToken({
302
- userId: user.id.toString(),
303
- email: user.email,
304
- roles: user.roles,
305
- });
306
-
307
- return { user: { id: user.id, email, name: user.name }, token };
308
- }
309
-
310
- async getCurrentUser(userId: string) {
311
- const user = await this.userRepository.findById(parseInt(userId));
312
- if (!user) {
313
- throw new NotFoundError("User not found");
314
- }
315
- return { id: user.id, email: user.email, name: user.name };
86
+ export class ConfigService {
87
+ @Value('${PORT:3000}')
88
+ private port!: number;
89
+
90
+ @Value('${DB_HOST:localhost}')
91
+ private dbHost!: string;
92
+
93
+ @Value('${FEATURE_ENABLED:false}')
94
+ private featureEnabled!: boolean;
95
+
96
+ getPort(): number {
97
+ return this.port;
98
+ }
99
+
100
+ isFeatureEnabled(): boolean {
101
+ return this.featureEnabled;
316
102
  }
317
103
  }
318
104
  ```
319
105
 
320
- **src/controllers/auth.controller.ts**
106
+ ## Controllers & Routes
321
107
 
322
- ```typescript
323
- import { Controller, Post, Get, Body, Req } from "fragment-ts";
324
- import { AuthModule } from "fragment-ts";
325
- import { AuthService } from "../services/auth.service";
108
+ ### Basic Controller
326
109
 
327
- @Controller("/auth")
328
- export class AuthController {
329
- constructor(private authService: AuthService) {}
110
+ ```typescript
111
+ import { Controller, Get, Post, Body, Param, Query } from 'fragment-ts';
112
+ import { UserService } from '../services/user.service';
113
+ import { CreateUserDto } from '../dtos/create-user.dto';
330
114
 
331
- @Post("/register")
332
- async register(@Body() body: any) {
333
- return this.authService.register(body.email, body.password, body.name);
115
+ @Controller('/users')
116
+ export class UserController {
117
+ constructor(private userService: UserService) {}
118
+
119
+ @Get()
120
+ async findAll(@Query('page') page: number = 1) {
121
+ return this.userService.findAll(page);
334
122
  }
335
-
336
- @Post("/login")
337
- async login(@Body() body: any) {
338
- return this.authService.login(body.email, body.password);
123
+
124
+ @Get('/:id')
125
+ async findById(@Param('id') id: string) {
126
+ return this.userService.findById(id);
339
127
  }
340
- }
341
-
342
- @Controller("/api")
343
- export class ProtectedController {
344
- constructor(private authService: AuthService) {}
345
-
346
- // Protected route - requires authentication
347
- @Get("/me")
348
- async getProfile(@Req() req: any) {
349
- // In production, use middleware to attach user to request
350
- const authHeader = req.headers.authorization;
351
- if (!authHeader) {
352
- throw new UnauthorizedError("Missing authorization header");
353
- }
354
-
355
- const token = authHeader.replace("Bearer ", "");
356
- const payload = AuthModule.verifyToken(token);
357
-
358
- return this.authService.getCurrentUser(payload.userId);
128
+
129
+ @Post()
130
+ async create(@Body() createUserDto: CreateUserDto) {
131
+ return this.userService.create(createUserDto);
359
132
  }
360
133
  }
361
134
  ```
362
135
 
363
- **src/main.ts - Add Auth Middleware**
136
+ ### Route Parameters and Query Parameters
364
137
 
365
138
  ```typescript
366
- import "reflect-metadata";
367
- import { FragmentApplication } from "fragment-ts";
368
- import { FragmentWebApplication } from "fragment-ts";
369
- import { AuthModule } from "fragment-ts";
370
-
371
- @FragmentApplication({
372
- port: 3000,
373
- autoScan: true,
374
- })
375
- class Application {}
376
-
377
- async function bootstrap() {
378
- const app = new FragmentWebApplication();
379
-
380
- // Apply auth middleware to protected routes
381
- const expressApp = app.getExpressApp();
382
- expressApp.use("/api/*", AuthModule.authMiddleware());
383
-
384
- // Apply role-based guards
385
- expressApp.use("/admin/*", AuthModule.roleGuard("admin"));
386
-
387
- await app.bootstrap(Application);
388
- }
389
-
390
- bootstrap();
391
- ```
392
-
393
- **Testing Authentication:**
394
-
395
- ```bash
396
- # Register
397
- curl -X POST http://localhost:3000/auth/register \
398
- -H "Content-Type: application/json" \
399
- -d '{"email":"user@example.com","password":"secret123","name":"John Doe"}'
400
-
401
- # Response: {"user":{"id":1,"email":"user@example.com","name":"John Doe"},"token":"eyJ..."}
402
-
403
- # Login
404
- curl -X POST http://localhost:3000/auth/login \
405
- -H "Content-Type: application/json" \
406
- -d '{"email":"user@example.com","password":"secret123"}'
407
-
408
- # Access protected route
409
- curl http://localhost:3000/api/me \
410
- -H "Authorization: Bearer YOUR_TOKEN_HERE"
411
- ```
139
+ import { Controller, Get, Param, Query } from 'fragment-ts';
412
140
 
413
- ---
414
-
415
- ## Database Operations
416
-
417
- ### Use Case 3: Advanced Database Migrations
418
-
419
- ```bash
420
- # Create migration
421
- fragment migrate:create AddProductCategories
422
-
423
- # Run all pending migrations
424
- fragment migrate:run
425
-
426
- # Check migration status
427
- fragment migrate:status
428
-
429
- # Revert last migration
430
- fragment migrate:revert
431
-
432
- # Refresh all migrations (drop and re-run - CAREFUL!)
433
- fragment migrate:refresh
434
-
435
- # Sync schema (development only - not for production!)
436
- fragment schema:sync
437
-
438
- # Drop all tables (DANGEROUS!)
439
- fragment schema:drop
440
- ```
441
-
442
- **Example Migration: src/migrations/1234567890-AddProductCategories.ts**
443
-
444
- ```typescript
445
- import {
446
- MigrationInterface,
447
- QueryRunner,
448
- Table,
449
- TableForeignKey,
450
- } from "typeorm";
451
-
452
- export class AddProductCategories1234567890 implements MigrationInterface {
453
- async up(queryRunner: QueryRunner): Promise<void> {
454
- // Create categories table
455
- await queryRunner.createTable(
456
- new Table({
457
- name: "category",
458
- columns: [
459
- {
460
- name: "id",
461
- type: "int",
462
- isPrimary: true,
463
- isGenerated: true,
464
- generationStrategy: "increment",
465
- },
466
- { name: "name", type: "varchar", isUnique: true },
467
- { name: "description", type: "text", isNullable: true },
468
- {
469
- name: "createdAt",
470
- type: "timestamp",
471
- default: "CURRENT_TIMESTAMP",
472
- },
473
- ],
474
- }),
475
- );
476
-
477
- // Add category column to products
478
- await queryRunner.query(`ALTER TABLE product ADD COLUMN categoryId INT`);
479
-
480
- // Add foreign key
481
- await queryRunner.createForeignKey(
482
- "product",
483
- new TableForeignKey({
484
- columnNames: ["categoryId"],
485
- referencedTableName: "category",
486
- referencedColumnNames: ["id"],
487
- onDelete: "SET NULL",
488
- }),
489
- );
141
+ @Controller('/products')
142
+ export class ProductController {
143
+ @Get()
144
+ async search(
145
+ @Query('q') query: string,
146
+ @Query('category') category?: string,
147
+ @Query('minPrice') minPrice?: number,
148
+ @Query('maxPrice') maxPrice?: number
149
+ ) {
150
+ // Implementation
490
151
  }
491
-
492
- async down(queryRunner: QueryRunner): Promise<void> {
493
- // Drop foreign key first
494
- const table = await queryRunner.getTable("product");
495
- const foreignKey = table?.foreignKeys.find(
496
- (fk) => fk.columnNames.indexOf("categoryId") !== -1,
497
- );
498
- if (foreignKey) {
499
- await queryRunner.dropForeignKey("product", foreignKey);
500
- }
501
-
502
- // Drop column and table
503
- await queryRunner.dropColumn("product", "categoryId");
504
- await queryRunner.dropTable("category");
152
+
153
+ @Get('/:id/reviews')
154
+ async getProductReviews(
155
+ @Param('id') productId: string,
156
+ @Query('rating') minRating?: number
157
+ ) {
158
+ // Implementation
505
159
  }
506
160
  }
507
161
  ```
508
162
 
509
- ### Database Seeding
510
-
511
- ```bash
512
- # Create seed
513
- fragment seed:create InitialData
514
-
515
- # Run all seeds
516
- fragment seed
517
- ```
163
+ ## Repositories
518
164
 
519
- **src/seeds/InitialData.seed.ts**
165
+ ### TypeORM Repository Injection
520
166
 
521
167
  ```typescript
522
- import { TypeORMModule } from "fragment-ts";
523
-
524
- export default class InitialDataSeed {
525
- static async run() {
526
- const dataSource = TypeORMModule.getDataSource();
527
-
528
- // Seed categories
529
- await dataSource.query(`
530
- INSERT INTO category (name, description) VALUES
531
- ('Electronics', 'Electronic devices and accessories'),
532
- ('Clothing', 'Apparel and fashion items'),
533
- ('Books', 'Physical and digital books')
534
- `);
535
-
536
- // Seed products
537
- await dataSource.query(`
538
- INSERT INTO product (name, price, stock, categoryId) VALUES
539
- ('Laptop', 999.99, 50, 1),
540
- ('T-Shirt', 29.99, 200, 2),
541
- ('Novel', 14.99, 100, 3)
542
- `);
543
-
544
- console.log("✅ Initial data seeded successfully");
168
+ import { Repository, InjectRepository } from 'fragment-ts';
169
+ import { User } from '../entities/user.entity';
170
+ import { Repository as TypeOrmRepository } from 'typeorm';
171
+
172
+ @Repository()
173
+ export class UserRepository {
174
+ @InjectRepository(User)
175
+ private repo!: TypeOrmRepository<User>;
176
+
177
+ async findAll(): Promise<User[]> {
178
+ return this.repo.find();
179
+ }
180
+
181
+ async findById(id: string): Promise<User | null> {
182
+ return this.repo.findOneBy({ id });
183
+ }
184
+
185
+ async save(user: User): Promise<User> {
186
+ return this.repo.save(user);
545
187
  }
546
188
  }
547
189
  ```
548
190
 
549
- ---
550
-
551
- ## AI-Powered Applications
552
-
553
- ### Use Case 4: AI Chat Service
554
-
555
- ```bash
556
- # Initialize with AI feature
557
- fragment init ai-app --features=ai
558
- cd ai-app
559
-
560
- # Generate AI components
561
- fragment generate controller chat
562
- fragment generate service chat
563
- ```
564
-
565
- **src/services/chat.service.ts**
191
+ ### Custom Repository Methods
566
192
 
567
193
  ```typescript
568
- import { Service } from "fragment-ts";
569
- import { AIModule } from "fragment-ts";
570
-
571
- @Service()
572
- export class ChatService {
573
- private aiModule: AIModule;
574
-
575
- constructor() {
576
- this.aiModule = new AIModule({
577
- openaiKey: process.env.OPENAI_API_KEY,
578
- ollamaUrl: process.env.OLLAMA_URL,
579
- });
580
- }
581
-
582
- async chat(message: string, history: any[] = []) {
583
- const messages = [
584
- { role: "system" as const, content: "You are a helpful assistant." },
585
- ...history,
586
- { role: "user" as const, content: message },
587
- ];
588
-
589
- const response = await this.aiModule.complete(messages, {
590
- model: "gpt-3.5-turbo",
591
- temperature: 0.7,
592
- maxTokens: 500,
194
+ import { Repository, InjectRepository } from 'fragment-ts';
195
+ import { User } from '../entities/user.entity';
196
+ import { Repository as TypeOrmRepository } from 'typeorm';
197
+
198
+ @Repository()
199
+ export class UserRepository {
200
+ @InjectRepository(User)
201
+ private repo!: TypeOrmRepository<User>;
202
+
203
+ async findByEmail(email: string): Promise<User | null> {
204
+ return this.repo.findOneBy({ email });
205
+ }
206
+
207
+ async findActiveUsers(): Promise<User[]> {
208
+ return this.repo.find({ where: { isActive: true } });
209
+ }
210
+
211
+ async findWithRelations(id: string): Promise<User | null> {
212
+ return this.repo.findOne({
213
+ where: { id },
214
+ relations: ['orders', 'profile']
593
215
  });
594
-
595
- return {
596
- response,
597
- history: [
598
- ...history,
599
- { role: "user", content: message },
600
- { role: "assistant", content: response },
601
- ],
602
- };
603
- }
604
-
605
- async streamChat(message: string, onChunk: (chunk: string) => void) {
606
- const messages = [
607
- { role: "system" as const, content: "You are a helpful assistant." },
608
- { role: "user" as const, content: message },
609
- ];
610
-
611
- await this.aiModule.streamComplete(
612
- messages,
613
- { model: "gpt-3.5-turbo" },
614
- onChunk,
615
- );
616
- }
617
-
618
- async generateEmbeddings(texts: string[]) {
619
- return this.aiModule.embeddings(texts);
620
216
  }
621
217
  }
622
218
  ```
623
219
 
624
- **src/controllers/chat.controller.ts**
625
-
626
- ```typescript
627
- import { Controller, Post, Body, Res } from "fragment-ts";
628
- import { ChatService } from "../services/chat.service";
629
- import { Response } from "express";
220
+ ## Conditional Components
630
221
 
631
- @Controller("/chat")
632
- export class ChatController {
633
- constructor(private chatService: ChatService) {}
222
+ ### Conditional On Class
634
223
 
635
- @Post("/message")
636
- async sendMessage(@Body() body: any) {
637
- return this.chatService.chat(body.message, body.history || []);
638
- }
639
-
640
- @Post("/stream")
641
- async streamMessage(@Body() body: any, @Res() res: Response) {
642
- res.setHeader("Content-Type", "text/event-stream");
643
- res.setHeader("Cache-Control", "no-cache");
644
- res.setHeader("Connection", "keep-alive");
645
-
646
- await this.chatService.streamChat(body.message, (chunk) => {
647
- res.write(`data: ${JSON.stringify({ chunk })}\n\n`);
648
- });
649
-
650
- res.write("data: [DONE]\n\n");
651
- res.end();
652
- }
653
-
654
- @Post("/embeddings")
655
- async getEmbeddings(@Body() body: any) {
656
- const embeddings = await this.chatService.generateEmbeddings(body.texts);
657
- return { embeddings };
658
- }
224
+ ```typescript
225
+ import { AutoConfiguration, ConditionalOnClass } from 'fragment-ts';
226
+ import { RedisCacheService } from './redis-cache.service';
227
+ import { Redis } from 'ioredis';
228
+
229
+ @AutoConfiguration()
230
+ @ConditionalOnClass(Redis)
231
+ export class RedisAutoConfiguration {
232
+ // This configuration only loads if Redis class is available
659
233
  }
660
234
  ```
661
235
 
662
- **.env Configuration:**
663
-
664
- ```env
665
- NODE_ENV=development
666
- PORT=3000
667
- OPENAI_API_KEY=sk-your-key-here
668
- OLLAMA_URL=http://localhost:11434
669
- ```
670
-
671
- **Testing AI Endpoints:**
672
-
673
- ```bash
674
- # Chat
675
- curl -X POST http://localhost:3000/chat/message \
676
- -H "Content-Type: application/json" \
677
- -d '{"message":"What is TypeScript?"}'
678
-
679
- # Stream (watch responses appear in real-time)
680
- curl -X POST http://localhost:3000/chat/stream \
681
- -H "Content-Type: application/json" \
682
- -d '{"message":"Write a short story"}' \
683
- --no-buffer
684
-
685
- # Embeddings (vector representations of text)
686
- curl -X POST http://localhost:3000/chat/embeddings \
687
- -H "Content-Type: application/json" \
688
- -d '{"texts":["Hello world","Fragment Framework"]}'
689
- ```
690
-
691
- ---
692
-
693
- ## Dependency Injection Patterns
694
-
695
- ### Use Case 5: Complex DI with Multiple Services
236
+ ### Conditional On Property
696
237
 
697
238
  ```typescript
698
- // src/services/email.service.ts
699
- import { Service } from "fragment-ts";
239
+ import { Service, ConditionalOnProperty } from 'fragment-ts';
240
+ import { CloudStorageService } from './cloud-storage.service';
700
241
 
701
242
  @Service()
702
- export class EmailService {
703
- async sendEmail(to: string, subject: string, body: string) {
704
- console.log(`Sending email to ${to}: ${subject}`);
705
- // Email sending logic (SendGrid, Mailgun, etc.)
706
- return { sent: true };
707
- }
708
- }
709
-
710
- // src/services/notification.service.ts
711
- import { Service, Autowired } from "fragment-ts";
712
- import { EmailService } from "./email.service";
713
-
714
- @Service()
715
- export class NotificationService {
716
- // Property injection using @Autowired
717
- @Autowired()
718
- private emailService: EmailService;
719
-
720
- async notifyUser(userId: string, message: string) {
721
- // Send email notification
722
- await this.emailService.sendEmail(
723
- `user${userId}@example.com`,
724
- "Notification",
725
- message,
726
- );
727
- }
728
- }
729
-
730
- // src/services/order.service.ts
731
- import { Service } from "fragment-ts";
732
- import { NotificationService } from "./notification.service";
733
- import { ProductRepository } from "../repositories/product.repository";
734
-
735
- @Service()
736
- export class OrderService {
737
- // Constructor injection
738
- constructor(
739
- private notificationService: NotificationService,
740
- private productRepository: ProductRepository,
741
- ) {}
742
-
743
- async createOrder(userId: string, productId: number, quantity: number) {
744
- const product = await this.productRepository.findById(productId);
745
-
746
- if (!product || product.stock < quantity) {
747
- throw new Error("Insufficient stock");
748
- }
749
-
750
- // Update stock
751
- await this.productRepository.update(productId, {
752
- stock: product.stock - quantity,
753
- });
754
-
755
- // Notify user
756
- await this.notificationService.notifyUser(
757
- userId,
758
- `Your order for ${quantity}x ${product.name} has been placed!`,
759
- );
760
-
761
- return { orderId: Date.now(), product, quantity };
762
- }
243
+ @ConditionalOnProperty('USE_CLOUD_STORAGE', 'true')
244
+ export class CloudStorageService {
245
+ // Only registered if USE_CLOUD_STORAGE env var is 'true'
763
246
  }
764
247
  ```
765
248
 
766
- ---
767
-
768
- ## Advanced Routing
769
-
770
- ### Use Case 6: Complex Route Parameters
249
+ ### Conditional On Missing Bean
771
250
 
772
251
  ```typescript
773
- import {
774
- Controller,
775
- Get,
776
- Post,
777
- Param,
778
- Query,
779
- Header,
780
- Body,
781
- Req,
782
- Res,
783
- } from "fragment-ts";
784
- import { Request, Response } from "express";
785
-
786
- @Controller("/api/v1/users")
787
- export class UserController {
788
- // Nested route parameters
789
- // Route: GET /api/v1/users/:userId/orders/:orderId
790
- @Get("/:userId/orders/:orderId")
791
- async getUserOrder(
792
- @Param("userId") userId: string,
793
- @Param("orderId") orderId: string,
794
- ) {
795
- return { userId, orderId };
796
- }
797
-
798
- // Query parameters
799
- // Route: GET /api/v1/users?page=1&limit=10&sort=name
800
- @Get()
801
- async getUsers(
802
- @Query("page") page: string,
803
- @Query("limit") limit: string,
804
- @Query("sort") sort: string,
805
- ) {
806
- return {
807
- page: parseInt(page) || 1,
808
- limit: parseInt(limit) || 10,
809
- sort: sort || "id",
810
- users: [],
811
- };
812
- }
813
-
814
- // Header extraction
815
- @Get("/profile")
816
- async getProfile(@Header("Authorization") auth: string) {
817
- return { authorized: !!auth };
818
- }
252
+ import { Service, ConditionalOnMissingBean } from 'fragment-ts';
253
+ import { DefaultLoggerService } from './default-logger.service';
254
+ import { LoggerService } from './logger.service.interface';
819
255
 
820
- // Complex body handling
821
- @Post("/bulk")
822
- async bulkCreate(@Body() body: any) {
823
- const users = body.users || [];
824
- return { created: users.length };
825
- }
826
-
827
- // Full request/response access
828
- @Get("/advanced")
829
- async advancedRoute(@Req() req: Request, @Res() res: Response) {
830
- res.status(200).json({
831
- method: req.method,
832
- path: req.path,
833
- headers: req.headers,
834
- });
835
- }
256
+ @Service()
257
+ @ConditionalOnMissingBean(LoggerService)
258
+ export class DefaultLoggerService implements LoggerService {
259
+ // Only registered if no other LoggerService is available
836
260
  }
837
261
  ```
838
262
 
839
- ---
840
-
841
- ## Custom Middleware
263
+ ## Lifecycle Hooks
842
264
 
843
- ### Use Case 7: Request Logging Middleware
265
+ ### PostConstruct & PreDestroy
844
266
 
845
267
  ```typescript
846
- // src/middlewares/logger.middleware.ts
847
- import { Request, Response, NextFunction } from "express";
848
-
849
- export class LoggerMiddleware {
850
- use(req: Request, res: Response, next: NextFunction) {
851
- const start = Date.now();
852
-
853
- res.on("finish", () => {
854
- const duration = Date.now() - start;
855
- console.log(
856
- `${req.method} ${req.path} - ${res.statusCode} - ${duration}ms`,
857
- );
858
- });
268
+ import { Service, PostConstruct, PreDestroy } from 'fragment-ts';
269
+ import { ConnectionPool } from './connection-pool';
859
270
 
860
- next();
271
+ @Service()
272
+ export class DatabaseService {
273
+ private connectionPool!: ConnectionPool;
274
+
275
+ @PostConstruct()
276
+ init() {
277
+ console.log('Initializing database connection pool');
278
+ this.connectionPool = new ConnectionPool();
279
+ }
280
+
281
+ getConnection() {
282
+ return this.connectionPool.getConnection();
283
+ }
284
+
285
+ @PreDestroy()
286
+ cleanup() {
287
+ console.log('Cleaning up database connections');
288
+ this.connectionPool.closeAll();
861
289
  }
862
290
  }
863
-
864
- // src/main.ts
865
- import "reflect-metadata";
866
- import { FragmentApplication, FragmentWebApplication } from "fragment-ts";
867
- import { LoggerMiddleware } from "./middlewares/logger.middleware";
868
-
869
- @FragmentApplication({
870
- port: 3000,
871
- autoScan: true,
872
- })
873
- class Application {}
874
-
875
- async function bootstrap() {
876
- const app = new FragmentWebApplication();
877
- const expressApp = app.getExpressApp();
878
-
879
- // Apply middleware
880
- const logger = new LoggerMiddleware();
881
- expressApp.use(logger.use.bind(logger));
882
-
883
- await app.bootstrap(Application);
884
- }
885
-
886
- bootstrap();
887
291
  ```
888
292
 
889
- ---
890
-
891
293
  ## Testing
892
294
 
893
- ### Use Case 8: Comprehensive Testing
894
-
895
- ```bash
896
- # Generate test file alongside controller
897
- fragment generate controller users
898
- ```
899
-
900
- **test/users.spec.ts**
295
+ ### Unit Testing a Service
901
296
 
902
297
  ```typescript
903
- import { describe, it, expect } from "fragment-ts";
904
- import { DIContainer } from "fragment-ts";
905
- import { UserService } from "../src/services/user.service";
298
+ import { UserService } from './user.service';
299
+ import { UserRepository } from '../repositories/user.repository';
300
+ import { User } from '../entities/user.entity';
906
301
 
907
- describe("UserService", () => {
302
+ describe('UserService', () => {
908
303
  let userService: UserService;
909
-
304
+ let mockUserRepository: jest.Mocked<UserRepository>;
305
+
910
306
  beforeEach(() => {
911
- const container = DIContainer.getInstance();
912
- userService = container.resolve<UserService>(UserService);
913
- });
914
-
915
- it("should create a user", async () => {
916
- const user = await userService.create({
917
- email: "test@example.com",
918
- name: "Test User",
919
- password: "password123",
307
+ mockUserRepository = {
308
+ findAll: jest.fn(),
309
+ findById: jest.fn(),
310
+ save: jest.fn()
311
+ } as any;
312
+
313
+ userService = new UserService();
314
+ // Use reflection to set the private property
315
+ Object.defineProperty(userService, 'userRepository', {
316
+ value: mockUserRepository
920
317
  });
921
-
922
- expect(user).toBeTruthy();
923
- expect(user.email).toBe("test@example.com");
924
- expect(user.name).toBe("Test User");
925
- });
926
-
927
- it("should find user by id", async () => {
928
- const user = await userService.findOne("1");
929
- expect(user).toBeTruthy();
930
- expect(user.id).toBe(1);
931
318
  });
932
-
933
- it("should throw error for non-existent user", async () => {
934
- expect(() => userService.findOne("99999")).toThrow();
935
- });
936
-
937
- it("should update user", async () => {
938
- const updated = await userService.update("1", { name: "Updated Name" });
939
- expect(updated.name).toBe("Updated Name");
319
+
320
+ describe('findAll', () => {
321
+ it('should return all users', async () => {
322
+ const mockUsers = [{ id: '1', name: 'John' }, { id: '2', name: 'Jane' }];
323
+ mockUserRepository.findAll.mockResolvedValue(mockUsers);
324
+
325
+ const result = await userService.findAll();
326
+
327
+ expect(result).toEqual(mockUsers);
328
+ expect(mockUserRepository.findAll).toHaveBeenCalled();
329
+ });
940
330
  });
941
331
  });
942
332
  ```
943
333
 
944
- ```bash
945
- # Run tests
946
- fragment test
334
+ ## Environment Configuration
947
335
 
948
- # Run with coverage
949
- fragment test --coverage
336
+ ### fragment.json Example
950
337
 
951
- # Run specific test file
952
- fragment test test/users.spec.ts
338
+ ```json
339
+ {
340
+ "type": "mysql",
341
+ "host": "localhost",
342
+ "port": 3306,
343
+ "username": "root",
344
+ "password": "password",
345
+ "database": "my_app",
346
+ "entities": ["dist/entities/**/*.js"],
347
+ "migrations": ["dist/migrations/**/*.js"],
348
+ "subscribers": ["dist/subscribers/**/*.js"],
349
+ "logging": true,
350
+ "synchronize": false,
351
+ "migrationsRun": true
352
+ }
953
353
  ```
954
354
 
955
- ---
355
+ ### Environment Variables
956
356
 
957
- ## Production Deployment
357
+ ```
358
+ # .env file
359
+ PORT=3000
360
+ DB_HOST=production-db.example.com
361
+ DB_USER=app_user
362
+ DB_PASSWORD=secure_password
363
+ FEATURE_FLAG_NEW_UI=true
364
+ ```
958
365
 
959
- ### Use Case 9: Deploy to Production
366
+ ## Production Deployment
960
367
 
961
- **Build and Deploy**
368
+ ### Building for Production
962
369
 
963
370
  ```bash
964
- # Build for production
965
- fragment build
966
-
967
- # Verify build
968
- ls dist/
371
+ # Install dependencies
372
+ npm install
969
373
 
970
- # Set production environment variables
971
- export NODE_ENV=production
972
- export PORT=8080
973
- export DATABASE_URL=postgresql://user:pass@host:5432/db
974
- export JWT_SECRET=super-secret-production-key
374
+ # Build TypeScript to JavaScript
375
+ frg build
975
376
 
976
- # Run migrations
977
- fragment migrate:run
978
-
979
- # Start production server
377
+ # Start the application
980
378
  npm start
981
379
  ```
982
380
 
983
- **Docker Deployment**
984
-
985
- **Dockerfile:**
381
+ ### Docker Configuration
986
382
 
987
383
  ```dockerfile
988
- FROM node:18-alpine
384
+ FROM node:16-alpine
989
385
 
990
386
  WORKDIR /app
991
387
 
992
- # Copy package files
388
+ # Copy package.json and install dependencies
993
389
  COPY package*.json ./
994
-
995
- # Install production dependencies
996
390
  RUN npm ci --only=production
997
391
 
998
- # Copy source code
999
- COPY . .
392
+ # Copy built application
393
+ COPY dist ./dist
394
+ COPY fragment.json ./
1000
395
 
1001
- # Build TypeScript
1002
- RUN npm run build
1003
-
1004
- # Expose port
1005
396
  EXPOSE 3000
1006
397
 
1007
- # Health check
1008
- HEALTHCHECK --interval=30s --timeout=3s \
1009
- CMD node -e "require('http').get('http://localhost:3000/health', (r) => process.exit(r.statusCode === 200 ? 0 : 1))"
1010
-
1011
- # Start application
1012
- CMD ["npm", "start"]
1013
- ```
1014
-
1015
- **docker-compose.yml:**
1016
-
1017
- ```yaml
1018
- version: "3.8"
1019
-
1020
- services:
1021
- app:
1022
- build: .
1023
- ports:
1024
- - "3000:3000"
1025
- environment:
1026
- - NODE_ENV=production
1027
- - DATABASE_URL=postgresql://postgres:password@db:5432/myapp
1028
- - JWT_SECRET=your-secret-key
1029
- depends_on:
1030
- - db
1031
- restart: unless-stopped
1032
-
1033
- db:
1034
- image: postgres:15-alpine
1035
- environment:
1036
- - POSTGRES_DB=myapp
1037
- - POSTGRES_USER=postgres
1038
- - POSTGRES_PASSWORD=password
1039
- volumes:
1040
- - db-data:/var/lib/postgresql/data
1041
- restart: unless-stopped
1042
-
1043
- volumes:
1044
- db-data:
1045
- ```
1046
-
1047
- ```bash
1048
- # Build and start services
1049
- docker-compose up -d
1050
-
1051
- # View logs
1052
- docker-compose logs -f app
1053
-
1054
- # Run migrations in container
1055
- docker-compose exec app fragment migrate:run
1056
-
1057
- # Stop services
1058
- docker-compose down
1059
-
1060
- # Stop and remove volumes
1061
- docker-compose down -v
398
+ CMD ["node", "dist/app.js"]
1062
399
  ```
1063
400
 
1064
- ---
1065
-
1066
- ## CLI Reference
1067
-
1068
- ### Complete CLI Command Reference
1069
-
1070
- **Project Management**
1071
-
1072
- ```bash
1073
- fragment init [dir] # Initialize new project
1074
- fragment init . --features=auth # Initialize with features in current dir
1075
- fragment serve # Start development server
1076
- fragment serve --port=4000 # Start on custom port
1077
- fragment serve --no-watch # Disable hot reload
1078
- fragment build # Build for production
1079
- ```
1080
-
1081
- **Code Generation**
1082
-
1083
- ```bash
1084
- fragment generate <type> <name> # Generate component
1085
- fragment g <type> <name> # Shorthand
1086
-
1087
- # Types:
1088
- fragment generate controller users
1089
- fragment generate service auth
1090
- fragment generate entity product
1091
- fragment generate repository users
1092
- fragment generate dto create-user
1093
- fragment generate resource product # Generates all of the above
1094
-
1095
- # With options:
1096
- fragment generate service users --with-repository
1097
- ```
1098
-
1099
- **Database Operations**
1100
-
1101
- ```bash
1102
- fragment migrate # Run pending migrations
1103
- fragment migrate:create <name> # Create new migration
1104
- fragment migrate:run # Run migrations
1105
- fragment migrate:revert # Revert last migration
1106
- fragment migrate:refresh # Drop and re-run all migrations
1107
- fragment migrate:status # Show migration status
1108
- fragment schema:sync # Sync schema (dev only)
1109
- fragment schema:drop # Drop all tables
1110
- fragment seed # Run seeds
1111
- fragment seed:create <name> # Create seed file
1112
- ```
1113
-
1114
- **Diagnostics**
1115
-
1116
- ```bash
1117
- fragment routes # List all routes (auto-detect mode)
1118
- fragment routes --env=dev # List routes from TypeScript src/
1119
- fragment routes --env=prod # List routes from compiled dist/
1120
- fragment beans # List all beans
1121
- fragment beans --tree # Show beans as tree
1122
- fragment info # Show app information
1123
- fragment config # Show configuration
1124
- fragment version # Show Fragment version
1125
- ```
1126
-
1127
- ### Diagnostics & Debugging
1128
-
1129
- **Environment-Specific Diagnostics**
1130
-
1131
- ```bash
1132
- # Auto-detect (prefers src/ if available, falls back to dist/)
1133
- fragment routes
1134
- fragment beans
1135
-
1136
- # Force development mode (use TypeScript source)
1137
- fragment routes --env=dev
1138
- fragment beans --env=dev --tree
1139
-
1140
- # Force production mode (use compiled JavaScript)
1141
- fragment routes --env=prod
1142
- fragment beans --env=prod
1143
-
1144
- # Show application info
1145
- fragment info
1146
- # Output:
1147
- # 📊 Application Information:
1148
- # Name: my-app
1149
- # Version: 1.0.0
1150
- # Node: v18.17.0
1151
- # Platform: darwin
1152
- # Architecture: arm64
1153
- # Environment: development
1154
- # Source: ✓ src/
1155
- # Built: ✓ dist/
1156
-
1157
- # View configuration
1158
- fragment config
1159
- # Shows contents of fragment.json
1160
-
1161
- # Check version
1162
- fragment version
1163
- # ✨ Fragment Framework v1.0.0
1164
- ```
1165
-
1166
- ---
1167
-
1168
- ## Best Practices
1169
-
1170
- ### Development
1171
-
1172
- 1. **Always use decorators** - Leverage `@Controller`, `@Service`, `@Injectable` for clean architecture
1173
- 2. **Keep services focused** - One responsibility per service (Single Responsibility Principle)
1174
- 3. **Use DTOs** - Define Data Transfer Objects for request/response validation
1175
- 4. **Type everything** - Take advantage of TypeScript's type system
1176
- 5. **Use environment variables** - Never hardcode secrets or configuration
1177
-
1178
- ### Database
1179
-
1180
- 1. **Use migrations in production** - Never use `synchronize: true` in production
1181
- 2. **Version your migrations** - Keep migration files in version control
1182
- 3. **Test migrations** - Always test both `up` and `down` migrations
1183
- 4. **Use transactions** - Wrap related operations in database transactions
1184
- 5. **Index strategically** - Add indexes for frequently queried columns
1185
-
1186
- ### Security
1187
-
1188
- 1. **Hash passwords** - Always use `AuthModule.hashPassword()`
1189
- 2. **Validate input** - Use DTOs with class-validator
1190
- 3. **Rate limiting** - Implement rate limiting on public endpoints
1191
- 4. **CORS configuration** - Configure CORS appropriately for your frontend
1192
- 5. **Environment-specific secrets** - Use different secrets for dev/prod
1193
-
1194
- ### Testing
1195
-
1196
- 1. **Test business logic** - Focus on service and repository tests
1197
- 2. **Mock external dependencies** - Don't hit real databases or APIs in tests
1198
- 3. **Test edge cases** - Test error conditions and boundary cases
1199
- 4. **Use descriptive test names** - Make test failures easy to understand
1200
-
1201
- ### Deployment
1202
-
1203
- 1. **Use Docker** - Containerize your application for consistent deployments
1204
- 2. **Health checks** - Implement health check endpoints
1205
- 3. **Logging** - Use structured logging for production
1206
- 4. **Monitoring** - Set up monitoring and alerting
1207
- 5. **Backup strategy** - Regular database backups
1208
-
1209
- ---
1210
-
1211
401
  ## Troubleshooting
1212
402
 
1213
- ### Common Issues and Solutions
1214
-
1215
- #### Issue: "Cannot find module"
1216
-
1217
- ```bash
1218
- # Solution: Rebuild the project
1219
- npm run build
1220
- fragment serve
1221
- ```
1222
-
1223
- #### Issue: "Port already in use"
1224
-
1225
- ```bash
1226
- # Solution 1: Use different port
1227
- fragment serve --port=4000
1228
-
1229
- # Solution 2: Kill process using port (macOS/Linux)
1230
- lsof -ti:3000 | xargs kill -9
1231
-
1232
- # Solution 3: Kill process using port (Windows)
1233
- netstat -ano | findstr :3000
1234
- taskkill /PID <PID> /F
1235
- ```
1236
-
1237
- #### Issue: "Database connection failed"
1238
-
1239
- ```bash
1240
- # Check configuration
1241
- fragment config
1242
-
1243
- # Test connection
1244
- fragment migrate:status
1245
-
1246
- # Verify DATABASE_URL
1247
- echo $DATABASE_URL
1248
-
1249
- # Common fixes:
1250
- # - Ensure database server is running
1251
- # - Check username/password
1252
- # - Verify database name exists
1253
- # - Check network connectivity
1254
- ```
1255
-
1256
- #### Issue: "Decorators not working"
1257
-
1258
- ```bash
1259
- # Ensure tsconfig.json has these settings:
1260
- {
1261
- "compilerOptions": {
1262
- "experimentalDecorators": true,
1263
- "emitDecoratorMetadata": true
1264
- }
1265
- }
1266
-
1267
- # Rebuild after changes
1268
- npm run build
1269
- ```
1270
-
1271
- #### Issue: "Routes not registering"
1272
-
1273
- ```bash
1274
- # Check if components are being scanned
1275
- fragment routes
1276
-
1277
- # If no routes shown:
1278
- # 1. Ensure files follow naming convention (*.controller.ts)
1279
- # 2. Verify decorators are applied correctly
1280
- # 3. Check if autoScan is enabled in @FragmentApplication
1281
- # 4. Try manual import in main.ts
1282
-
1283
- # Force specific environment
1284
- fragment routes --env=dev
1285
- fragment routes --env=prod
1286
- ```
1287
-
1288
- #### Issue: "Circular dependency detected"
1289
-
1290
- ```bash
1291
- # Error: Circular dependency detected: UserService
1292
-
1293
- # Solution: Use property injection instead of constructor injection
1294
- # Before (circular):
1295
- @Service()
1296
- class UserService {
1297
- constructor(private authService: AuthService) {}
1298
- }
1299
- @Service()
1300
- class AuthService {
1301
- constructor(private userService: UserService) {} // Circular!
1302
- }
1303
-
1304
- # After (fixed):
1305
- @Service()
1306
- class UserService {
1307
- @Autowired()
1308
- private authService: AuthService; // Property injection breaks cycle
1309
- }
1310
- ```
1311
-
1312
- #### Issue: "Hot reload not working"
1313
-
1314
- ```bash
1315
- # Check if chokidar is installed
1316
- npm install --save-dev chokidar
1317
-
1318
- # Restart with watch mode explicitly enabled
1319
- fragment serve --watch
1320
-
1321
- # Check file watch patterns in serve command
1322
- ```
1323
-
1324
- #### Issue: "Migration fails"
1325
-
1326
- ```bash
1327
- # Revert last migration
1328
- fragment migrate:revert
1329
-
1330
- # Check migration status
1331
- fragment migrate:status
1332
-
1333
- # Fix migration file and re-run
1334
- fragment migrate:run
1335
-
1336
- # If all else fails, drop and recreate (DEV ONLY!)
1337
- fragment schema:drop
1338
- fragment migrate:run
1339
- ```
1340
-
1341
- #### Issue: "TypeScript compilation errors"
1342
-
1343
- ```bash
1344
- # Check TypeScript version compatibility
1345
- npm list typescript
1346
-
1347
- # Clear build artifacts
1348
- rm -rf dist/
1349
- npm run build
1350
-
1351
- # Check for conflicting type definitions
1352
- npm list @types
1353
- ```
1354
-
1355
- ---
1356
-
1357
- ## Performance Tips
1358
-
1359
- ### Optimize Database Queries
1360
-
1361
- ```typescript
1362
- // Use eager loading to avoid N+1 queries
1363
- const users = await userRepository.find({
1364
- relations: ["posts", "comments"],
1365
- });
1366
-
1367
- // Use indexes for frequently queried columns
1368
- @Entity()
1369
- export class User {
1370
- @Index()
1371
- @Column({ unique: true })
1372
- email: string;
1373
- }
1374
-
1375
- // Use pagination for large datasets
1376
- const [users, total] = await userRepository.findAndCount({
1377
- skip: (page - 1) * limit,
1378
- take: limit,
1379
- });
1380
- ```
1381
-
1382
- ### Caching Strategies
1383
-
1384
- ```typescript
1385
- // In-memory cache for frequently accessed data
1386
- @Service()
1387
- export class CacheService {
1388
- private cache = new Map<string, any>();
1389
-
1390
- get(key: string) {
1391
- return this.cache.get(key);
1392
- }
1393
-
1394
- set(key: string, value: any, ttl: number = 3600000) {
1395
- this.cache.set(key, value);
1396
- setTimeout(() => this.cache.delete(key), ttl);
1397
- }
1398
- }
1399
- ```
1400
-
1401
- ### Connection Pooling
1402
-
1403
- ```json
1404
- // fragment.json
1405
- {
1406
- "database": {
1407
- "poolSize": ${DB_POOL_SIZE},
1408
- "maxQueryExecutionTime": ${DB_MAX_QUERY_TIME}
1409
- }
1410
- }
1411
- ```
1412
-
1413
- ---
1414
-
1415
- ## Summary
1416
-
1417
- Fragment Framework provides:
1418
-
1419
- - ✅ **Quick project initialization** - `fragment init .` or `fragment init my-app`
1420
- - ✅ **Hot reload development** - Automatic server restart on file changes
1421
- - ✅ **Comprehensive CLI** - Code generation, migrations, diagnostics
1422
- - ✅ **Built-in authentication** - JWT, password hashing, role guards
1423
- - ✅ **Database migrations** - TypeORM integration with full migration support
1424
- - ✅ **AI integrations** - OpenAI, Ollama, and custom providers
1425
- - ✅ **Production-ready** - Docker, health checks, logging
1426
- - ✅ **Type-safe DI** - Automatic dependency injection
1427
- - ✅ **Convention over configuration** - Sensible defaults, minimal setup
1428
- - ✅ **Environment flexibility** - Dev/prod modes with `--env` flag
1429
-
1430
- **Start building your next TypeScript application with Fragment today!**
1431
-
1432
- ```bash
1433
- npm install -g fragment
1434
- fragment init my-app
1435
- cd my-app
1436
- fragment serve
1437
- ```
1438
-
1439
- Visit [https://fragment.digitwhale.com](https://fragment.digitwhale.com) for more documentation and examples.
403
+ ### Common Issues
404
+
405
+ 1. **"Cannot read properties of undefined" errors**: This typically means dependency injection failed. Check:
406
+ - All services/controllers have appropriate decorators (`@Service()`, `@Controller()`)
407
+ - TypeScript compiler option `emitDecoratorMetadata` is enabled in `tsconfig.json`
408
+ - All dependencies are properly registered before use
409
+
410
+ 2. **TypeORM connection issues**:
411
+ - Verify `fragment.json` configuration is correct
412
+ - Ensure database is accessible from your application
413
+ - Check entity paths match your compiled output directory
414
+
415
+ 3. **Route registration problems**:
416
+ - Verify controller classes have `@Controller()` decorator
417
+ - Check route methods have appropriate HTTP decorators (`@Get()`, `@Post()`, etc.)
418
+ - Ensure controller classes are exported and discoverable
419
+
420
+ ### Debugging Tips
421
+
422
+ 1. Enable detailed logging by setting environment variable:
423
+ ```bash
424
+ DEBUG=fragment:* npm run dev
425
+ ```
426
+
427
+ 2. Use the container API to inspect registered components:
428
+ ```typescript
429
+ import { DIContainer } from 'fragment-ts';
430
+
431
+ const container = DIContainer.getInstance();
432
+ console.log('Registered components:', container.getAllInstances());
433
+ ```
434
+
435
+ 3. Verify component scanning is working:
436
+ ```typescript
437
+ import { MetadataStorage } from 'fragment-ts';
438
+
439
+ const storage = MetadataStorage.getInstance();
440
+ console.log('Discovered classes:', storage.getAllClasses());
441
+ console.log('Discovered methods:', storage.getAllMethods());
442
+ ```