@avleon/core 0.0.44 → 0.0.46

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 (58) hide show
  1. package/License +21 -21
  2. package/README.md +667 -681
  3. package/dist/application.test.js +15 -0
  4. package/dist/controller.test.js +0 -14
  5. package/dist/core/application.d.ts +74 -0
  6. package/dist/core/application.js +424 -0
  7. package/dist/core/router.d.ts +44 -0
  8. package/dist/core/router.js +520 -0
  9. package/dist/core/testing.d.ts +21 -0
  10. package/dist/core/testing.js +104 -0
  11. package/dist/core/types.d.ts +67 -0
  12. package/dist/core/types.js +2 -0
  13. package/dist/event-dispatcher.d.ts +0 -1
  14. package/dist/event-dispatcher.js +4 -7
  15. package/dist/file-storage.test.js +15 -2
  16. package/dist/helpers.d.ts +9 -42
  17. package/dist/helpers.js +19 -411
  18. package/dist/index.d.ts +17 -15
  19. package/dist/index.js +18 -22
  20. package/dist/interfaces/avleon-application.d.ts +74 -26
  21. package/dist/interfaces/avleon-application.js +1 -0
  22. package/dist/middleware.d.ts +11 -4
  23. package/dist/middleware.js +9 -0
  24. package/dist/multipart.d.ts +2 -2
  25. package/dist/openapi.d.ts +70 -3
  26. package/dist/openapi.js +32 -0
  27. package/dist/params.js +1 -6
  28. package/dist/params.test.js +8 -8
  29. package/dist/queue.d.ts +27 -36
  30. package/dist/queue.js +67 -99
  31. package/dist/route-methods.js +16 -5
  32. package/dist/swagger-schema.d.ts +11 -17
  33. package/dist/swagger-schema.js +84 -82
  34. package/dist/swagger-schema.test.js +32 -12
  35. package/dist/utils/common-utils.d.ts +17 -0
  36. package/dist/utils/common-utils.js +108 -0
  37. package/dist/utils/di-utils.d.ts +1 -0
  38. package/dist/utils/di-utils.js +22 -0
  39. package/dist/utils/hash.d.ts +0 -2
  40. package/dist/utils/hash.js +1 -5
  41. package/dist/utils/object-utils.d.ts +11 -0
  42. package/dist/utils/object-utils.js +198 -0
  43. package/dist/utils/validation-utils.d.ts +13 -0
  44. package/dist/utils/validation-utils.js +119 -0
  45. package/dist/validation.js +1 -4
  46. package/dist/websocket.d.ts +3 -0
  47. package/dist/websocket.js +2 -1
  48. package/package.json +53 -39
  49. package/dist/application.d.ts +0 -47
  50. package/dist/application.js +0 -50
  51. package/dist/icore.d.ts +0 -226
  52. package/dist/icore.js +0 -968
  53. package/dist/icore.test.js +0 -14
  54. package/dist/queue.test.d.ts +0 -1
  55. package/dist/queue.test.js +0 -79
  56. package/dist/testing.d.ts +0 -55
  57. package/dist/testing.js +0 -196
  58. /package/dist/{icore.test.d.ts → application.test.d.ts} +0 -0
