@veloxts/core 0.4.1 → 0.4.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.
Files changed (2) hide show
  1. package/README.md +20 -1229
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,1255 +1,46 @@
1
1
  # @veloxts/core
2
2
 
3
- > **Alpha Release** - This framework is in early development. APIs may change between versions. Not recommended for production use yet.
3
+ **Pre-Alpha Notice:** This framework is in early development (v0.4.x). APIs are subject to change. Not recommended for production use. Documentation may be incomplete or out of date.
4
4
 
5
- Foundation package for the VeloxTS framework - provides the core Fastify wrapper, plugin system, and base context.
5
+ ## What is this?
6
6
 
7
- ## Installation
7
+ Core foundation package for the VeloxTS Framework, providing the Fastify wrapper, plugin system, dependency injection container, and base context.
8
8
 
9
- ```bash
10
- npm install @veloxts/core
11
- # or
12
- pnpm add @veloxts/core
13
- ```
14
-
15
- ## Quick Start
16
-
17
- ```typescript
18
- import { veloxApp } from '@veloxts/core';
19
-
20
- // Create application
21
- const app = await veloxApp({
22
- port: 3210,
23
- host: '0.0.0.0',
24
- logger: true,
25
- });
26
-
27
- // Start server
28
- await app.start();
29
- console.log(`Server running on ${app.address}`);
30
- ```
31
-
32
- ## Core API
33
-
34
- ### `veloxApp(config?)`
35
-
36
- Creates a new VeloxTS application instance with sensible defaults.
37
-
38
- **Configuration Options:**
39
-
40
- ```typescript
41
- interface VeloxAppConfig {
42
- port?: number; // Port to listen on (default: 3000)
43
- host?: string; // Host to bind to (default: '0.0.0.0')
44
- logger?: boolean | object; // Enable logging (default: true in dev)
45
- fastify?: FastifyOptions; // Additional Fastify options
46
- }
47
- ```
48
-
49
- **Example:**
50
-
51
- ```typescript
52
- const app = await veloxApp({
53
- port: 4000,
54
- host: '127.0.0.1',
55
- logger: {
56
- level: 'info',
57
- prettyPrint: true,
58
- },
59
- fastify: {
60
- requestTimeout: 30000,
61
- bodyLimit: 1048576 * 10, // 10MB
62
- },
63
- });
64
- ```
65
-
66
- **Returns:** Promise resolving to `VeloxApp` instance
67
-
68
- ### VeloxApp Instance
69
-
70
- The application instance provides methods for lifecycle management:
71
-
72
- ```typescript
73
- interface VeloxApp {
74
- server: FastifyInstance; // Underlying Fastify server
75
- config: VeloxAppConfig; // Application configuration
76
- isRunning: boolean; // Server running state
77
- address: string | null; // Server address if running
78
-
79
- // Methods
80
- start(): Promise<void>; // Start the server
81
- stop(): Promise<void>; // Stop the server gracefully
82
- register(plugin, options?): Promise<void>; // Register a plugin
83
- beforeShutdown(handler): void; // Add shutdown handler
84
- }
85
- ```
86
-
87
- **Example:**
88
-
89
- ```typescript
90
- const app = await veloxApp();
91
-
92
- // Register plugins
93
- await app.register(databasePlugin);
94
- await app.register(routerPlugin);
95
-
96
- // Add shutdown hook
97
- app.beforeShutdown(async () => {
98
- console.log('Cleaning up resources...');
99
- });
100
-
101
- // Start server
102
- await app.start();
103
- console.log(`Server running at ${app.address}`);
104
-
105
- // Graceful shutdown
106
- process.on('SIGTERM', async () => {
107
- await app.stop();
108
- });
109
- ```
110
-
111
- ## Plugin System
112
-
113
- VeloxTS's plugin system extends functionality while maintaining type safety and encapsulation.
114
-
115
- ### Defining Plugins
116
-
117
- Use `definePlugin()` to create reusable plugins:
118
-
119
- ```typescript
120
- import { definePlugin } from '@veloxts/core';
121
- import { PrismaClient } from '@prisma/client';
122
-
123
- export const databasePlugin = definePlugin({
124
- name: '@myapp/database',
125
- version: '1.0.0',
126
- async register(server, options) {
127
- const db = new PrismaClient();
128
-
129
- // Decorate server with database client
130
- server.decorate('db', db);
131
-
132
- // Add lifecycle hook
133
- server.addHook('onClose', async () => {
134
- await db.$disconnect();
135
- });
136
- },
137
- });
138
- ```
139
-
140
- ### Extending Context
141
-
142
- Use TypeScript declaration merging to extend the base context:
143
-
144
- ```typescript
145
- import type { PrismaClient } from '@prisma/client';
146
-
147
- declare module '@veloxts/core' {
148
- interface BaseContext {
149
- db: PrismaClient;
150
- }
151
- }
152
- ```
153
-
154
- Now `ctx.db` is available in all route handlers with full type safety.
155
-
156
- ### Registering Plugins
157
-
158
- ```typescript
159
- // Register with app instance
160
- await app.register(databasePlugin);
161
-
162
- // Register with options
163
- await app.register(databasePlugin, {
164
- connectionString: process.env.DATABASE_URL,
165
- });
166
- ```
167
-
168
- ### Plugin Example: Database Integration
169
-
170
- Complete example integrating Prisma:
171
-
172
- ```typescript
173
- import { definePlugin, type BaseContext } from '@veloxts/core';
174
- import { PrismaClient } from '@prisma/client';
175
-
176
- // Define plugin
177
- export const databasePlugin = definePlugin({
178
- name: '@myapp/database',
179
- version: '1.0.0',
180
- async register(server, options) {
181
- const prisma = new PrismaClient({
182
- log: options?.log || ['error'],
183
- });
184
-
185
- // Test connection
186
- await prisma.$connect();
187
-
188
- // Decorate Fastify instance
189
- server.decorate('db', prisma);
190
-
191
- // Graceful shutdown
192
- server.addHook('onClose', async () => {
193
- await prisma.$disconnect();
194
- });
195
-
196
- console.log('Database connected');
197
- },
198
- });
199
-
200
- // Extend context
201
- declare module '@veloxts/core' {
202
- interface BaseContext {
203
- db: PrismaClient;
204
- }
205
- }
206
-
207
- // Use in app
208
- const app = await veloxApp();
209
- await app.register(databasePlugin);
210
- ```
211
-
212
- ## Context System
213
-
214
- The context object provides request-scoped state accessible throughout the request lifecycle.
215
-
216
- ### Base Context
217
-
218
- Every request has a base context:
219
-
220
- ```typescript
221
- interface BaseContext {
222
- request: FastifyRequest; // Fastify request object
223
- reply: FastifyReply; // Fastify reply object
224
- }
225
- ```
226
-
227
- ### Accessing Context
228
-
229
- Context is available in route handlers:
230
-
231
- ```typescript
232
- app.server.get('/users', async (request, reply) => {
233
- // Access base context
234
- const { request: req, reply: rep } = request.context;
235
-
236
- // Access plugin-extended properties
237
- const users = await request.context.db.user.findMany();
238
-
239
- return users;
240
- });
241
- ```
242
-
243
- ### Context in Procedures
244
-
245
- When using `@veloxts/router`, context is passed to procedure handlers:
246
-
247
- ```typescript
248
- import { procedure } from '@veloxts/router';
249
-
250
- const getUsers = procedure()
251
- .query(async ({ ctx }) => {
252
- // ctx has type BaseContext with extensions
253
- return ctx.db.user.findMany();
254
- });
255
- ```
256
-
257
- ## Error Handling
258
-
259
- VeloxTS provides structured error classes for consistent API responses.
260
-
261
- ### Error Classes
262
-
263
- ```typescript
264
- import {
265
- VeloxError,
266
- ValidationError,
267
- NotFoundError,
268
- UnauthorizedError,
269
- ForbiddenError,
270
- } from '@veloxts/core';
271
- ```
272
-
273
- ### `VeloxError`
274
-
275
- Base error class with status code:
276
-
277
- ```typescript
278
- throw new VeloxError('Something went wrong', 500);
279
-
280
- // Response:
281
- // {
282
- // "error": "Something went wrong",
283
- // "statusCode": 500
284
- // }
285
- ```
286
-
287
- ### `ValidationError`
288
-
289
- Validation errors with field-level details:
290
-
291
- ```typescript
292
- throw new ValidationError('Invalid input', {
293
- email: 'Must be a valid email address',
294
- age: 'Must be at least 18',
295
- });
296
-
297
- // Response:
298
- // {
299
- // "error": "Invalid input",
300
- // "statusCode": 400,
301
- // "fields": {
302
- // "email": "Must be a valid email address",
303
- // "age": "Must be at least 18"
304
- // }
305
- // }
306
- ```
307
-
308
- ### `NotFoundError`
309
-
310
- Resource not found errors:
311
-
312
- ```typescript
313
- throw new NotFoundError('User', userId);
314
-
315
- // Response:
316
- // {
317
- // "error": "User not found",
318
- // "statusCode": 404,
319
- // "resource": "User",
320
- // "id": "123"
321
- // }
322
- ```
323
-
324
- ### `UnauthorizedError`
325
-
326
- Authentication required:
327
-
328
- ```typescript
329
- throw new UnauthorizedError('Invalid credentials');
330
-
331
- // Response:
332
- // {
333
- // "error": "Invalid credentials",
334
- // "statusCode": 401
335
- // }
336
- ```
337
-
338
- ### `ForbiddenError`
339
-
340
- Insufficient permissions:
341
-
342
- ```typescript
343
- throw new ForbiddenError('Insufficient permissions');
344
-
345
- // Response:
346
- // {
347
- // "error": "Insufficient permissions",
348
- // "statusCode": 403
349
- // }
350
- ```
351
-
352
- ### Error Handler Example
353
-
354
- ```typescript
355
- import { VeloxError, NotFoundError } from '@veloxts/core';
356
-
357
- app.server.get('/users/:id', async (request, reply) => {
358
- try {
359
- const user = await request.context.db.user.findUnique({
360
- where: { id: request.params.id },
361
- });
362
-
363
- if (!user) {
364
- throw new NotFoundError('User', request.params.id);
365
- }
366
-
367
- return user;
368
- } catch (error) {
369
- if (error instanceof VeloxError) {
370
- // VeloxError is automatically handled by Fastify
371
- throw error;
372
- }
373
-
374
- // Handle unexpected errors
375
- throw new VeloxError('Internal server error', 500);
376
- }
377
- });
378
- ```
379
-
380
- ## Configuration
381
-
382
- ### Default Configuration
383
-
384
- ```typescript
385
- {
386
- port: 3210,
387
- host: '0.0.0.0',
388
- logger: process.env.NODE_ENV !== 'production',
389
- }
390
- ```
391
-
392
- ### Environment-Based Configuration
393
-
394
- ```typescript
395
- const app = await veloxApp({
396
- port: Number(process.env.PORT) || 3210,
397
- host: process.env.HOST || '0.0.0.0',
398
- logger: process.env.NODE_ENV !== 'production',
399
- fastify: {
400
- trustProxy: process.env.TRUST_PROXY === 'true',
401
- },
402
- });
403
- ```
404
-
405
- ### Production Configuration
406
-
407
- ```typescript
408
- const app = await veloxApp({
409
- port: 3210,
410
- host: '0.0.0.0',
411
- logger: {
412
- level: 'warn',
413
- prettyPrint: false,
414
- },
415
- fastify: {
416
- trustProxy: true,
417
- requestTimeout: 30000,
418
- bodyLimit: 1048576, // 1MB
419
- disableRequestLogging: true,
420
- },
421
- });
422
- ```
423
-
424
- ## Lifecycle Management
425
-
426
- ### Graceful Shutdown
427
-
428
- ```typescript
429
- const app = await veloxApp();
430
-
431
- // Add custom shutdown handlers
432
- app.beforeShutdown(async () => {
433
- console.log('Cleaning up resources...');
434
- await database.disconnect();
435
- await cache.flush();
436
- });
437
-
438
- // Handle SIGTERM (e.g., from Kubernetes, Docker)
439
- process.on('SIGTERM', async () => {
440
- console.log('SIGTERM received, shutting down gracefully...');
441
- await app.stop();
442
- process.exit(0);
443
- });
444
-
445
- // Handle SIGINT (Ctrl+C)
446
- process.on('SIGINT', async () => {
447
- console.log('SIGINT received, shutting down gracefully...');
448
- await app.stop();
449
- process.exit(0);
450
- });
451
-
452
- await app.start();
453
- ```
454
-
455
- ### Startup and Shutdown Hooks
456
-
457
- Plugins can register lifecycle hooks:
458
-
459
- ```typescript
460
- export const lifecyclePlugin = definePlugin({
461
- name: 'lifecycle',
462
- version: '1.0.0',
463
- async register(server, options) {
464
- // Called when server starts listening
465
- server.addHook('onListen', async () => {
466
- console.log('Server is listening');
467
- });
468
-
469
- // Called when server is closing
470
- server.addHook('onClose', async () => {
471
- console.log('Server is closing');
472
- });
473
-
474
- // Called for each request
475
- server.addHook('onRequest', async (request, reply) => {
476
- console.log(`Request: ${request.method} ${request.url}`);
477
- });
478
- },
479
- });
480
- ```
481
-
482
- ## Advanced Usage
483
-
484
- ### Custom Fastify Instance
485
-
486
- For advanced scenarios, you can pass a custom Fastify instance:
487
-
488
- ```typescript
489
- import Fastify from 'fastify';
490
-
491
- const fastify = Fastify({
492
- logger: true,
493
- requestIdHeader: 'x-request-id',
494
- trustProxy: true,
495
- });
496
-
497
- const app = await veloxApp({ fastify });
498
- ```
499
-
500
- ### Accessing Fastify Directly
501
-
502
- The underlying Fastify instance is available via `app.server`:
503
-
504
- ```typescript
505
- const app = await veloxApp();
506
-
507
- // Add Fastify plugins
508
- await app.server.register(fastifyCors, {
509
- origin: true,
510
- });
511
-
512
- // Add raw routes
513
- app.server.get('/custom', async (request, reply) => {
514
- return { message: 'Custom route' };
515
- });
516
- ```
517
-
518
- ## Practical Examples
519
-
520
- ### Complete Application Setup
521
-
522
- ```typescript
523
- import { veloxApp } from '@veloxts/core';
524
- import { createDatabasePlugin } from '@veloxts/orm';
525
- import { registerRestRoutes } from '@veloxts/router';
526
- import { PrismaClient } from '@prisma/client';
527
- import { userProcedures } from './procedures/users';
528
-
529
- // Initialize
530
- const prisma = new PrismaClient();
531
- const app = await veloxApp({
532
- port: Number(process.env.PORT) || 3210,
533
- logger: true,
534
- });
535
-
536
- // Register database plugin
537
- await app.register(createDatabasePlugin({ client: prisma }));
538
-
539
- // Register API routes
540
- await registerRestRoutes(app.server, {
541
- prefix: '/api',
542
- procedures: {
543
- users: userProcedures,
544
- },
545
- });
546
-
547
- // Graceful shutdown
548
- const shutdown = async () => {
549
- await app.stop();
550
- process.exit(0);
551
- };
552
-
553
- process.on('SIGTERM', shutdown);
554
- process.on('SIGINT', shutdown);
555
-
556
- // Start server
557
- await app.start();
558
- console.log(`Server running at ${app.address}`);
559
- ```
560
-
561
- ## Dependency Injection
562
-
563
- VeloxTS provides a powerful, type-safe dependency injection container inspired by Angular and NestJS. The DI system enables automatic constructor injection, lifecycle management, and clean separation of concerns.
564
-
565
- ### Overview
566
-
567
- The DI container manages service creation, dependency resolution, and lifecycle:
568
-
569
- - **Automatic constructor injection** via TypeScript decorators
570
- - **Multiple provider types**: classes, factories, values, aliases
571
- - **Lifecycle scopes**: singleton, transient, request-scoped
572
- - **Type safety**: Full TypeScript inference without code generation
573
- - **Circular dependency detection**
574
- - **Fastify integration** for request-scoped services
575
-
576
- ### Accessing the Container
577
-
578
- Every VeloxApp instance has a DI container available:
579
-
580
- ```typescript
581
- const app = await veloxApp();
582
-
583
- // Access the container
584
- app.container.register({ /* ... */ });
585
- const service = app.container.resolve(MyService);
586
- ```
587
-
588
- ### Tokens
589
-
590
- Tokens are unique identifiers for services. VeloxTS supports three token types:
591
-
592
- #### Class Tokens
593
-
594
- Use the class itself as a token:
9
+ ## Part of @veloxts/velox
595
10
 
