fragment-ts 1.0.2 → 1.0.3
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 +401 -0
- package/USAGE.md +1439 -0
- package/bin/fragment.js +2 -0
- package/dist/ai/ai.module.d.ts +27 -0
- package/dist/ai/ai.module.d.ts.map +1 -0
- package/dist/ai/ai.module.js +77 -0
- package/dist/ai/ai.module.js.map +1 -0
- package/dist/auth/auth.module.d.ts +18 -0
- package/dist/auth/auth.module.d.ts.map +1 -0
- package/dist/auth/auth.module.js +89 -0
- package/dist/auth/auth.module.js.map +1 -0
- package/dist/cli/commands/build.command.d.ts +6 -1
- package/dist/cli/commands/build.command.d.ts.map +1 -0
- package/dist/cli/commands/build.command.js +130 -10
- package/dist/cli/commands/build.command.js.map +1 -1
- package/dist/cli/commands/diagnostics.command.d.ts +16 -0
- package/dist/cli/commands/diagnostics.command.d.ts.map +1 -0
- package/dist/cli/commands/diagnostics.command.js +419 -0
- package/dist/cli/commands/diagnostics.command.js.map +1 -0
- package/dist/cli/commands/generate.command.d.ts +13 -6
- package/dist/cli/commands/generate.command.d.ts.map +1 -0
- package/dist/cli/commands/generate.command.js +200 -128
- package/dist/cli/commands/generate.command.js.map +1 -1
- package/dist/cli/commands/init.command.d.ts +11 -5
- package/dist/cli/commands/init.command.d.ts.map +1 -0
- package/dist/cli/commands/init.command.js +267 -305
- package/dist/cli/commands/init.command.js.map +1 -1
- package/dist/cli/commands/migrate.command.d.ts +12 -5
- package/dist/cli/commands/migrate.command.d.ts.map +1 -0
- package/dist/cli/commands/migrate.command.js +211 -83
- package/dist/cli/commands/migrate.command.js.map +1 -1
- package/dist/cli/commands/serve.command.d.ts +5 -4
- package/dist/cli/commands/serve.command.d.ts.map +1 -0
- package/dist/cli/commands/serve.command.js +178 -21
- package/dist/cli/commands/serve.command.js.map +1 -1
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +22 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/core/container/di-container.d.ts +13 -7
- package/dist/core/container/di-container.d.ts.map +1 -0
- package/dist/core/container/di-container.js +75 -25
- package/dist/core/container/di-container.js.map +1 -1
- package/dist/core/decorators/application.decorator.d.ts +8 -0
- package/dist/core/decorators/application.decorator.d.ts.map +1 -0
- package/dist/core/decorators/application.decorator.js +16 -0
- package/dist/core/decorators/application.decorator.js.map +1 -0
- package/dist/core/decorators/auto-configuration.decorator.d.ts +2 -0
- package/dist/core/decorators/auto-configuration.decorator.d.ts.map +1 -0
- package/dist/core/decorators/auto-configuration.decorator.js +19 -0
- package/dist/core/decorators/auto-configuration.decorator.js.map +1 -0
- package/dist/core/decorators/conditional.decorators.d.ts +4 -0
- package/dist/core/decorators/conditional.decorators.d.ts.map +1 -0
- package/dist/core/decorators/conditional.decorators.js +22 -0
- package/dist/core/decorators/conditional.decorators.js.map +1 -0
- package/dist/core/decorators/controller.decorator.d.ts +1 -3
- package/dist/core/decorators/controller.decorator.d.ts.map +1 -0
- package/dist/core/decorators/controller.decorator.js +12 -9
- package/dist/core/decorators/controller.decorator.js.map +1 -1
- package/dist/core/decorators/http.decorators.d.ts +12 -0
- package/dist/core/decorators/http.decorators.d.ts.map +1 -0
- package/dist/core/decorators/http.decorators.js +52 -0
- package/dist/core/decorators/http.decorators.js.map +1 -0
- package/dist/core/decorators/injectable.decorator.d.ts +3 -3
- package/dist/core/decorators/injectable.decorator.d.ts.map +1 -0
- package/dist/core/decorators/injectable.decorator.js +11 -8
- package/dist/core/decorators/injectable.decorator.js.map +1 -1
- package/dist/core/decorators/injection.decorators.d.ts +5 -0
- package/dist/core/decorators/injection.decorators.d.ts.map +1 -0
- package/dist/core/decorators/injection.decorators.js +29 -0
- package/dist/core/decorators/injection.decorators.js.map +1 -0
- package/dist/core/decorators/repository.decorator.d.ts +1 -0
- package/dist/core/decorators/repository.decorator.d.ts.map +1 -0
- package/dist/core/decorators/repository.decorator.js +12 -1
- package/dist/core/decorators/repository.decorator.js.map +1 -1
- package/dist/core/decorators/service.decorator.d.ts +1 -0
- package/dist/core/decorators/service.decorator.d.ts.map +1 -0
- package/dist/core/decorators/service.decorator.js +12 -1
- package/dist/core/decorators/service.decorator.js.map +1 -1
- package/dist/core/index.d.ts +13 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +29 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/loader/file-loader.d.ts +5 -0
- package/dist/core/loader/file-loader.d.ts.map +1 -0
- package/dist/core/loader/file-loader.js +85 -0
- package/dist/core/loader/file-loader.js.map +1 -0
- package/dist/core/metadata/metadata-keys.d.ts +20 -0
- package/dist/core/metadata/metadata-keys.d.ts.map +1 -0
- package/dist/core/metadata/metadata-keys.js +23 -0
- package/dist/core/metadata/metadata-keys.js.map +1 -0
- package/dist/core/metadata/metadata-storage.d.ts +36 -0
- package/dist/core/metadata/metadata-storage.d.ts.map +1 -0
- package/dist/core/metadata/metadata-storage.js +53 -0
- package/dist/core/metadata/metadata-storage.js.map +1 -0
- package/dist/core/scanner/component-scanner.d.ts +16 -0
- package/dist/core/scanner/component-scanner.d.ts.map +1 -0
- package/dist/core/scanner/component-scanner.js +147 -0
- package/dist/core/scanner/component-scanner.js.map +1 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +60 -0
- package/dist/index.js.map +1 -0
- package/dist/plugins/plugin-manager.d.ts +14 -0
- package/dist/plugins/plugin-manager.d.ts.map +1 -0
- package/dist/{cli/utils/file-generator.js → plugins/plugin-manager.js} +32 -24
- package/dist/plugins/plugin-manager.js.map +1 -0
- package/dist/shared/errors.d.ts +18 -0
- package/dist/shared/errors.d.ts.map +1 -0
- package/dist/shared/errors.js +41 -0
- package/dist/shared/errors.js.map +1 -0
- package/dist/testing/runner.d.ts +26 -0
- package/dist/testing/runner.d.ts.map +1 -0
- package/dist/testing/runner.js +143 -0
- package/dist/testing/runner.js.map +1 -0
- package/dist/typeorm/typeorm-module.d.ts +36 -0
- package/dist/typeorm/typeorm-module.d.ts.map +1 -0
- package/dist/typeorm/typeorm-module.js +150 -0
- package/dist/typeorm/typeorm-module.js.map +1 -0
- package/dist/web/application.d.ts +29 -0
- package/dist/web/application.d.ts.map +1 -0
- package/dist/web/application.js +301 -0
- package/dist/web/application.js.map +1 -0
- package/dist/web/interfaces.d.ts +14 -0
- package/dist/web/interfaces.d.ts.map +1 -0
- package/dist/{auth/dto/login.dto.js → web/interfaces.js} +1 -1
- package/dist/web/interfaces.js.map +1 -0
- package/examples/blog-api/fragment.json +14 -0
- package/examples/blog-api/package-lock.json +3405 -0
- package/examples/blog-api/package.json +19 -0
- package/examples/blog-api/src/controllers/app.controller.ts +9 -0
- package/examples/blog-api/src/controllers/auth.controller.ts +17 -0
- package/examples/blog-api/src/controllers/category.controller.ts +29 -0
- package/examples/blog-api/src/controllers/comment.controller.ts +31 -0
- package/examples/blog-api/src/controllers/post.controller.ts +46 -0
- package/examples/blog-api/src/dto/create-category.dto.ts +6 -0
- package/examples/blog-api/src/dto/create-comment.dto.ts +6 -0
- package/examples/blog-api/src/dto/create-post.dto.ts +6 -0
- package/examples/blog-api/src/entities/category.entity.ts +16 -0
- package/examples/blog-api/src/entities/comment.entity.ts +29 -0
- package/examples/blog-api/src/entities/post.entity.ts +42 -0
- package/examples/blog-api/src/entities/user.entity.ts +25 -0
- package/examples/blog-api/src/main.ts +16 -0
- package/examples/blog-api/src/migrations/1767737463842-InitialSchema.ts +60 -0
- package/examples/blog-api/src/repositories/category.repository.ts +25 -0
- package/examples/blog-api/src/repositories/comment.repository.ts +25 -0
- package/examples/blog-api/src/repositories/post.repository.ts +29 -0
- package/examples/blog-api/src/seeds/SampleData.seed.ts +41 -0
- package/examples/blog-api/src/services/app.service.ts +8 -0
- package/examples/blog-api/src/services/auth.service.ts +15 -0
- package/examples/blog-api/src/services/category.service.ts +27 -0
- package/examples/blog-api/src/services/comment.service.ts +31 -0
- package/examples/blog-api/src/services/post.service.ts +35 -0
- package/examples/blog-api/tsconfig.json +23 -0
- package/package.json +55 -33
- package/src/ai/ai.module.ts +110 -0
- package/src/auth/auth.module.ts +77 -0
- package/src/cli/commands/build.command.ts +123 -13
- package/src/cli/commands/diagnostics.command.ts +438 -0
- package/src/cli/commands/generate.command.ts +206 -137
- package/src/cli/commands/init.command.ts +337 -349
- package/src/cli/commands/migrate.command.ts +203 -88
- package/src/cli/commands/serve.command.ts +176 -24
- package/src/cli/index.ts +23 -0
- package/src/core/container/di-container.ts +83 -26
- package/src/core/decorators/application.decorator.ts +26 -0
- package/src/core/decorators/auto-configuration.decorator.ts +17 -0
- package/src/core/decorators/conditional.decorators.ts +19 -0
- package/src/core/decorators/controller.decorator.ts +14 -11
- package/src/core/decorators/http.decorators.ts +71 -0
- package/src/core/decorators/injectable.decorator.ts +14 -9
- package/src/core/decorators/injection.decorators.ts +26 -0
- package/src/core/decorators/repository.decorator.ts +13 -2
- package/src/core/decorators/service.decorator.ts +13 -2
- package/src/core/index.ts +13 -0
- package/src/core/loader/file-loader.ts +55 -0
- package/src/core/metadata/metadata-keys.ts +19 -0
- package/src/core/metadata/metadata-storage.ts +91 -0
- package/src/core/scanner/component-scanner.ts +129 -0
- package/src/index.ts +45 -0
- package/src/plugins/plugin-manager.ts +52 -0
- package/src/shared/errors.ts +34 -0
- package/src/testing/runner.ts +143 -0
- package/src/typeorm/typeorm-module.ts +216 -0
- package/src/web/application.ts +348 -0
- package/src/web/interfaces.ts +17 -0
- package/tsconfig.json +8 -6
- package/.env.example +0 -0
- package/base.ts +0 -1810
- package/base2.ts +0 -968
- package/bin/frg.ts +0 -5
- package/config/fragment.lock.yaml +0 -0
- package/config/fragment.yaml +0 -0
- package/dist/app.d.ts +0 -15
- package/dist/app.js +0 -91
- package/dist/app.js.map +0 -1
- package/dist/auth/auth.controller.d.ts +0 -10
- package/dist/auth/auth.controller.js +0 -88
- package/dist/auth/auth.controller.js.map +0 -1
- package/dist/auth/auth.middleware.d.ts +0 -2
- package/dist/auth/auth.middleware.js +0 -25
- package/dist/auth/auth.middleware.js.map +0 -1
- package/dist/auth/auth.service.d.ts +0 -20
- package/dist/auth/auth.service.js +0 -144
- package/dist/auth/auth.service.js.map +0 -1
- package/dist/auth/dto/login.dto.d.ts +0 -9
- package/dist/auth/dto/login.dto.js.map +0 -1
- package/dist/cli/cli.d.ts +0 -12
- package/dist/cli/cli.js +0 -187
- package/dist/cli/cli.js.map +0 -1
- package/dist/cli/commands/config.command.d.ts +0 -6
- package/dist/cli/commands/config.command.js +0 -285
- package/dist/cli/commands/config.command.js.map +0 -1
- package/dist/cli/templates/controller.template.d.ts +0 -1
- package/dist/cli/templates/controller.template.js +0 -53
- package/dist/cli/templates/controller.template.js.map +0 -1
- package/dist/cli/templates/entity.template.d.ts +0 -1
- package/dist/cli/templates/entity.template.js +0 -24
- package/dist/cli/templates/entity.template.js.map +0 -1
- package/dist/cli/templates/repository.template.d.ts +0 -1
- package/dist/cli/templates/repository.template.js +0 -44
- package/dist/cli/templates/repository.template.js.map +0 -1
- package/dist/cli/templates/service.template.d.ts +0 -1
- package/dist/cli/templates/service.template.js +0 -44
- package/dist/cli/templates/service.template.js.map +0 -1
- package/dist/cli/utils/file-generator.d.ts +0 -9
- package/dist/cli/utils/file-generator.js.map +0 -1
- package/dist/cli/utils/logger.d.ts +0 -14
- package/dist/cli/utils/logger.js +0 -50
- package/dist/cli/utils/logger.js.map +0 -1
- package/dist/controllers/health.controller.d.ts +0 -13
- package/dist/controllers/health.controller.js +0 -51
- package/dist/controllers/health.controller.js.map +0 -1
- package/dist/core/config/config-loader.d.ts +0 -31
- package/dist/core/config/config-loader.js +0 -99
- package/dist/core/config/config-loader.js.map +0 -1
- package/dist/core/decorators/auth-guard.decorator.d.ts +0 -3
- package/dist/core/decorators/auth-guard.decorator.js +0 -19
- package/dist/core/decorators/auth-guard.decorator.js.map +0 -1
- package/dist/core/decorators/autowire.decorator.d.ts +0 -3
- package/dist/core/decorators/autowire.decorator.js +0 -18
- package/dist/core/decorators/autowire.decorator.js.map +0 -1
- package/dist/core/decorators/middleware.decorator.d.ts +0 -3
- package/dist/core/decorators/middleware.decorator.js +0 -21
- package/dist/core/decorators/middleware.decorator.js.map +0 -1
- package/dist/core/decorators/route.decorator.d.ts +0 -14
- package/dist/core/decorators/route.decorator.js +0 -33
- package/dist/core/decorators/route.decorator.js.map +0 -1
- package/dist/core/openai/openai-client.d.ts +0 -12
- package/dist/core/openai/openai-client.js +0 -94
- package/dist/core/openai/openai-client.js.map +0 -1
- package/dist/database/data-source.d.ts +0 -4
- package/dist/database/data-source.js +0 -27
- package/dist/database/data-source.js.map +0 -1
- package/dist/entities/session.entity.d.ts +0 -9
- package/dist/entities/session.entity.js +0 -46
- package/dist/entities/session.entity.js.map +0 -1
- package/dist/entities/user.entity.d.ts +0 -10
- package/dist/entities/user.entity.js +0 -49
- package/dist/entities/user.entity.js.map +0 -1
- package/dist/middlewares/logging.middleware.d.ts +0 -2
- package/dist/middlewares/logging.middleware.js +0 -29
- package/dist/middlewares/logging.middleware.js.map +0 -1
- package/dist/repositories/session.repository.d.ts +0 -9
- package/dist/repositories/session.repository.js +0 -51
- package/dist/repositories/session.repository.js.map +0 -1
- package/dist/repositories/user.repository.d.ts +0 -10
- package/dist/repositories/user.repository.js +0 -44
- package/dist/repositories/user.repository.js.map +0 -1
- package/dist/server.d.ts +0 -1
- package/dist/server.js +0 -31
- package/dist/server.js.map +0 -1
- package/dist/services/health.service.d.ts +0 -13
- package/dist/services/health.service.js +0 -45
- package/dist/services/health.service.js.map +0 -1
- package/readme.md +0 -120
- package/src/app.ts +0 -121
- package/src/auth/auth.controller.ts +0 -52
- package/src/auth/auth.middleware.ts +0 -27
- package/src/auth/auth.service.ts +0 -110
- package/src/auth/dto/login.dto.ts +0 -11
- package/src/cli/cli.ts +0 -212
- package/src/cli/commands/config.command.ts +0 -280
- package/src/cli/templates/controller.template.ts +0 -51
- package/src/cli/templates/entity.template.ts +0 -22
- package/src/cli/templates/repository.template.ts +0 -42
- package/src/cli/templates/service.template.ts +0 -42
- package/src/cli/utils/file-generator.ts +0 -37
- package/src/cli/utils/logger.ts +0 -52
- package/src/controllers/health.controller.ts +0 -24
- package/src/core/config/config-loader.ts +0 -98
- package/src/core/decorators/auth-guard.decorator.ts +0 -15
- package/src/core/decorators/autowire.decorator.ts +0 -18
- package/src/core/decorators/middleware.decorator.ts +0 -18
- package/src/core/decorators/route.decorator.ts +0 -33
- package/src/core/openai/openai-client.ts +0 -99
- package/src/database/data-source.ts +0 -29
- package/src/entities/session.entity.ts +0 -25
- package/src/entities/user.entity.ts +0 -27
- package/src/middlewares/logging.middleware.ts +0 -28
- package/src/repositories/session.repository.ts +0 -42
- package/src/repositories/user.repository.ts +0 -37
- package/src/server.ts +0 -32
- package/src/services/health.service.ts +0 -29
package/USAGE.md
ADDED
|
@@ -0,0 +1,1439 @@
|
|
|
1
|
+
# Fragment Framework - Comprehensive Use Cases & Examples
|
|
2
|
+
|
|
3
|
+
## Table of Contents
|
|
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)
|
|
18
|
+
|
|
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
|
+
```
|
|
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
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
**Generated Files:**
|
|
85
|
+
|
|
86
|
+
**src/controllers/product.controller.ts**
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
import { Controller, Get, Post, Put, Delete, Body, Param } from "fragment";
|
|
90
|
+
import { ProductService } from "../services/product.service";
|
|
91
|
+
|
|
92
|
+
@Controller("/products")
|
|
93
|
+
export class ProductController {
|
|
94
|
+
constructor(private productService: ProductService) {}
|
|
95
|
+
|
|
96
|
+
@Get()
|
|
97
|
+
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);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
**src/services/product.service.ts**
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
import { Service } from "fragment";
|
|
127
|
+
import { ProductRepository } from "../repositories/product.repository";
|
|
128
|
+
|
|
129
|
+
@Service()
|
|
130
|
+
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");
|
|
141
|
+
}
|
|
142
|
+
return product;
|
|
143
|
+
}
|
|
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
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
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**
|
|
228
|
+
|
|
229
|
+
```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";
|
|
259
|
+
import { UserRepository } from "../repositories/user.repository";
|
|
260
|
+
|
|
261
|
+
@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 };
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
**src/controllers/auth.controller.ts**
|
|
321
|
+
|
|
322
|
+
```typescript
|
|
323
|
+
import { Controller, Post, Get, Body, Req } from "fragment";
|
|
324
|
+
import { AuthModule } from "fragment";
|
|
325
|
+
import { AuthService } from "../services/auth.service";
|
|
326
|
+
|
|
327
|
+
@Controller("/auth")
|
|
328
|
+
export class AuthController {
|
|
329
|
+
constructor(private authService: AuthService) {}
|
|
330
|
+
|
|
331
|
+
@Post("/register")
|
|
332
|
+
async register(@Body() body: any) {
|
|
333
|
+
return this.authService.register(body.email, body.password, body.name);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
@Post("/login")
|
|
337
|
+
async login(@Body() body: any) {
|
|
338
|
+
return this.authService.login(body.email, body.password);
|
|
339
|
+
}
|
|
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);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
**src/main.ts - Add Auth Middleware**
|
|
364
|
+
|
|
365
|
+
```typescript
|
|
366
|
+
import "reflect-metadata";
|
|
367
|
+
import { FragmentApplication } from "fragment";
|
|
368
|
+
import { FragmentWebApplication } from "fragment";
|
|
369
|
+
import { AuthModule } from "fragment";
|
|
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
|
+
```
|
|
412
|
+
|
|
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
|
+
);
|
|
490
|
+
}
|
|
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");
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
### Database Seeding
|
|
510
|
+
|
|
511
|
+
```bash
|
|
512
|
+
# Create seed
|
|
513
|
+
fragment seed:create InitialData
|
|
514
|
+
|
|
515
|
+
# Run all seeds
|
|
516
|
+
fragment seed
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
**src/seeds/InitialData.seed.ts**
|
|
520
|
+
|
|
521
|
+
```typescript
|
|
522
|
+
import { TypeORMModule } from "fragment";
|
|
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");
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
```
|
|
548
|
+
|
|
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**
|
|
566
|
+
|
|
567
|
+
```typescript
|
|
568
|
+
import { Service } from "fragment";
|
|
569
|
+
import { AIModule } from "fragment";
|
|
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,
|
|
593
|
+
});
|
|
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
|
+
}
|
|
621
|
+
}
|
|
622
|
+
```
|
|
623
|
+
|
|
624
|
+
**src/controllers/chat.controller.ts**
|
|
625
|
+
|
|
626
|
+
```typescript
|
|
627
|
+
import { Controller, Post, Body, Res } from "fragment";
|
|
628
|
+
import { ChatService } from "../services/chat.service";
|
|
629
|
+
import { Response } from "express";
|
|
630
|
+
|
|
631
|
+
@Controller("/chat")
|
|
632
|
+
export class ChatController {
|
|
633
|
+
constructor(private chatService: ChatService) {}
|
|
634
|
+
|
|
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
|
+
}
|
|
659
|
+
}
|
|
660
|
+
```
|
|
661
|
+
|
|
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
|
|
696
|
+
|
|
697
|
+
```typescript
|
|
698
|
+
// src/services/email.service.ts
|
|
699
|
+
import { Service } from "fragment";
|
|
700
|
+
|
|
701
|
+
@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";
|
|
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";
|
|
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
|
+
}
|
|
763
|
+
}
|
|
764
|
+
```
|
|
765
|
+
|
|
766
|
+
---
|
|
767
|
+
|
|
768
|
+
## Advanced Routing
|
|
769
|
+
|
|
770
|
+
### Use Case 6: Complex Route Parameters
|
|
771
|
+
|
|
772
|
+
```typescript
|
|
773
|
+
import {
|
|
774
|
+
Controller,
|
|
775
|
+
Get,
|
|
776
|
+
Post,
|
|
777
|
+
Param,
|
|
778
|
+
Query,
|
|
779
|
+
Header,
|
|
780
|
+
Body,
|
|
781
|
+
Req,
|
|
782
|
+
Res,
|
|
783
|
+
} from "fragment";
|
|
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
|
+
}
|
|
819
|
+
|
|
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
|
+
}
|
|
836
|
+
}
|
|
837
|
+
```
|
|
838
|
+
|
|
839
|
+
---
|
|
840
|
+
|
|
841
|
+
## Custom Middleware
|
|
842
|
+
|
|
843
|
+
### Use Case 7: Request Logging Middleware
|
|
844
|
+
|
|
845
|
+
```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
|
+
});
|
|
859
|
+
|
|
860
|
+
next();
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
// src/main.ts
|
|
865
|
+
import "reflect-metadata";
|
|
866
|
+
import { FragmentApplication, FragmentWebApplication } from "fragment";
|
|
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
|
+
```
|
|
888
|
+
|
|
889
|
+
---
|
|
890
|
+
|
|
891
|
+
## Testing
|
|
892
|
+
|
|
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**
|
|
901
|
+
|
|
902
|
+
```typescript
|
|
903
|
+
import { describe, it, expect } from "fragment";
|
|
904
|
+
import { DIContainer } from "fragment";
|
|
905
|
+
import { UserService } from "../src/services/user.service";
|
|
906
|
+
|
|
907
|
+
describe("UserService", () => {
|
|
908
|
+
let userService: UserService;
|
|
909
|
+
|
|
910
|
+
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",
|
|
920
|
+
});
|
|
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
|
+
});
|
|
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");
|
|
940
|
+
});
|
|
941
|
+
});
|
|
942
|
+
```
|
|
943
|
+
|
|
944
|
+
```bash
|
|
945
|
+
# Run tests
|
|
946
|
+
fragment test
|
|
947
|
+
|
|
948
|
+
# Run with coverage
|
|
949
|
+
fragment test --coverage
|
|
950
|
+
|
|
951
|
+
# Run specific test file
|
|
952
|
+
fragment test test/users.spec.ts
|
|
953
|
+
```
|
|
954
|
+
|
|
955
|
+
---
|
|
956
|
+
|
|
957
|
+
## Production Deployment
|
|
958
|
+
|
|
959
|
+
### Use Case 9: Deploy to Production
|
|
960
|
+
|
|
961
|
+
**Build and Deploy**
|
|
962
|
+
|
|
963
|
+
```bash
|
|
964
|
+
# Build for production
|
|
965
|
+
fragment build
|
|
966
|
+
|
|
967
|
+
# Verify build
|
|
968
|
+
ls dist/
|
|
969
|
+
|
|
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
|
|
975
|
+
|
|
976
|
+
# Run migrations
|
|
977
|
+
fragment migrate:run
|
|
978
|
+
|
|
979
|
+
# Start production server
|
|
980
|
+
npm start
|
|
981
|
+
```
|
|
982
|
+
|
|
983
|
+
**Docker Deployment**
|
|
984
|
+
|
|
985
|
+
**Dockerfile:**
|
|
986
|
+
|
|
987
|
+
```dockerfile
|
|
988
|
+
FROM node:18-alpine
|
|
989
|
+
|
|
990
|
+
WORKDIR /app
|
|
991
|
+
|
|
992
|
+
# Copy package files
|
|
993
|
+
COPY package*.json ./
|
|
994
|
+
|
|
995
|
+
# Install production dependencies
|
|
996
|
+
RUN npm ci --only=production
|
|
997
|
+
|
|
998
|
+
# Copy source code
|
|
999
|
+
COPY . .
|
|
1000
|
+
|
|
1001
|
+
# Build TypeScript
|
|
1002
|
+
RUN npm run build
|
|
1003
|
+
|
|
1004
|
+
# Expose port
|
|
1005
|
+
EXPOSE 3000
|
|
1006
|
+
|
|
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
|
|
1062
|
+
```
|
|
1063
|
+
|
|
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
|
+
## Troubleshooting
|
|
1212
|
+
|
|
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": 10,
|
|
1408
|
+
"maxQueryExecutionTime": 1000
|
|
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.
|