package/README.md CHANGED
@@ -1,681 +1,667 @@
1
- # Avleon
2
-
3
- ![npm version](https://img.shields.io/npm/v/@avleon/core.svg) ![Build](https://github.com/avleonjs/avleon-core/actions/workflows/release.yml/badge.svg)
4
- ## ⚠️ WARNING
5
-
6
- > **🚧 This project is in active development.**
7
-
8
- ## Overview
9
-
10
- Avleon is a powerful, TypeScript-based web framework built on top of Fastify, designed to simplify API development with a focus on decorators, dependency injection, and OpenAPI documentation. It provides a robust set of tools for building scalable, maintainable web applications with minimal boilerplate code.
11
-
12
- ## Table of Contents
13
-
14
- - [Features](#features)
15
- - [Installation](#installation)
16
- - [Quick Start](#quick-start)
17
- - [Route Based](#route-based)
18
- - [Controller Based](#controller-based)
19
- - [Core Concepts](#core-concepts)
20
- - [Application Creation](#application-creation)
21
- - [Controllers](#controllers)
22
- - [Route Methods](#route-methods)
23
- - [Parameter Decorators](#parameter-decorators)
24
- - [Response Handling](#response-handling)
25
- - [Middleware](#middleware)
26
- - [Authentication & Authorization](#authentication--authorization)
27
- - [Validation](#validation)
28
- - [OpenAPI Documentation](#openapi-documentation)
29
- - [Advanced Features](#advanced-features)
30
- - [Database Integration](#database-integration)
31
- - [File Uploads](#file-uploads)
32
- - [Static Files](#static-files)
33
- - [Testing](#testing)
34
- - [Configuration](#configuration)
35
- - [Route Mapping](#route-mapping)
36
- - [mapGet](#mapget)
37
- - [mapPost](#mappost)
38
- - [mapPut](#mapput)
39
- - [mapDelete](#mapdelete)
40
- - [Testing](#testing)
41
- - [WebSocket](#websocket-intregation-socketio)
42
-
43
- ## Features
44
-
45
- - **Decorator-based API Development**: Define controllers, routes, and middleware using TypeScript decorators
46
- - **Dependency Injection**: Built-in DI system using TypeDI for service management
47
- - **OpenAPI/Swagger Integration**: Automatic API documentation generation with support for both Swagger UI and Scalar
48
- - **Validation**: Request validation with support for class-validator and custom validation rules
49
- - **Middleware System**: Flexible middleware architecture for request processing
50
- - **Response Handling**: Standardized response formats with HTTP status codes
51
- - **File Upload**: Built-in support for multipart file uploads with file validation
52
- - **Authentication & Authorization**: Middleware for securing your API endpoints
53
- - **Database Integration**: Support for TypeORM for database operations
54
- - **Queue System**: Background job processing capabilities
55
- - **Environment Configuration**: Environment variable management
56
- - **Logging**: Integrated logging with Pino
57
- - **Testing**: Built-in testing utilities for API endpoints
58
-
59
- ## Installation
60
-
61
- ```bash
62
- npx @avleon/cli new myapp
63
- # or
64
- yarn dlx @avleon/cli new myapp
65
- # or
66
- pnpm dlx @avleon/cli new myapp
67
- ```
68
-
69
- ## Quick Start
70
-
71
- ### Minimal
72
- ```typescript
73
- import { Avleon } from "@avleon/core";
74
-
75
- const app = Avleon.createApplication();
76
- app.mapGet("/", () => "Hello, Avleon");
77
- app.run(); // or app.run(3000);
78
- ```
79
-
80
- ### Controller Based
81
- ```typescript
82
- import { Avleon, ApiController, Get, Results } from "@avleon/core";
83
-
84
- // Define a controller
85
- @ApiController
86
- class HelloController {
87
- @Get()
88
- sayHello() {
89
- return "Hello, Avleon!";
90
- }
91
- }
92
-
93
- // Create and start the application
94
- const app = Avleon.createApplication();
95
- app.useControllers([HelloController]);
96
- app.run();
97
- ```
98
-
99
- ## Core Concepts
100
-
101
- ### Application Creation
102
- Avleon provides a builder pattern for creating applications:
103
-
104
- ```typescript
105
- import { Avleon } from "@avleon/core";
106
-
107
- // Create an application
108
- const app = Avleon.createApplication();
109
-
110
- // Configure and run the application
111
- app.useCors();
112
- app.useControllers([UserController]);
113
- // For auto register controller `app.useControllers({auto:true});`
114
-
115
- app.run(); // or app.run(port)
116
- ```
117
-
118
- ### Controllers
119
- Controllers are the entry points for your API requests. They are defined using the `@ApiController` decorator:
120
-
121
- ```typescript
122
- @ApiController("/users")
123
- class UserController {
124
- // Route handlers go here
125
- }
126
- ```
127
-
128
- ### Route Methods
129
- Define HTTP methods using decorators:
130
-
131
- ```typescript
132
- @Get('/')
133
- async getUsers() {
134
- // Handle GET request
135
- }
136
-
137
- @Post('/')
138
- async createUser(@Body() user: UserDto) {
139
- // Handle POST request
140
- }
141
-
142
- @Put('/:id')
143
- async updateUser(@Param('id') id: string, @Body() user: UserDto) {
144
- // Handle PUT request
145
- }
146
-
147
- @Delete('/:id')
148
- async deleteUser(@Param('id') id: string) {
149
- // Handle DELETE request
150
- }
151
- ```
152
-
153
- ### Parameter Decorators
154
- Extract data from requests using parameter decorators:
155
-
156
- ```typescript
157
- @Get('/:id')
158
- async getUser(
159
- @Param('id') id: string,
160
- @Query('include') include: string,
161
- @Header('authorization') token: string,
162
- @Body() data: UserDto
163
- ) {
164
- // Access route parameters, query strings, headers, and request body
165
- }
166
- ```
167
-
168
- <!-- You can also access the current user and files:
169
-
170
- ```typescript
171
- @Post('/upload')
172
- async uploadFile(
173
- @User() currentUser: any,
174
- @File() file: any
175
- ) {
176
- // Access the current user and uploaded file
177
- }
178
-
179
- @Post('/upload-multiple')
180
- async uploadFiles(
181
- @Files() files: any[]
182
- ) {
183
- // Access multiple uploaded files
184
- }
185
- ``` -->
186
-
187
- ### Error Handling
188
- Return standardized responses using the `HttpResponse` and `HttpExceptions` class:
189
-
190
- ```typescript
191
- @Get('/:id')
192
- async getUser(@Param('id') id: string) {
193
- const user = await this.userService.findById(id);
194
-
195
- if (!user) {
196
- throw HttpExceptions.NotFound('User not found');
197
- }
198
-
199
- return HttpResponse.Ok(user);
200
- }
201
- ```
202
-
203
- ### Middleware
204
- Create and apply middleware for cross-cutting concerns:
205
-
206
- ```typescript
207
- @Middleware
208
- class LoggingMiddleware extends AppMiddleware {
209
- async invoke(req: IRequest) {
210
- console.log(`Request: ${req.method} ${req.url}`);
211
- return req;
212
- }
213
- }
214
-
215
- @UseMiddleware(LoggingMiddleware)
216
- @ApiController("/users")
217
- class UserController {
218
- // Controller methods
219
- }
220
- ```
221
-
222
- You can also apply middleware to specific routes:
223
-
224
- ```typescript
225
- @ApiController("/users")
226
- class UserController {
227
- @UseMiddleware(LoggingMiddleware)
228
- @Get("/")
229
- async getUsers() {
230
- // Only this route will use the LoggingMiddleware
231
- }
232
- }
233
- ```
234
-
235
- ### Authentication & Authorization
236
- Secure your API with authentication and authorization:
237
-
238
- ```typescript
239
- import { CanAuthorize } from "@avleon/core";
240
-
241
- @CanAuthorize
242
- class JwtAuthorization extends AuthorizeMiddleware {
243
- authorize(roles: string[]) {
244
- return async (req: IRequest) => {
245
- // Implement JWT authentication logic
246
- return req;
247
- };
248
- }
249
- }
250
- ```
251
-
252
- Now register the authrization class to our app by `useAuthorization` function;
253
-
254
- ```typescript
255
- app.useAuthorization(JwtAuthorization);
256
- ```
257
-
258
- Then you have access the `AuthUser` on class lavel or method lavel depending on how you use the `@Authorized()` decorator.
259
-
260
- ```typescript
261
- // admin.controller.ts
262
- @Authorized()
263
- @ApiController("/admin")
264
- class AdminController {
265
- // Protected controller methods
266
-
267
- // protected controller has access to AuthUser in each route method
268
- @Get()
269
- async account(@AuthUser() user: User) {
270
- ///
271
- }
272
- }
273
-
274
- // Or protect specific routes with roles
275
- @ApiController("/admin")
276
- class AdminController {
277
- @Authorized({
278
- roles: ["admin"],
279
- })
280
- @Get("/")
281
- async adminDashboard() {
282
- // Only users with 'admin' role can access this
283
- }
284
- }
285
- ```
286
-
287
- ### Validation
288
- Validate request data using class-validator:
289
-
290
- ```typescript
291
- class UserDto {
292
- @IsString()
293
- @IsNotEmpty()
294
- name: string;
295
-
296
- @IsEmail()
297
- email: string;
298
-
299
- @IsNumber()
300
- @Min(0)
301
- @Max(120)
302
- age: number;
303
- }
304
-
305
- @Post('/')
306
- async createUser(@Body() user: UserDto) {
307
- // User data is automatically validated
308
- return user;
309
- }
310
- ```
311
-
312
- You can also use custom validation rules:
313
-
314
- ```typescript
315
- class UserDto {
316
- @Validate({
317
- type: "string",
318
- required: true,
319
- message: "Name is required",
320
- })
321
- name: string;
322
-
323
- @Validate({
324
- type: "number",
325
- min: 0,
326
- max: 120,
327
- message: "Age must be between 0 and 120",
328
- })
329
- age: number;
330
- }
331
- ```
332
-
333
- ### OpenAPI Documentation
334
- Generate API documentation automatically:
335
-
336
- ```typescript
337
-
338
- app.useOpenApi({
339
- info: {
340
- title: "User API",
341
- version: "1.0.0",
342
- description: "API for managing users",
343
- },
344
- servers: [
345
- {
346
- url: "http://localhost:3000",
347
- description: "Development server",
348
- },
349
- ],
350
- });
351
-
352
- ```
353
-
354
- You can also customize the OpenAPI UI:
355
-
356
- ```typescript
357
- app.useOpenApi(OpenApiConfig, (config) => {
358
- // Modify the OpenAPI configuration
359
- config.info.title = "Custom API Title";
360
- return config;
361
- });
362
- ```
363
-
364
- ## Advanced Features
365
-
366
- ### Database Integration
367
-
368
- ## 1. Knex
369
- ```typescript
370
- const app = Avleon.createApplication();
371
- app.useKnex({
372
- client: 'mysql',
373
- connection: {
374
- host: '127.0.0.1',
375
- port: 3306,
376
- user: 'your_database_user',
377
- password: 'your_database_password',
378
- database: 'myapp_test',
379
- },
380
- })
381
- ```
382
- or using config class
383
-
384
- ```typescript
385
- @AppConfig
386
- export class KnexConfig implements IConfig {
387
- // config method is mendatory
388
- // config method has access to environment variables by default
389
- config(env: Environment) {
390
- return {
391
- client: 'mysql',
392
- connection: {
393
- host: env.get("DB_HOST") || '127.0.0.1',
394
- port: env.get("DB_PORT") || 3306,
395
- user: env.get("DB_USER")|| 'your_database_user',
396
- password: env.get("DB_PASS") || 'your_database_password',
397
- database: env.get("DB_NAME") || 'myapp_test',
398
- },
399
- };
400
- }
401
- }
402
-
403
- // now we can register it with our app
404
-
405
- app.useKenx(KnexConfig)
406
- ```
407
-
408
- ### Exmaple uses
409
- ```typescript
410
- import { DB, AppService } from "@avleon/core";
411
-
412
- @AppService
413
- export class UsersService{
414
- constructor(
415
- private readonly db: DB
416
- ){}
417
-
418
- async findAll(){
419
- const result = await this.db.client.select("*").from("users");
420
- return result;
421
- }
422
- }
423
- ```
424
-
425
- ## 2. Typeorm
426
- Connect to databases using TypeORM:
427
-
428
- ```typescript
429
- const app = Avleon.createApplication();
430
- app.useDataSource({
431
- type: "postgres",
432
- host: "localhost",
433
- port: 5432,
434
- username: "postgres",
435
- password: "password",
436
- database: "avleon",
437
- entities: [User],
438
- synchronize: true,
439
- });
440
- ```
441
-
442
- Or use the config class:
443
-
444
- ```typescript
445
- // datasource.config.ts
446
- import { AppConfig, IConfig } from "@avleon/core";
447
-
448
- @AppConfig
449
- export class DataSourceConfig implements IConfig {
450
- // config method is mendatory
451
- // config method has access to environment variables by default
452
- config(env: Environment) {
453
- return {
454
- type: env.get("type") || "postgres",
455
- host: "localhost",
456
- port: 5432,
457
- username: "postgres",
458
- password: "password",
459
- database: "avleon",
460
- entities: [User],
461
- synchronize: true,
462
- };
463
- }
464
- }
465
- ```
466
-
467
- ```typescript
468
- // app.ts
469
- const app = Avleon.createApplication();
470
- app.useDataSource(DataSourceConfig);
471
- // ... other impments
472
- ```
473
-
474
- Now in your Controller or Injected service use can use like this
475
-
476
- ```typescript
477
- import { AppService, InjectRepository } from "@avleon/core";
478
- import { Repository } from "typeorm";
479
- import { User } from "model_path";
480
-
481
- @AppService
482
- export class UserService {
483
- constructor(
484
- @InjectRepository(User)
485
- private readonly _userRepository: Repository<User>,
486
- ) {}
487
-
488
- async findAll() {
489
- const users = await this._userRepository.find();
490
- return users;
491
- }
492
- }
493
- ```
494
-
495
- ### File Uploads & File Storage
496
- Handle file uploads with multipart support:
497
-
498
- ```typescript
499
- // Configure multipart file uploads
500
- app.useMultipart({
501
- destination: path.join(process.cwd(), 'public/uploads'),
502
- limits: {
503
- fileSize: 5 * 1024 * 1024 // 5MB
504
- }
505
- });
506
- ```
507
- ```typescript
508
- // In your controller
509
- import {FileStorage} from '@avleon/core';
510
-
511
- //inject FileStorage into constructor
512
- constructor(
513
- private readonly fileStorage: FileStorage
514
- ){}
515
-
516
- @OpenApi({
517
- description: "Uploading single file"
518
- body:{
519
- type:"object",
520
- properties:{
521
- file:{
522
- type:"string",
523
- format:"binary"
524
- }
525
- },
526
- required:["file"]
527
- }
528
- })
529
- @Post('/upload')
530
- async uploadSingleFile(@UploadFile('file') file: MultipartFile) {
531
- // Process uploaded file
532
- const result = await this.fileStorage.save(file);
533
- // or with new name
534
- // const result = await this.fileStorage.save(file, {as:newname.ext});
535
- // result
536
- // {
537
- // uploadPath:"/uplaod",
538
- // staticPath: "/static/"
539
- //}
540
- return result;
541
- }
542
- ```
543
-
544
- ### Static Files
545
- Serve static files:
546
-
547
- ```typescript
548
- import path from 'path';
549
-
550
-
551
- app.useStaticFiles({
552
- path: path.join(process.cwd(), "public"),
553
- prefix: "/static/",
554
- });
555
- ```
556
-
557
- ## Configuration
558
- Coming soon...
559
-
560
- ## Route Mapping
561
- Avleon provides several methods for mapping routes in your application:
562
-
563
- ### mapGet
564
- The `mapGet` method is used to define GET routes in your application. It takes a path string and a handler function as parameters.
565
-
566
- ```typescript
567
- app.mapGet("/users", async (req, res) => {
568
- // Handle GET request to /users
569
- return { users: [] };
570
- });
571
- ```
572
-
573
- ### mapPost
574
- The `mapPost` method is used to define POST routes in your application. It takes a path string and a handler function as parameters.
575
-
576
- ```typescript
577
- app.mapPost("/users", async (req, res) => {
578
- // Handle POST request to /users
579
- const userData = req.body;
580
- // Process user data
581
- return { success: true };
582
- });
583
- ```
584
-
585
- ### mapPut
586
- The `mapPut` method is used to define PUT routes in your application. It takes a path string and a handler function as parameters.
587
-
588
- ```typescript
589
- app.mapPut("/users/:id", async (req, res) => {
590
- // Handle PUT request to /users/:id
591
- const userId = req.params.id;
592
- const userData = req.body;
593
- // Update user data
594
- return { success: true };
595
- });
596
- ```
597
-
598
- ### mapDelete
599
- The `mapDelete` method is used to define DELETE routes in your application. It takes a path string and a handler function as parameters.
600
-
601
- ```typescript
602
- app.mapDelete("/users/:id", async (req, res) => {
603
- // Handle DELETE request to /users/:id
604
- const userId = req.params.id;
605
- // Delete user
606
- return { success: true };
607
- });
608
- ```
609
-
610
- ### Add openapi and middleware support for inline route
611
- Each of these methods returns a route object that can be used to add middleware or Swagger documentation to the route.
612
-
613
- ```typescript
614
- app
615
- .mapGet("/users", async (req, res) => {
616
- // Handler function
617
- })
618
- .useMiddleware([AuthMiddleware])
619
- .useOpenApi({
620
- summary: "Get all users",
621
- description: "Retrieves a list of all users",
622
- tags: ["users"],
623
- response: {
624
- 200: {
625
- description: "Successful response",
626
- content: {
627
- "application/json": {
628
- schema: {
629
- type: "array",
630
- items: {
631
- type: "object",
632
- properties: {
633
- id: { type: "string" },
634
- name: { type: "string" },
635
- },
636
- },
637
- },
638
- },
639
- },
640
- },
641
- },
642
- });
643
- ```
644
- ### Websocket Intregation (Socket.io)
645
- ```typescript
646
- app.useSocketIO({
647
- cors:{origin:"*"}
648
- })
649
- ```
650
- Now in controller or service use EventDispatcher
651
-
652
-
653
- ```typescript
654
- export class UserService{
655
- constructor(
656
- private readonly dispatcher: EventDispatcher
657
- )
658
-
659
- async create(){
660
- ...rest code
661
-
662
- await this.dispatcher.dispatch("users:notifications",{created:true, userId: newUser.Id})
663
- }
664
-
665
- }
666
- ```
667
-
668
-
669
- ### Testing
670
-
671
- Test your API endpoints with the built-in testing utilities:
672
-
673
- Coming soon...
674
-
675
- ## License
676
-
677
- ISC
678
-
679
- ## Author
680
-
681
- Tareq Hossain - [GitHub](https://github.com/xtareq)
1
+ # Avleon
2
+
3
+ ![npm version](https://img.shields.io/npm/v/@avleon/core.svg)
4
+ ![Build](https://github.com/avleonjs/avleon-core/actions/workflows/release.yml/badge.svg)
5
+ ![License](https://img.shields.io/npm/l/@avleon/core.svg)
6
+
7
+ > **🚧 This project is in active development. APIs may change between versions.**
8
+
9
+ Avleon is a TypeScript-first web framework built on top of [Fastify](https://fastify.dev), designed for building scalable, maintainable REST APIs with minimal boilerplate. It provides decorator-based routing, built-in dependency injection, automatic OpenAPI documentation, and first-class validation support.
10
+
11
+ ---
12
+
13
+ ## Table of Contents
14
+
15
+ - [Features](#features)
16
+ - [Installation](#installation)
17
+ - [Quick Start](#quick-start)
18
+ - [Core Concepts](#core-concepts)
19
+ - [Application](#application)
20
+ - [Controllers](#controllers)
21
+ - [Route Methods](#route-methods)
22
+ - [Parameter Decorators](#parameter-decorators)
23
+ - [Error Handling](#error-handling)
24
+ - [Middleware](#middleware)
25
+ - [Authorization](#authorization)
26
+ - [Validation](#validation)
27
+ - [OpenAPI Documentation](#openapi-documentation)
28
+ - [Advanced Features](#advanced-features)
29
+ - [Database — Knex](#database--knex)
30
+ - [Database — TypeORM](#database--typeorm)
31
+ - [File Uploads](#file-uploads)
32
+ - [Static Files](#static-files)
33
+ - [WebSocket (Socket.IO)](#websocket-socketio)
34
+ - [Route Mapping (Functional Style)](#route-mapping-functional-style)
35
+ - [Testing](#testing)
36
+ - [License](#license)
37
+
38
+ ---
39
+
40
+ ## Features
41
+
42
+ - 🎯 **Decorator-based routing** — define controllers and routes with TypeScript decorators
43
+ - 💉 **Dependency injection** — powered by [TypeDI](https://github.com/typestack/typedi)
44
+ - 📄 **OpenAPI / Swagger** — automatic docs with Swagger UI or [Scalar](https://scalar.com)
45
+ - **Validation** request validation via [class-validator](https://github.com/typestack/class-validator)
46
+ - 🔒 **Authorization** flexible middleware-based auth system
47
+ - 📁 **File uploads** multipart form support out of the box
48
+ - 🗄️ **Database** TypeORM and Knex integrations
49
+ - 🔌 **WebSocket** Socket.IO integration
50
+ - 🧪 **Testing** built-in test utilities
51
+
52
+ ---
53
+
54
+ ## Installation
55
+
56
+ Scaffold a new project using the CLI:
57
+
58
+ ```bash
59
+ npx @avleon/cli new myapp
60
+ # or
61
+ yarn dlx @avleon/cli new myapp
62
+ # or
63
+ pnpm dlx @avleon/cli new myapp
64
+ ```
65
+
66
+ Or install manually:
67
+
68
+ ```bash
69
+ npm install @avleon/core reflect-metadata class-validator class-transformer
70
+ ```
71
+
72
+ ---
73
+
74
+ ## Quick Start
75
+
76
+ ### Minimal (functional style)
77
+
78
+ ```typescript
79
+ import { Avleon } from '@avleon/core';
80
+
81
+ const app = Avleon.createApplication();
82
+
83
+ app.mapGet('/', () => ({ message: 'Hello, Avleon!' }));
84
+
85
+ app.run(4000);
86
+ ```
87
+
88
+ ### Controller style
89
+
90
+ ```typescript
91
+ import { Avleon, ApiController, Get } from '@avleon/core';
92
+
93
+ @ApiController('/')
94
+ class HelloController {
95
+ @Get()
96
+ sayHello() {
97
+ return { message: 'Hello, Avleon!' };
98
+ }
99
+ }
100
+
101
+ const app = Avleon.createApplication();
102
+ app.useControllers([HelloController]);
103
+ app.run(4000);
104
+ ```
105
+
106
+ ---
107
+
108
+ ## Core Concepts
109
+
110
+ ### Application
111
+
112
+ ```typescript
113
+ import { Avleon } from '@avleon/core';
114
+
115
+ const app = Avleon.createApplication();
116
+
117
+ app.useCors({ origin: '*' });
118
+ app.useControllers([UserController]);
119
+ // Auto-discover controllers from a directory:
120
+ // app.useControllers({ auto: true, path: 'src/controllers' });
121
+
122
+ app.run(4000);
123
+ ```
124
+
125
+ ---
126
+
127
+ ### Controllers
128
+
129
+ ```typescript
130
+ import { ApiController, Get, Post, Put, Delete } from '@avleon/core';
131
+
132
+ @ApiController('/users')
133
+ class UserController {
134
+ @Get('/')
135
+ getAll() { ... }
136
+
137
+ @Post('/')
138
+ create() { ... }
139
+
140
+ @Put('/:id')
141
+ update() { ... }
142
+
143
+ @Delete('/:id')
144
+ remove() { ... }
145
+ }
146
+ ```
147
+
148
+ ---
149
+
150
+ ### Route Methods
151
+
152
+ | Decorator | HTTP Method |
153
+ |-----------|-------------|
154
+ | `@Get(path?)` | GET |
155
+ | `@Post(path?)` | POST |
156
+ | `@Put(path?)` | PUT |
157
+ | `@Patch(path?)` | PATCH |
158
+ | `@Delete(path?)` | DELETE |
159
+
160
+ ---
161
+
162
+ ### Parameter Decorators
163
+
164
+ ```typescript
165
+ @Get('/:id')
166
+ async getUser(
167
+ @Param('id') id: string,
168
+ @Query('include') include: string,
169
+ @Query() query: UserQuery, // maps full query to a DTO
170
+ @Body() body: CreateUserDto,
171
+ @Header('authorization') token: string,
172
+ @AuthUser() user: CurrentUser,
173
+ ) {
174
+ // ...
175
+ }
176
+ ```
177
+
178
+ | Decorator | Source |
179
+ |-----------|--------|
180
+ | `@Param(key?)` | Route path params |
181
+ | `@Query(key?)` | Query string |
182
+ | `@Body()` | Request body |
183
+ | `@Header(key?)` | Request headers |
184
+ | `@AuthUser()` | Current authenticated user |
185
+
186
+ ---
187
+
188
+ ### Error Handling
189
+
190
+ ```typescript
191
+ import { HttpExceptions, HttpResponse } from '@avleon/core';
192
+
193
+ @Get('/:id')
194
+ async getUser(@Param('id') id: string) {
195
+ const user = await this.userService.findById(id);
196
+
197
+ if (!user) {
198
+ throw HttpExceptions.NotFound('User not found');
199
+ }
200
+
201
+ return HttpResponse.Ok(user);
202
+ }
203
+ ```
204
+
205
+ Available exceptions: `NotFound`, `BadRequest`, `Unauthorized`, `Forbidden`, `InternalServerError`.
206
+
207
+ ---
208
+
209
+ ### Middleware
210
+
211
+ ```typescript
212
+ import { Middleware, AppMiddleware, IRequest, UseMiddleware } from '@avleon/core';
213
+
214
+ @Middleware
215
+ class LoggingMiddleware extends AppMiddleware {
216
+ async invoke(req: IRequest) {
217
+ console.log(`${req.method} ${req.url}`);
218
+ return req;
219
+ }
220
+ }
221
+
222
+ // Apply to entire controller
223
+ @UseMiddleware(LoggingMiddleware)
224
+ @ApiController('/users')
225
+ class UserController { ... }
226
+
227
+ // Or apply to a specific route
228
+ @ApiController('/users')
229
+ class UserController {
230
+ @UseMiddleware(LoggingMiddleware)
231
+ @Get('/')
232
+ getAll() { ... }
233
+ }
234
+ ```
235
+
236
+ ---
237
+
238
+ ### Authorization
239
+
240
+ **1 — Define your authorization class:**
241
+
242
+ ```typescript
243
+ import { CanAuthorize, AuthorizeMiddleware, IRequest } from '@avleon/core';
244
+
245
+ @CanAuthorize
246
+ class JwtAuthorization extends AuthorizeMiddleware {
247
+ async authorize(req: IRequest, options?: any) {
248
+ const token = req.headers['authorization']?.split(' ')[1];
249
+ if (!token) throw HttpExceptions.Unauthorized('Missing token');
250
+ req.user = verifyToken(token); // attach user to request
251
+ }
252
+ }
253
+ ```
254
+
255
+ **2 — Register with the app:**
256
+
257
+ ```typescript
258
+ app.useAuthorization(JwtAuthorization);
259
+ ```
260
+
261
+ **3 — Protect controllers or routes:**
262
+
263
+ ```typescript
264
+ // Protect entire controller
265
+ @Authorized()
266
+ @ApiController('/admin')
267
+ class AdminController {
268
+ @Get('/')
269
+ dashboard(@AuthUser() user: User) {
270
+ return user;
271
+ }
272
+ }
273
+
274
+ // Protect specific route with roles
275
+ @ApiController('/admin')
276
+ class AdminController {
277
+ @Authorized({ roles: ['admin'] })
278
+ @Get('/stats')
279
+ stats() { ... }
280
+ }
281
+ ```
282
+
283
+ ---
284
+
285
+ ### Validation
286
+
287
+ Validation is powered by `class-validator`. Decorate your DTOs and Avleon validates automatically:
288
+
289
+ ```typescript
290
+ import { IsString, IsEmail, IsInt, Min, Max, IsOptional } from 'class-validator';
291
+
292
+ class CreateUserDto {
293
+ @IsString()
294
+ @IsNotEmpty()
295
+ name: string;
296
+
297
+ @IsEmail()
298
+ email: string;
299
+
300
+ @IsInt()
301
+ @Min(0)
302
+ @Max(120)
303
+ age: number;
304
+
305
+ @IsOptional()
306
+ @IsString()
307
+ role?: string;
308
+ }
309
+
310
+ @Post('/')
311
+ async createUser(@Body() body: CreateUserDto) {
312
+ return this.userService.create(body);
313
+ }
314
+ ```
315
+
316
+ ---
317
+
318
+ ### OpenAPI Documentation
319
+
320
+ **Inline config:**
321
+
322
+ ```typescript
323
+ app.useOpenApi({
324
+ info: {
325
+ title: 'User API',
326
+ version: '1.0.0',
327
+ description: 'API for managing users',
328
+ },
329
+ servers: [{ url: 'http://localhost:4000', description: 'Dev server' }],
330
+ components: {
331
+ securitySchemes: {
332
+ bearerAuth: {
333
+ type: 'http',
334
+ scheme: 'bearer',
335
+ bearerFormat: 'JWT',
336
+ },
337
+ },
338
+ },
339
+ });
340
+ ```
341
+
342
+ **Config class:**
343
+
344
+ ```typescript
345
+ import { AppConfig, IConfig, Environment } from '@avleon/core';
346
+
347
+ @AppConfig
348
+ export class OpenApiConfig implements IConfig {
349
+ config(env: Environment) {
350
+ return {
351
+ info: { title: 'My API', version: '1.0.0' },
352
+ routePrefix: '/docs',
353
+ provider: 'scalar', // or 'default' for Swagger UI
354
+ };
355
+ }
356
+ }
357
+
358
+ // In app.ts
359
+ if (app.isDevelopment()) {
360
+ app.useOpenApi(OpenApiConfig);
361
+ }
362
+ ```
363
+
364
+ **Route-level docs with `@OpenApi`:**
365
+
366
+ ```typescript
367
+ import { OpenApi, OpenApiProperty, OpenApiSchema } from '@avleon/core';
368
+
369
+ @OpenApiSchema()
370
+ export class UserQuery {
371
+ @OpenApiProperty({ type: 'string', example: 'john', required: false })
372
+ @IsOptional()
373
+ search?: string;
374
+
375
+ @OpenApiProperty({ type: 'integer', example: 1, required: false })
376
+ @IsOptional()
377
+ page?: number;
378
+ }
379
+
380
+ @OpenApi({
381
+ summary: 'Get all users',
382
+ tags: ['users'],
383
+ security: [{ bearerAuth: [] }],
384
+ response: {
385
+ 200: {
386
+ description: 'List of users',
387
+ type: 'object',
388
+ properties: {
389
+ data: { type: 'array' },
390
+ total: { type: 'integer', example: 100 },
391
+ },
392
+ },
393
+ 401: { description: 'Unauthorized' },
394
+ },
395
+ })
396
+ @Get('/')
397
+ getAll(@Query() query: UserQuery) { ... }
398
+ ```
399
+
400
+ ---
401
+
402
+ ## Advanced Features
403
+
404
+ ### Database — Knex
405
+
406
+ ```typescript
407
+ app.useKnex({
408
+ client: 'mysql',
409
+ connection: {
410
+ host: '127.0.0.1',
411
+ port: 3306,
412
+ user: 'root',
413
+ password: 'password',
414
+ database: 'myapp',
415
+ },
416
+ });
417
+ ```
418
+
419
+ Using a config class:
420
+
421
+ ```typescript
422
+ @AppConfig
423
+ export class KnexConfig implements IConfig {
424
+ config(env: Environment) {
425
+ return {
426
+ client: 'mysql',
427
+ connection: {
428
+ host: env.get('DB_HOST') || '127.0.0.1',
429
+ port: env.get('DB_PORT') || 3306,
430
+ user: env.get('DB_USER') || 'root',
431
+ password: env.get('DB_PASS') || 'password',
432
+ database: env.get('DB_NAME') || 'myapp',
433
+ },
434
+ };
435
+ }
436
+ }
437
+
438
+ app.useKnex(KnexConfig);
439
+ ```
440
+
441
+ Using in a service:
442
+
443
+ ```typescript
444
+ import { DB, AppService } from '@avleon/core';
445
+
446
+ @AppService
447
+ export class UsersService {
448
+ constructor(private readonly db: DB) {}
449
+
450
+ async findAll() {
451
+ return this.db.client.select('*').from('users');
452
+ }
453
+ }
454
+ ```
455
+
456
+ ---
457
+
458
+ ### Database — TypeORM
459
+
460
+ ```typescript
461
+ app.useDataSource({
462
+ type: 'postgres',
463
+ host: 'localhost',
464
+ port: 5432,
465
+ username: 'postgres',
466
+ password: 'password',
467
+ database: 'avleon',
468
+ entities: [User],
469
+ synchronize: true,
470
+ });
471
+ ```
472
+
473
+ Using a config class:
474
+
475
+ ```typescript
476
+ @AppConfig
477
+ export class DataSourceConfig implements IConfig {
478
+ config(env: Environment) {
479
+ return {
480
+ type: 'postgres',
481
+ host: env.get('DB_HOST') || 'localhost',
482
+ port: Number(env.get('DB_PORT')) || 5432,
483
+ username: env.get('DB_USER') || 'postgres',
484
+ password: env.get('DB_PASS') || 'password',
485
+ database: env.get('DB_NAME') || 'avleon',
486
+ entities: [User],
487
+ synchronize: true,
488
+ };
489
+ }
490
+ }
491
+
492
+ app.useDataSource(DataSourceConfig);
493
+ ```
494
+
495
+ Using in a service:
496
+
497
+ ```typescript
498
+ import { AppService, InjectRepository } from '@avleon/core';
499
+ import { Repository } from 'typeorm';
500
+ import { User } from './user.entity';
501
+
502
+ @AppService
503
+ export class UserService {
504
+ constructor(
505
+ @InjectRepository(User)
506
+ private readonly userRepo: Repository<User>,
507
+ ) {}
508
+
509
+ async findAll() {
510
+ return this.userRepo.find();
511
+ }
512
+ }
513
+ ```
514
+
515
+ ---
516
+
517
+ ### File Uploads
518
+
519
+ ```typescript
520
+ // Configure multipart support
521
+ app.useMultipart({
522
+ destination: path.join(process.cwd(), 'public/uploads'),
523
+ limits: { fileSize: 5 * 1024 * 1024 }, // 5MB
524
+ });
525
+ ```
526
+
527
+ ```typescript
528
+ import { FileStorage, UploadFile, MultipartFile } from '@avleon/core';
529
+
530
+ @ApiController('/files')
531
+ class FileController {
532
+ constructor(private readonly fileStorage: FileStorage) {}
533
+
534
+ @OpenApi({
535
+ description: 'Upload a single file',
536
+ body: {
537
+ type: 'object',
538
+ properties: {
539
+ file: { type: 'string', format: 'binary' },
540
+ },
541
+ required: ['file'],
542
+ },
543
+ })
544
+ @Post('/upload')
545
+ async upload(@UploadFile('file') file: MultipartFile) {
546
+ const result = await this.fileStorage.save(file);
547
+ // optionally rename: this.fileStorage.save(file, { as: 'newname.jpg' })
548
+ return result;
549
+ // { uploadPath: '/uploads/...', staticPath: '/static/...' }
550
+ }
551
+ }
552
+ ```
553
+
554
+ ---
555
+
556
+ ### Static Files
557
+
558
+ ```typescript
559
+ import path from 'path';
560
+
561
+ app.useStaticFiles({
562
+ path: path.join(process.cwd(), 'public'),
563
+ prefix: '/static/',
564
+ });
565
+ ```
566
+
567
+ ---
568
+
569
+ ### WebSocket (Socket.IO)
570
+
571
+ ```typescript
572
+ app.useSocketIo({ cors: { origin: '*' } });
573
+ ```
574
+
575
+ Dispatch events from services:
576
+
577
+ ```typescript
578
+ import { AppService, EventDispatcher } from '@avleon/core';
579
+
580
+ @AppService
581
+ export class UserService {
582
+ constructor(private readonly dispatcher: EventDispatcher) {}
583
+
584
+ async create(data: any) {
585
+ const user = await this.save(data);
586
+ await this.dispatcher.dispatch('users:created', { userId: user.id });
587
+ return user;
588
+ }
589
+ }
590
+ ```
591
+
592
+ ---
593
+
594
+ ## Route Mapping (Functional Style)
595
+
596
+ For simple routes without a controller class:
597
+
598
+ ```typescript
599
+ app.mapGet('/users', async (req, res) => {
600
+ return { users: [] };
601
+ });
602
+
603
+ app.mapPost('/users', async (req, res) => {
604
+ return { success: true };
605
+ });
606
+
607
+ app.mapPut('/users/:id', async (req, res) => {
608
+ return { success: true };
609
+ });
610
+
611
+ app.mapDelete('/users/:id', async (req, res) => {
612
+ return { success: true };
613
+ });
614
+ ```
615
+
616
+ Add middleware and OpenAPI docs to functional routes:
617
+
618
+ ```typescript
619
+ app
620
+ .mapGet('/users', async (req, res) => {
621
+ return { users: [] };
622
+ })
623
+ .useMiddleware([AuthMiddleware])
624
+ .useOpenApi({
625
+ summary: 'Get all users',
626
+ tags: ['users'],
627
+ security: [{ bearerAuth: [] }],
628
+ response: {
629
+ 200: {
630
+ description: 'List of users',
631
+ type: 'array',
632
+ },
633
+ },
634
+ });
635
+ ```
636
+
637
+ ---
638
+
639
+ ## Testing
640
+
641
+ ```typescript
642
+ import { AvleonTest } from '@avleon/core';
643
+ import { UserController } from './user.controller';
644
+
645
+ describe('UserController', () => {
646
+ let controller: UserController;
647
+
648
+ beforeAll(() => {
649
+ controller = AvleonTest.getController(UserController);
650
+ });
651
+
652
+ it('should be defined', () => {
653
+ expect(controller).toBeDefined();
654
+ });
655
+
656
+ it('should return users', async () => {
657
+ const result = await controller.getAll();
658
+ expect(Array.isArray(result)).toBe(true);
659
+ });
660
+ });
661
+ ```
662
+
663
+ ---
664
+
665
+ ## License
666
+
667
+ ISC © [Tareq Hossain](https://github.com/xtareq)