596
- ```typescript
597
- @Injectable()
598
- class UserService {
599
- getUsers() { /* ... */ }
600
- }
601
-
602
- // Register with class token
603
- app.container.register({
604
- provide: UserService,
605
- useClass: UserService
606
- });
607
-
608
- // Resolve with class token
609
- const userService = app.container.resolve(UserService);
610
- ```
611
-
612
- #### String Tokens
613
-
614
- Use string literals for named services:
615
-
616
- ```typescript
617
- import { token } from '@veloxts/core';
618
-
619
- interface DatabaseClient {
620
- query(sql: string): Promise<unknown>;
621
- }
622
-
623
- const DATABASE = token<DatabaseClient>('DATABASE');
624
-
625
- app.container.register({
626
- provide: DATABASE,
627
- useFactory: () => createDatabaseClient()
628
- });
629
-
630
- const db = app.container.resolve(DATABASE);
631
- ```
632
-
633
- #### Symbol Tokens
634
-
635
- Use symbols for guaranteed uniqueness:
636
-
637
- ```typescript
638
- import { token } from '@veloxts/core';
639
-
640
- interface Logger {
641
- log(message: string): void;
642
- }
643
-
644
- const LOGGER = token.symbol<Logger>('Logger');
645
-
646
- app.container.register({
647
- provide: LOGGER,
648
- useClass: ConsoleLogger
649
- });
650
-
651
- const logger = app.container.resolve(LOGGER);
652
- ```
653
-
654
- ### Provider Types
655
-
656
- The container supports four provider types for different use cases. For common patterns, VeloxTS provides succinct provider helpers that simplify registration.
657
-
658
- #### Succinct Provider Helpers
659
-
660
- VeloxTS provides convenient helpers for common provider patterns:
661
-
662
- ```typescript
663
- import { singleton, scoped, transient, value, factory } from '@veloxts/core';
664
-
665
- // Singleton scope (one instance for entire app)
666
- app.container.register(singleton(ConfigService));
667
-
668
- // Request scope (one instance per HTTP request)
669
- app.container.register(scoped(UserContext));
670
-
671
- // Transient scope (new instance every time)
672
- app.container.register(transient(RequestIdGenerator));
673
-
674
- // Value provider (provide existing value)
675
- app.container.register(value(CONFIG, { port: 3210 }));
676
-
677
- // Factory provider (use factory function)
678
- app.container.register(
679
- factory(DATABASE, (config: ConfigService) => {
680
- return new PrismaClient({ datasources: { db: { url: config.databaseUrl } } });
681
- }, [ConfigService])
682
- );
683
- ```
684
-
685
- These helpers are equivalent to the full provider syntax but more concise and readable.
686
-
687
- #### Class Provider
688
-
689
- Instantiate a class with automatic dependency injection:
690
-
691
- ```typescript
692
- @Injectable()
693
- class UserService {
694
- constructor(
695
- private db: DatabaseClient,
696
- private logger: Logger
697
- ) {}
698
- }
699
-
700
- // Using succinct helper (recommended)
701
- app.container.register(singleton(UserService));
702
-
703
- // Or using full syntax
704
- app.container.register({
705
- provide: UserService,
706
- useClass: UserService,
707
- scope: Scope.SINGLETON
708
- });
709
- ```
710
-
711
- #### Factory Provider
712
-
713
- Use a factory function to create instances:
714
-
715
- ```typescript
716
- // Using succinct helper (recommended)
717
- app.container.register(
718
- factory(DATABASE, (config: ConfigService) => {
719
- return createDatabaseClient(config.databaseUrl);
720
- }, [ConfigService])
721
- );
722
-
723
- // Or using full syntax
724
- app.container.register({
725
- provide: DATABASE,
726
- useFactory: (config: ConfigService) => {
727
- return createDatabaseClient(config.databaseUrl);
728
- },
729
- inject: [ConfigService],
730
- scope: Scope.SINGLETON
731
- });
732
- ```
733
-
734
- #### Value Provider
735
-
736
- Provide an existing value directly:
737
-
738
- ```typescript
739
- const CONFIG = token<AppConfig>('CONFIG');
740
-
741
- // Using succinct helper (recommended)
742
- app.container.register(
743
- value(CONFIG, {
744
- port: 3210,
745
- host: 'localhost',
746
- debug: true
747
- })
748
- );
11
+ This package is part of the VeloxTS Framework. For the complete framework experience, install:
749
12
 
750
- // Or using full syntax
751
- app.container.register({
752
- provide: CONFIG,
753
- useValue: {
754
- port: 3210,
755
- host: 'localhost',
756
- debug: true
757
- }
758
- });
759
- ```
760
-
761
- #### Existing Provider (Alias)
762
-
763
- Create an alias to another token:
764
-
765
- ```typescript
766
- // Register concrete implementation
767
- app.container.register({
768
- provide: ConsoleLogger,
769
- useClass: ConsoleLogger
770
- });
771
-
772
- // Create an alias
773
- app.container.register({
774
- provide: LOGGER,
775
- useExisting: ConsoleLogger
776
- });
777
-
778
- // Both resolve to the same instance
779
- const logger1 = app.container.resolve(LOGGER);
780
- const logger2 = app.container.resolve(ConsoleLogger);
781
- // logger1 === logger2
782
- ```
783
-
784
- ### Lifecycle Scopes
785
-
786
- Scopes determine how service instances are created and shared.
787
-
788
- #### Singleton Scope
789
-
790
- One instance for the entire application (default):
791
-
792
- ```typescript
793
- @Injectable({ scope: Scope.SINGLETON })
794
- class ConfigService {
795
- // Shared across all requests
796
- }
797
-
798
- // Register using succinct helper
799
- app.container.register(singleton(ConfigService));
800
- ```
801
-
802
- **Best for:**
803
- - Configuration services
804
- - Database connection pools
805
- - Cache clients
806
- - Stateless utility services
807
-
808
- #### Transient Scope
809
-
810
- New instance on every resolution:
811
-
812
- ```typescript
813
- @Injectable({ scope: Scope.TRANSIENT })
814
- class RequestIdGenerator {
815
- readonly id = crypto.randomUUID();
816
- }
817
-
818
- // Register using succinct helper
819
- app.container.register(transient(RequestIdGenerator));
13
+ ```bash
14
+ npm install @veloxts/velox
820
15
  ```
821
16
 
822
- **Best for:**
823
- - Services with mutable state
824
- - Factories that produce unique objects
825
- - Services where isolation is critical
826
-
827
- #### Request Scope
828
-
829
- One instance per HTTP request:
830
-
831
- ```typescript
832
- @Injectable({ scope: Scope.REQUEST })
833
- class UserContext {
834
- constructor(private request: FastifyRequest) {}
17
+ Visit [@veloxts/velox](https://www.npmjs.com/package/@veloxts/velox) for the complete framework documentation.
835
18
 
836
- get userId(): string {
837
- return this.request.user?.id;
838
- }
839
- }
19
+ ## Standalone Installation
840
20
 
841
- // Register using succinct helper
842
- app.container.register(scoped(UserContext));
21
+ ```bash
22
+ npm install @veloxts/core
843
23
  ```
844
24
 
845
- **Best for:**
846
- - User context/session data
847
- - Request-specific caching
848
- - Transaction management
849
- - Audit logging with request context
850
-
851
- **Note:** Request-scoped services require a Fastify request context. Resolving them outside a request handler throws an error.
25
+ ## Documentation
852
26
 
853
- ### Decorators
27
+ For detailed documentation, usage examples, and API reference, see [GUIDE.md](./GUIDE.md).
854
28
 
855
- Use TypeScript decorators for automatic dependency injection.
856
-
857
- #### Prerequisites
858
-
859
- Enable decorators in `tsconfig.json`:
860
-
861
- ```json
862
- {
863
- "compilerOptions": {
864
- "experimentalDecorators": true,
865
- "emitDecoratorMetadata": true
866
- }
867
- }
868
- ```
869
-
870
- Import `reflect-metadata` at your app's entry point:
29
+ ## Quick Example
871
30
 
872
31
  ```typescript
873
- import 'reflect-metadata';
874
32
  import { veloxApp } from '@veloxts/core';
875
- ```
876
-
877
- #### @Injectable()
878
-
879
- Marks a class as injectable:
880
-
881
- ```typescript
882
- @Injectable()
883
- class UserService {
884
- constructor(private db: DatabaseClient) {}
885
- }
886
-
887
- @Injectable({ scope: Scope.REQUEST })
888
- class RequestLogger {
889
- constructor(private request: FastifyRequest) {}
890
- }
891
- ```
892
-
893
- #### @Inject()
894
-
895
- Explicitly specifies the injection token:
896
-
897
- ```typescript
898
- const DATABASE = token<DatabaseClient>('DATABASE');
899
-
900
- @Injectable()
901
- class UserService {
902
- constructor(
903
- @Inject(DATABASE) private db: DatabaseClient,
904
- private config: ConfigService // Auto-injected by class token
905
- ) {}
906
- }
907
- ```
908
-
909
- Use `@Inject()` when:
910
- - Injecting by string or symbol token
911
- - Injecting an interface (TypeScript interfaces are erased at runtime)
912
- - Automatic type resolution doesn't work
913
-
914
- #### @Optional()
915
-
916
- Marks a dependency as optional:
917
-
918
- ```typescript
919
- @Injectable()
920
- class NotificationService {
921
- constructor(
922
- @Optional() private emailService?: EmailService,
923
- @Optional() @Inject(SMS_SERVICE) private smsService?: SmsService
924
- ) {}
925
-
926
- notify(message: string) {
927
- // Gracefully handle missing services
928
- this.emailService?.send(message);
929
- this.smsService?.send(message);
930
- }
931
- }
932
- ```
933
-
934
- If an optional dependency cannot be resolved, `undefined` is injected instead of throwing an error.
935
-
936
- ### Container API
937
-
938
- #### Registering Services
939
-
940
- Register a single provider:
941
-
942
- ```typescript
943
- // Using succinct helper (recommended)
944
- app.container.register(scoped(UserService));
945
-
946
- // Or using full syntax
947
- app.container.register({
948
- provide: UserService,
949
- useClass: UserService,
950
- scope: Scope.REQUEST
951
- });
952
- ```
953
-
954
- Register multiple providers:
955
-
956
- ```typescript
957
- // Using succinct helpers (recommended)
958
- app.container.registerMany([
959
- singleton(UserService),
960
- singleton(PostService),
961
- value(CONFIG, appConfig)
962
- ]);
963
-
964
- // Or using full syntax
965
- app.container.registerMany([
966
- { provide: UserService, useClass: UserService },
967
- { provide: PostService, useClass: PostService },
968
- { provide: CONFIG, useValue: appConfig }
969
- ]);
970
- ```
971
-
972
- #### Resolving Services
973
-
974
- Synchronous resolution:
975
-
976
- ```typescript
977
- const userService = app.container.resolve(UserService);
978
- ```
979
-
980
- Asynchronous resolution (for async factories):
981
-
982
- ```typescript
983
- // Using succinct helper (recommended)
984
- app.container.register(
985
- factory(DATABASE, async (config: ConfigService) => {
986
- const client = createClient(config.dbUrl);
987
- await client.connect();
988
- return client;
989
- }, [ConfigService])
990
- );
991
-
992
- const db = await app.container.resolveAsync(DATABASE);
993
- ```
994
-
995
- Optional resolution (returns `undefined` if not found):
996
-
997
- ```typescript
998
- const service = app.container.resolveOptional(OptionalService);
999
- if (service) {
1000
- service.doSomething();
1001
- }
1002
- ```
1003
-
1004
- #### Request Context
1005
-
1006
- Resolve request-scoped services with context:
1007
-
1008
- ```typescript
1009
- app.server.get('/users', async (request, reply) => {
1010
- const ctx = Container.createContext(request);
1011
- const userContext = app.container.resolve(UserContext, ctx);
1012
-
1013
- return { userId: userContext.userId };
1014
- });
1015
- ```
1016
-
1017
- ### Integration with VeloxApp
1018
-
1019
- The container is automatically attached to the Fastify server for request-scoped services:
1020
-
1021
- ```typescript
1022
- const app = await veloxApp();
1023
-
1024
- // Container is already attached to app.server
1025
- // Request-scoped services work automatically
1026
-
1027
- @Injectable({ scope: Scope.REQUEST })
1028
- class RequestLogger {
1029
- constructor(private request: FastifyRequest) {}
1030
- }
1031
-
1032
- // Register using succinct helper
1033
- app.container.register(scoped(RequestLogger));
1034
-
1035
- app.server.get('/log', async (request, reply) => {
1036
- const ctx = Container.createContext(request);
1037
- const logger = app.container.resolve(RequestLogger, ctx);
1038
- logger.log('Request received');
1039
- return { ok: true };
1040
- });
1041
- ```
1042
-
1043
- ### Practical Examples
1044
-
1045
- #### Complete Service Layer
1046
-
1047
- ```typescript
1048
- import 'reflect-metadata';
1049
- import {
1050
- veloxApp,
1051
- Injectable,
1052
- Inject,
1053
- Scope,
1054
- token,
1055
- singleton,
1056
- scoped,
1057
- factory
1058
- } from '@veloxts/core';
1059
- import { PrismaClient } from '@prisma/client';
1060
-
1061
- // Define tokens
1062
- const DATABASE = token<PrismaClient>('DATABASE');
1063
- const CONFIG = token<AppConfig>('CONFIG');
1064
-
1065
- // Configuration service
1066
- @Injectable()
1067
- class ConfigService {
1068
- get databaseUrl(): string {
1069
- return process.env.DATABASE_URL!;
1070
- }
1071
- }
1072
-
1073
- // Database service
1074
- @Injectable()
1075
- class DatabaseService {
1076
- constructor(@Inject(DATABASE) private db: PrismaClient) {}
1077
-
1078
- async findUser(id: string) {
1079
- return this.db.user.findUnique({ where: { id } });
1080
- }
1081
- }
1082
-
1083
- // Business logic service
1084
- @Injectable({ scope: Scope.REQUEST })
1085
- class UserService {
1086
- constructor(
1087
- private database: DatabaseService,
1088
- private config: ConfigService
1089
- ) {}
1090
-
1091
- async getUser(id: string) {
1092
- return this.database.findUser(id);
1093
- }
1094
- }
1095
-
1096
- // Create app
1097
- const app = await veloxApp();
1098
-
1099
- // Register services using succinct helpers
1100
- app.container.register(
1101
- factory(DATABASE, (config: ConfigService) => {
1102
- return new PrismaClient({
1103
- datasources: { db: { url: config.databaseUrl } }
1104
- });
1105
- }, [ConfigService])
1106
- );
1107
-
1108
- app.container.register(singleton(ConfigService));
1109
- app.container.register(singleton(DatabaseService));
1110
- app.container.register(scoped(UserService));
1111
-
1112
- // Use in routes
1113
- app.server.get('/users/:id', async (request, reply) => {
1114
- const ctx = Container.createContext(request);
1115
- const userService = app.container.resolve(UserService, ctx);
1116
-
1117
- const user = await userService.getUser(request.params.id);
1118
- return user;
1119
- });
1120
33
 
34
+ const app = await veloxApp({ port: 3210 });
1121
35
  await app.start();
36
+ console.log(`Server running on ${app.address}`);
1122
37
  ```
1123
38
 
1124
- #### Testing with Child Containers
1125
-
1126
- ```typescript
1127
- import { createContainer, asClass } from '@veloxts/core';
1128
-
1129
- // Create a child container for testing
1130
- const testContainer = app.container.createChild();
1131
-
1132
- // Override services with mocks
1133
- class MockDatabaseService {
1134
- async findUser(id: string) {
1135
- return { id, name: 'Test User' };
1136
- }
1137
- }
1138
-
1139
- testContainer.register({
1140
- provide: DatabaseService,
1141
- useClass: MockDatabaseService
1142
- });
1143
-
1144
- // Test with mocked dependencies
1145
- const userService = testContainer.resolve(UserService);
1146
- const user = await userService.getUser('123');
1147
- // user comes from MockDatabaseService
1148
- ```
1149
-
1150
- #### Auto-Registration
1151
-
1152
- Enable auto-registration to automatically register `@Injectable` classes:
1153
-
1154
- ```typescript
1155
- const app = await createVeloxApp();
1156
- const autoContainer = createContainer({ autoRegister: true });
1157
-
1158
- @Injectable()
1159
- class AutoService {}
1160
-
1161
- // No need to manually register
1162
- const service = autoContainer.resolve(AutoService);
1163
- // Automatically registers and resolves
1164
- ```
1165
-
1166
- ### Advanced Features
1167
-
1168
- #### Circular Dependency Detection
1169
-
1170
- The container detects circular dependencies:
1171
-
1172
- ```typescript
1173
- @Injectable()
1174
- class ServiceA {
1175
- constructor(private b: ServiceB) {}
1176
- }
1177
-
1178
- @Injectable()
1179
- class ServiceB {
1180
- constructor(private a: ServiceA) {}
1181
- }
1182
-
1183
- // Throws: Circular dependency detected: ServiceA -> ServiceB -> ServiceA
1184
- const service = app.container.resolve(ServiceA);
1185
- ```
1186
-
1187
- #### Container Hierarchy
1188
-
1189
- Create parent-child container hierarchies:
1190
-
1191
- ```typescript
1192
- const parentContainer = createContainer();
1193
- parentContainer.register({
1194
- provide: ConfigService,
1195
- useClass: ConfigService
1196
- });
1197
-
1198
- const childContainer = parentContainer.createChild();
1199
- // Child inherits ConfigService from parent
1200
- // Can override with its own providers
1201
-
1202
- childContainer.register({
1203
- provide: UserService,
1204
- useClass: UserService
1205
- });
1206
- ```
1207
-
1208
- #### Debug Information
1209
-
1210
- Get container statistics:
1211
-
1212
- ```typescript
1213
- const info = app.container.getDebugInfo();
1214
- console.log(info);
1215
- // {
1216
- // providerCount: 5,
1217
- // providers: [
1218
- // 'class(UserService, request)',
1219
- // 'factory(DATABASE, singleton)',
1220
- // 'value(CONFIG)',
1221
- // 'existing(LOGGER => ConsoleLogger)'
1222
- // ],
1223
- // hasParent: false,
1224
- // autoRegister: false
1225
- // }
1226
- ```
1227
-
1228
- ### Best Practices
1229
-
1230
- 1. **Use tokens for interfaces**: Create string or symbol tokens for interface types since TypeScript interfaces don't exist at runtime.
1231
-
1232
- 2. **Prefer singleton scope**: Use `Scope.SINGLETON` by default for stateless services to improve performance.
1233
-
1234
- 3. **Use request scope for user context**: Store user authentication, request-specific state, and transactions in request-scoped services.
1235
-
1236
- 4. **Avoid circular dependencies**: Refactor shared logic into a third service to break circular dependencies.
1237
-
1238
- 5. **Use `@Inject()` for clarity**: Explicitly specify tokens with `@Inject()` to make dependencies clear and avoid runtime type resolution issues.
1239
-
1240
- 6. **Test with child containers**: Create child containers in tests to override services with mocks without affecting the main container.
1241
-
1242
- ## Related Packages
1243
-
1244
- - [@veloxts/router](/packages/router) - Procedure-based routing
1245
- - [@veloxts/validation](/packages/validation) - Schema validation with Zod
1246
- - [@veloxts/orm](/packages/orm) - Prisma integration
1247
- - [@veloxts/client](/packages/client) - Type-safe frontend API client
1248
- - [@veloxts/cli](/packages/cli) - Developer tooling
1249
-
1250
- ## TypeScript Support
39
+ ## Learn More
1251
40
 
1252
- All exports are fully typed with comprehensive JSDoc documentation. The package includes type definitions and declaration maps for excellent IDE support.
41
+ - [Full Documentation](./GUIDE.md)
42
+ - [VeloxTS Framework](https://www.npmjs.com/package/@veloxts/velox)
43
+ - [GitHub Repository](https://github.com/veloxts/velox-ts-framework)
1253
44
 
1254
45
  ## License
1255
46
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@veloxts/core",
3
- "version": "0.4.1",
3
+ "version": "0.4.3",
4
4
  "description": "Fastify wrapper, DI container, and plugin system for VeloxTS framework",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",