wynkjs 1.0.0 → 1.0.2

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 CHANGED
@@ -2,15 +2,16 @@
2
2
 
3
3
  <div align="center">
4
4
 
5
- **A high-performance TypeScript framework built on Elysia with NestJS-style decorators**
5
+ **A high-performance TypeScript framework built on Elysia for Bun with NestJS-style decorators**
6
6
 
7
7
  [![npm version](https://img.shields.io/npm/v/wynkjs.svg)](https://www.npmjs.com/package/wynkjs)
8
8
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
9
9
  [![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-blue.svg)](https://www.typescriptlang.org/)
10
+ [![Bun](https://img.shields.io/badge/Bun-1.0+-black.svg)](https://bun.sh/)
10
11
 
11
- _20x faster than Express, easier than NestJS_
12
+ _20x faster than Express, easier than NestJS, built for Bun_
12
13
 
13
- [Documentation](#documentation) • [Quick Start](#quick-start) • [Examples](#examples) • [Features](#features)
14
+ [Quick Start](#-quick-start) • [CLI Tools](#️-cli-tools) • [Features](#-features--examples) • [Documentation](#-documentation)
14
15
 
15
16
  </div>
16
17
 
@@ -18,9 +19,23 @@ _20x faster than Express, easier than NestJS_ ⚡
18
19
 
19
20
  ## About
20
21
 
21
- WynkJS is a modern, TypeScript-first web framework that brings NestJS-style decorators to the blazing-fast Elysia runtime. Its a lightweight NestJS alternative with familiar concepts—controllers, dependency injection, guards, pipes, interceptors, and exception filters—designed for building high‑performance REST APIs and backends on Node.js or Bun. WynkJS embraces ESM, ships first-class types, and keeps things simple so you can move fast without the bloat.
22
+ WynkJS is a modern, TypeScript-first web framework that brings NestJS-style decorators to the blazing-fast Elysia runtime built for **Bun**. It's a lightweight NestJS alternative with familiar concepts—controllers, dependency injection, guards, pipes, interceptors, and exception filters—designed for building high‑performance REST APIs and backends on **Bun**. WynkJS embraces ESM, ships first-class types, and keeps things simple so you can move fast without the bloat.
22
23
 
23
- Keywords: NestJS alternative, Elysia framework, TypeScript decorators, dependency injection (DI), guards, pipes, interceptors, exception filters, fast web framework, REST API, backend, Bun, Node.js.
24
+ **🚀 Get Started in 30 Seconds:**
25
+
26
+ ```bash
27
+ bunx create-wynkjs # Create new project
28
+ cd my-wynkjs-app # Navigate to project
29
+ bun run dev # Start server with hot reload
30
+ ```
31
+
32
+ Then generate your first API module:
33
+
34
+ ```bash
35
+ wynkjs g m product # Generate complete CRUD module
36
+ ```
37
+
38
+ Keywords: NestJS alternative, Elysia framework, Bun framework, TypeScript decorators, dependency injection (DI), guards, pipes, interceptors, exception filters, fast web framework, REST API, backend.
24
39
 
25
40
  ---
26
41
 
@@ -28,66 +43,173 @@ Keywords: NestJS alternative, Elysia framework, TypeScript decorators, dependenc
28
43
 
29
44
  WynkJS combines the **speed of Elysia** with the **elegant decorator syntax of NestJS**, giving you the best of both worlds:
30
45
 
31
- - 🚀 **20x Faster** - Built on Elysia, one of the fastest web frameworks
46
+ - 🚀 **20x Faster** - Built on Elysia, one of the fastest web frameworks for Bun
32
47
  - 🎨 **Decorator-Based** - Familiar NestJS-style decorators
33
- - 💉 **Dependency Injection** - Built-in DI with tsyringe
34
- - 🔒 **Type-Safe** - Full TypeScript support
48
+ - 💉 **Dependency Injection** - Built-in DI (no need to import reflect-metadata!)
49
+ - 🔒 **TypeScript First** - TypeScript is mandatory, not optional. Full type safety and IntelliSense support
35
50
  - 🎯 **Simple & Clean** - Easy to learn, powerful to use
36
51
  - 🔌 **Middleware Support** - Guards, interceptors, pipes, filters
37
- - ⚡ **Hot Reload** - Fast development with Bun
52
+ - ⚡ **Bun Only** - Built exclusively for Bun runtime (not Node.js)
53
+ - 📦 **Single Import** - Everything from `wynkjs` (Injectable, Controller, Get, etc.)
38
54
 
39
55
  ---
40
56
 
41
57
  ## 📦 Installation
42
58
 
59
+ **Requirements:** Bun 1.0 or higher
60
+
61
+ ### Quick Start (Recommended)
62
+
63
+ Create a new WynkJS project with all best practices:
64
+
43
65
  ```bash
44
- npm install wynkjs elysia reflect-metadata
66
+ bunx create-wynkjs
67
+ # or
68
+ npx create-wynkjs
45
69
  ```
46
70
 
47
- Or with Bun:
71
+ This will scaffold a complete **TypeScript** project with:
72
+
73
+ - ✅ TypeScript (strict mode, decorators enabled)
74
+ - ✅ ESLint + Prettier (optional)
75
+ - ✅ Hot reload with `bun --watch`
76
+ - ✅ Example code (controllers, services, DTOs)
77
+ - ✅ Git hooks (optional)
78
+
79
+ ### Manual Installation
80
+
81
+ Add to an existing project:
48
82
 
49
83
  ```bash
50
- bun add wynkjs elysia reflect-metadata
84
+ bun add wynkjs elysia
51
85
  ```
52
86
 
87
+ **Note**: WynkJS is built specifically for **Bun**. It leverages Elysia's performance optimizations that are designed for Bun runtime. ✨
88
+
53
89
  ---
54
90
 
55
91
  ## 🚀 Quick Start
56
92
 
57
- ### 1. Create Your First Controller
93
+ ### 1. Create Your DTOs (Data Transfer Objects)
58
94
 
59
95
  ```typescript
60
- import { Controller, Get, Post, Body } from "wynkjs";
61
- import { injectable } from "tsyringe";
96
+ // user.dto.ts
97
+ import { DTO, CommonDTO } from "wynkjs";
98
+
99
+ export const CreateUserDTO = DTO.Strict({
100
+ name: DTO.Optional(DTO.String({ minLength: 2, maxLength: 50 })),
101
+ email: CommonDTO.Email({
102
+ description: "User email address",
103
+ }),
104
+ mobile: DTO.Optional(
105
+ DTO.String({
106
+ pattern: "^[6-9]{1}[0-9]{9}$",
107
+ errorMessage: "Invalid mobile number",
108
+ })
109
+ ),
110
+ age: DTO.Optional(DTO.Number({ minimum: 18 })),
111
+ });
112
+
113
+ export interface CreateUserType {
114
+ name?: string;
115
+ email?: string;
116
+ mobile?: string;
117
+ age?: number;
118
+ }
119
+
120
+ export const UserIdDto = DTO.Object({
121
+ id: DTO.String({ minLength: 2, maxLength: 50 }),
122
+ });
62
123
 
63
- @injectable()
124
+ export interface ParamIdType {
125
+ id: string;
126
+ }
127
+ ```
128
+
129
+ ### 2. Create a Service with Dependency Injection
130
+
131
+ ```typescript
132
+ // email.service.ts
133
+ import { Injectable } from "wynkjs";
134
+
135
+ @Injectable()
136
+ export class EmailService {
137
+ async sendWelcomeEmail(email: string, userName: string): Promise<void> {
138
+ console.log(`📧 Sending welcome email to ${email}`);
139
+ // Your email logic here
140
+ }
141
+ }
142
+ ```
143
+
144
+ ### 3. Create Your Controller
145
+
146
+ ```typescript
147
+ // user.controller.ts
148
+ import {
149
+ Controller,
150
+ Get,
151
+ Post,
152
+ Body,
153
+ Param,
154
+ Patch,
155
+ Query,
156
+ Injectable,
157
+ NotFoundException,
158
+ } from "wynkjs";
159
+ import { CreateUserDTO, UserIdDto } from "./user.dto";
160
+ import type { CreateUserType, ParamIdType } from "./user.dto";
161
+ import { EmailService } from "./email.service";
162
+
163
+ @Injectable()
64
164
  @Controller("/users")
65
165
  export class UserController {
166
+ constructor(private emailService: EmailService) {}
167
+
66
168
  @Get("/")
67
169
  async list() {
68
170
  return { users: ["Alice", "Bob", "Charlie"] };
69
171
  }
70
172
 
71
- @Post("/")
72
- async create(@Body() body: any) {
173
+ @Post({
174
+ path: "/",
175
+ body: CreateUserDTO,
176
+ })
177
+ async create(@Body() body: CreateUserType) {
178
+ // Send welcome email using injected service
179
+ if (body.email && body.name) {
180
+ await this.emailService.sendWelcomeEmail(body.email, body.name);
181
+ }
182
+
73
183
  return { message: "User created", data: body };
74
184
  }
75
185
 
76
- @Get("/:id")
186
+ @Get({ path: "/:id", params: UserIdDto })
77
187
  async findOne(@Param("id") id: string) {
78
188
  return { user: { id, name: "Alice" } };
79
189
  }
190
+
191
+ @Patch({
192
+ path: "/:id",
193
+ params: UserIdDto,
194
+ })
195
+ async update(@Param("id") id: string, @Body() body: any) {
196
+ if (id === "nonexistent") {
197
+ throw new NotFoundException("User not found");
198
+ }
199
+
200
+ return { message: "User updated", id, data: body };
201
+ }
80
202
  }
81
203
  ```
82
204
 
83
- ### 2. Create Your Application
205
+ ### 4. Bootstrap Your Application
84
206
 
85
207
  ```typescript
86
- import "reflect-metadata";
87
- import { WynkFramework } from "wynkjs";
88
- import { UserController } from "./controllers/user.controller";
208
+ // index.ts
209
+ import { WynkFactory } from "wynkjs";
210
+ import { UserController } from "./user.controller";
89
211
 
90
- const app = await WynkFramework.create({
212
+ const app = WynkFactory.create({
91
213
  controllers: [UserController],
92
214
  });
93
215
 
@@ -96,10 +218,32 @@ await app.listen(3000);
96
218
  console.log("🚀 Server running on http://localhost:3000");
97
219
  ```
98
220
 
99
- ### 3. Run Your Server
221
+ ### 5. Run Your Server
100
222
 
101
223
  ```bash
102
224
  bun run index.ts
225
+ # or with --watch for hot reload
226
+ bun --watch index.ts
227
+ ```
228
+
229
+ ### 6. Test Your API
230
+
231
+ ```bash
232
+ # List users
233
+ curl http://localhost:3000/users
234
+
235
+ # Create user (with validation)
236
+ curl -X POST http://localhost:3000/users \
237
+ -H "Content-Type: application/json" \
238
+ -d '{"name":"John","email":"john@example.com","age":25}'
239
+
240
+ # Get user by ID
241
+ curl http://localhost:3000/users/123
242
+
243
+ # Update user
244
+ curl -X PATCH http://localhost:3000/users/123 \
245
+ -H "Content-Type: application/json" \
246
+ -d '{"email":"newemail@example.com"}'
103
247
  ```
104
248
 
105
249
  That's it! 🎉
@@ -151,6 +295,162 @@ That's it! 🎉
151
295
 
152
296
  ---
153
297
 
298
+ ## 🛠️ CLI Tools
299
+
300
+ WynkJS provides powerful CLI tools to speed up your development workflow:
301
+
302
+ ### 📦 create-wynkjs - Project Scaffolding
303
+
304
+ Quickly scaffold a new WynkJS project with best practices:
305
+
306
+ ```bash
307
+ # Create a new project
308
+ bunx create-wynkjs
309
+ # or
310
+ npx create-wynkjs
311
+ ```
312
+
313
+ **What you get:**
314
+
315
+ - ✅ **TypeScript** - Strict mode with decorators enabled (mandatory)
316
+ - ✅ **ESLint** - Code linting with TypeScript rules (optional)
317
+ - ✅ **Prettier** - Code formatting (optional)
318
+ - ✅ **Husky** - Git hooks for pre-commit checks (optional)
319
+ - ✅ **Hot Reload** - `bun --watch` for instant feedback
320
+ - ✅ **Working Example** - Complete CRUD controller, service, and DTOs
321
+
322
+ **Example:**
323
+
324
+ ```bash
325
+ bunx create-wynkjs
326
+ # Choose project name: my-api
327
+ # Add ESLint? Yes
328
+ # Add Prettier? Yes
329
+ # Add Husky? No
330
+
331
+ cd my-api
332
+ bun run dev
333
+ # 🚀 Server running on http://localhost:3000
334
+ ```
335
+
336
+ ### ⚡ wynkjs-cli - Code Generator
337
+
338
+ Generate modules, controllers, services, and DTOs instantly:
339
+
340
+ ```bash
341
+ # Install globally (recommended)
342
+ bun add -g wynkjs-cli
343
+
344
+ # Or install in project
345
+ bun add -D wynkjs-cli
346
+ ```
347
+
348
+ **Commands:**
349
+
350
+ ```bash
351
+ # Generate complete CRUD module (controller + service + DTO)
352
+ wynkjs generate module product
353
+ # or short: wynkjs g m product
354
+
355
+ # Generate controller only (all HTTP methods)
356
+ wynkjs generate controller user
357
+ # or short: wynkjs g c user
358
+
359
+ # Generate service only (all CRUD methods)
360
+ wynkjs generate service order
361
+ # or short: wynkjs g s order
362
+
363
+ # Generate DTO only (Create, Update, ID DTOs)
364
+ wynkjs generate dto payment
365
+ # or short: wynkjs g d payment
366
+ ```
367
+
368
+ **What it generates:**
369
+
370
+ ```bash
371
+ wynkjs g m product
372
+ # Creates:
373
+ # src/modules/product/
374
+ # ├── controllers/
375
+ # │ └── product.controller.ts # Full CRUD controller
376
+ # ├── services/
377
+ # │ └── product.service.ts # All CRUD methods
378
+ # └── dto/
379
+ # └── product.dto.ts # Validation schemas
380
+ ```
381
+
382
+ **Auto-imports:** Controllers are automatically imported and added to `src/index.ts`!
383
+
384
+ **Generated Code Example:**
385
+
386
+ ```typescript
387
+ // product.controller.ts - Ready to use!
388
+ @Injectable()
389
+ @Controller("/product")
390
+ export class ProductController {
391
+ constructor(private productService: ProductService) {}
392
+
393
+ @Get("/")
394
+ async findAll() {
395
+ /* ... */
396
+ }
397
+
398
+ @Post({ path: "/", body: CreateProductDTO })
399
+ async create(@Body() body: CreateProductType) {
400
+ /* ... */
401
+ }
402
+
403
+ @Get({ path: "/:id", params: ProductIdDto })
404
+ async findOne(@Param("id") id: string) {
405
+ /* ... */
406
+ }
407
+
408
+ @Put({ path: "/:id", params: ProductIdDto, body: UpdateProductDTO })
409
+ async update(@Param("id") id: string, @Body() body: UpdateProductType) {
410
+ /* ... */
411
+ }
412
+
413
+ @Delete({ path: "/:id", params: ProductIdDto })
414
+ async remove(@Param("id") id: string) {
415
+ /* ... */
416
+ }
417
+ }
418
+ ```
419
+
420
+ **Full Workflow:**
421
+
422
+ ```bash
423
+ # 1. Create new project
424
+ bunx create-wynkjs
425
+ cd my-api
426
+
427
+ # 2. Generate your first module
428
+ wynkjs g m product
429
+
430
+ # 3. Start developing
431
+ bun run dev
432
+
433
+ # 4. Your API is ready!
434
+ curl http://localhost:3000/product
435
+ # {"data":[]}
436
+ ```
437
+
438
+ **Custom Configuration** (optional):
439
+
440
+ Create `wynkjs.config.json` in your project root:
441
+
442
+ ```json
443
+ {
444
+ "srcDir": "src",
445
+ "controllersDir": "src/controllers",
446
+ "servicesDir": "src/services",
447
+ "dtoDir": "src/dto",
448
+ "modulesDir": "src/modules"
449
+ }
450
+ ```
451
+
452
+ ---
453
+
154
454
  ## 🎯 Features & Examples
155
455
 
156
456
  ### 🔒 Authentication with Guards
@@ -210,47 +510,78 @@ export class AdminController {
210
510
 
211
511
  ### 💉 Dependency Injection
212
512
 
513
+ WynkJS includes powerful dependency injection with **zero setup required**:
514
+
213
515
  ```typescript
214
- import { injectable, inject } from "tsyringe";
516
+ // email.service.ts
517
+ import { Injectable } from "wynkjs";
518
+
519
+ @Injectable()
520
+ export class EmailService {
521
+ async sendWelcomeEmail(email: string, userName: string): Promise<void> {
522
+ console.log(`📧 Sending welcome email to ${email}`);
523
+ // Your email sending logic
524
+ }
215
525
 
216
- @injectable()
217
- export class UserService {
218
- async findAll() {
219
- return [{ id: 1, name: "Alice" }];
526
+ async sendPasswordResetEmail(
527
+ email: string,
528
+ resetToken: string
529
+ ): Promise<void> {
530
+ console.log(`🔐 Sending password reset to ${email}`);
531
+ // Your reset email logic
220
532
  }
221
533
  }
222
534
 
223
- @injectable()
535
+ // user.controller.ts
536
+ import { Controller, Post, Body, Injectable } from "wynkjs";
537
+ import { EmailService } from "./email.service";
538
+
539
+ @Injectable()
224
540
  @Controller("/users")
225
541
  export class UserController {
226
- constructor(@inject(UserService) private userService: UserService) {}
542
+ // EmailService is automatically injected!
543
+ constructor(private emailService: EmailService) {}
227
544
 
228
- @Get("/")
229
- async list() {
230
- const users = await this.userService.findAll();
231
- return { users };
545
+ @Post("/")
546
+ async create(@Body() body: { name: string; email: string }) {
547
+ // Use the injected service
548
+ await this.emailService.sendWelcomeEmail(body.email, body.name);
549
+ return { message: "User created and email sent!" };
550
+ }
551
+
552
+ @Post("/send-reset-email")
553
+ async sendPasswordReset(@Body() body: { email: string }) {
554
+ await this.emailService.sendPasswordResetEmail(
555
+ body.email,
556
+ "reset-token-123"
557
+ );
558
+ return { message: "Password reset email sent" };
232
559
  }
233
560
  }
234
561
  ```
235
562
 
563
+ **Available DI decorators**:
564
+
565
+ - Capital: `Injectable`, `Inject`, `Singleton`, `AutoInjectable`, `Container`
566
+
236
567
  ### 🗃️ Database Integration (Drizzle ORM)
237
568
 
238
569
  ```typescript
239
- import { drizzle } from "drizzle-orm/node-postgres";
240
- import { pgTable, varchar } from "drizzle-orm/pg-core";
570
+ import { drizzle } from "drizzle-orm/bun-sqlite";
571
+ import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core";
241
572
  import { InjectTable, registerTables } from "wynkjs";
242
573
 
243
574
  // Define your table
244
- const userTable = pgTable("users", {
245
- id: varchar("id").primaryKey(),
246
- name: varchar("name").notNull(),
247
- email: varchar("email").notNull().unique(),
575
+ const userTable = sqliteTable("users", {
576
+ id: integer("id").primaryKey(),
577
+ name: text("name").notNull(),
578
+ email: text("email").notNull().unique(),
248
579
  });
249
580
 
250
581
  // Register tables
251
582
  registerTables({ userTable });
252
583
 
253
- @injectable()
584
+ @Injectable()
254
585
  export class UserService {
255
586
  private db = drizzle(process.env.DATABASE_URL);
256
587
 
@@ -264,14 +595,84 @@ export class UserService {
264
595
 
265
596
  ### 📝 Request Validation
266
597
 
598
+ WynkJS provides automatic request validation with **full IntelliSense support** and customizable error formats:
599
+
600
+ > 💡 **New in v1.0.2**: Type `DTO.` and get full autocomplete! See [INTELLISENSE_GUIDE.md](./docs/INTELLISENSE_GUIDE.md)
601
+
602
+ ```typescript
603
+ // user.dto.ts
604
+ import { DTO, CommonDTO } from "wynkjs";
605
+
606
+ // ✨ Full IntelliSense when typing DTO.String(), DTO.Number(), etc!
607
+ export const CreateUserDTO = DTO.Strict({
608
+ name: DTO.Optional(DTO.String({ minLength: 2, maxLength: 50 })),
609
+ email: CommonDTO.Email({
610
+ description: "User email address",
611
+ }),
612
+ mobile: DTO.Optional(
613
+ DTO.String({
614
+ pattern: "^[6-9]{1}[0-9]{9}$",
615
+ errorMessage: "Invalid mobile number",
616
+ })
617
+ ),
618
+ age: DTO.Optional(DTO.Number({ minimum: 18 })),
619
+ });
620
+
621
+ export interface CreateUserType {
622
+ name?: string;
623
+ email?: string;
624
+ mobile?: string;
625
+ age?: number;
626
+ }
627
+
628
+ export const UserUpdateDTO = DTO.Strict({
629
+ email: DTO.Optional(DTO.String({ format: "email", minLength: 5 })),
630
+ age: DTO.Optional(DTO.Number({ minimum: 18 })),
631
+ });
632
+
633
+ export interface UserUpdateType {
634
+ email?: string;
635
+ age?: number;
636
+ }
637
+
638
+ // user.controller.ts
639
+ import { Controller, Post, Patch, Body, Param } from "wynkjs";
640
+ import { CreateUserDTO, UserUpdateDTO } from "./user.dto";
641
+ import type { CreateUserType, UserUpdateType } from "./user.dto";
642
+
643
+ @Controller("/users")
644
+ export class UserController {
645
+ @Post({
646
+ path: "/",
647
+ body: CreateUserDTO, // ✅ Automatic validation
648
+ })
649
+ async create(@Body() body: CreateUserType) {
650
+ // Body is validated automatically!
651
+ // Invalid requests get clear error messages
652
+ return { message: "User created", data: body };
653
+ }
654
+
655
+ @Patch({
656
+ path: "/:id",
657
+ body: UserUpdateDTO, // ✅ Different schema for updates
658
+ })
659
+ async update(@Param("id") id: string, @Body() body: UserUpdateType) {
660
+ return { message: "User updated", id, data: body };
661
+ }
662
+ }
663
+ ```
664
+
665
+ **Customize validation error format:**
666
+
267
667
  ```typescript
268
- import { Post, Body, UsePipes } from "wynkjs";
269
- import { t } from "elysia";
668
+ import { WynkFactory, FormatErrorFormatter } from "wynkjs";
270
669
 
271
- const CreateUserDTO = t.Object({
272
- name: t.String({ minLength: 2 }),
273
- email: t.String({ format: "email" }),
274
- password: t.String({ minLength: 8 }),
670
+ const app = WynkFactory.create({
671
+ controllers: [UserController],
672
+ // Choose your validation error format
673
+ validationErrorFormatter: new FormatErrorFormatter(), // NestJS-style (recommended)
674
+ // validationErrorFormatter: new SimpleErrorFormatter(), // Simple array
675
+ // validationErrorFormatter: new DetailedErrorFormatter(), // Detailed with paths
275
676
  });
276
677
 
277
678
  @Controller("/users")
@@ -285,6 +686,82 @@ export class UserController {
285
686
  return { message: "User created", data: body };
286
687
  }
287
688
  }
689
+
690
+ // Choose your validation error format
691
+ const app = WynkFactory.create({
692
+ controllers: [UserController],
693
+ // Option 1: Default format (recommended)
694
+ // validationErrorFormatter: new FormatErrorFormatter(), // NestJS-style
695
+ // Option 2: Simple array format
696
+ // validationErrorFormatter: new SimpleErrorFormatter(),
697
+ // Option 3: Detailed format with field info
698
+ // validationErrorFormatter: new DetailedErrorFormatter(),
699
+ });
700
+ ```
701
+
702
+ **See [VALIDATION_FORMATTERS.md](./VALIDATION_FORMATTERS.md) for all available error formats**
703
+
704
+ ### 🚫 Exception Handling
705
+
706
+ ```typescript
707
+ import { Controller, Get, Param, NotFoundException } from "wynkjs";
708
+
709
+ @Controller("/users")
710
+ export class UserController {
711
+ @Get("/:id")
712
+ async findOne(@Param("id") id: string) {
713
+ // Built-in exceptions
714
+ if (id === "nonexistent") {
715
+ throw new NotFoundException("User not found");
716
+ }
717
+
718
+ return { user: { id, name: "Alice" } };
719
+ }
720
+ }
721
+ ```
722
+
723
+ **Built-in exceptions:**
724
+
725
+ - `BadRequestException` - 400
726
+ - `UnauthorizedException` - 401
727
+ - `ForbiddenException` - 403
728
+ - `NotFoundException` - 404
729
+ - `InternalServerErrorException` - 500
730
+
731
+ ### 🔄 Multiple Params and Query Validation
732
+
733
+ ```typescript
734
+ // user.dto.ts
735
+ export const MultiParamDto = DTO.Object({
736
+ id1: DTO.String({ minLength: 2, maxLength: 50 }),
737
+ id2: DTO.String({ minLength: 2, maxLength: 50 }),
738
+ });
739
+
740
+ export const UserQueryDto = DTO.Strict({
741
+ includePosts: DTO.Optional(DTO.Boolean({ default: false })),
742
+ includeComments: DTO.Optional(DTO.Boolean({ default: false })),
743
+ });
744
+
745
+ // user.controller.ts
746
+ @Post({
747
+ path: "/:id1/:id2",
748
+ body: CreateUserDTO,
749
+ params: MultiParamDto, // ✅ Validates route params
750
+ query: UserQueryDto, // ✅ Validates query params
751
+ })
752
+ async create(
753
+ @Body() body: CreateUserType,
754
+ @Param("id1") id1: string,
755
+ @Param("id2") id2: string,
756
+ @Query() query: UserQueryType
757
+ ) {
758
+ return {
759
+ message: "User created",
760
+ data: body,
761
+ params: { id1, id2 },
762
+ query
763
+ };
764
+ }
288
765
  ```
289
766
 
290
767
  ### 🔄 Multiple Middleware
@@ -319,16 +796,13 @@ export class ApiController {
319
796
  my-wynk-app/
320
797
  ├── src/
321
798
  │ ├── controllers/
322
- │ │ ├── user.controller.ts
323
- │ │ └── auth.controller.ts
799
+ │ │ └── user.controller.ts
324
800
  │ ├── services/
325
- │ │ ├── user.service.ts
326
- │ │ └── auth.service.ts
327
- │ ├── middleware/
328
- │ │ ├── jwt.guard.ts
329
- │ │ └── roles.guard.ts
801
+ │ │ └── email.service.ts
330
802
  │ ├── dto/
331
803
  │ │ └── user.dto.ts
804
+ │ ├── exceptions/
805
+ │ │ └── email.exceptions.ts
332
806
  │ └── index.ts
333
807
  ├── package.json
334
808
  └── tsconfig.json
@@ -336,113 +810,285 @@ my-wynk-app/
336
810
 
337
811
  ---
338
812
 
339
- ## 🎨 Complete Example
813
+ ## 🎨 Complete Working Example
814
+
815
+ Here's a complete, production-ready example with all features:
340
816
 
341
817
  ```typescript
342
- // index.ts
343
- import "reflect-metadata";
344
- import { WynkFramework } from "wynkjs";
345
- import { UserController } from "./controllers/user.controller";
346
- import { AuthController } from "./controllers/auth.controller";
818
+ // dto/user.dto.ts
819
+ import { DTO, CommonDTO } from "wynkjs";
820
+
821
+ export const CreateUserDTO = DTO.Strict({
822
+ name: DTO.Optional(DTO.String({ minLength: 2, maxLength: 50 })),
823
+ email: CommonDTO.Email({ description: "User email address" }),
824
+ mobile: DTO.Optional(
825
+ DTO.String({
826
+ pattern: "^[6-9]{1}[0-9]{9}$",
827
+ errorMessage: "Invalid mobile number",
828
+ })
829
+ ),
830
+ age: DTO.Optional(DTO.Number({ minimum: 18 })),
831
+ });
347
832
 
348
- const app = await WynkFramework.create({
349
- controllers: [UserController, AuthController],
350
- globalGuards: [],
351
- globalInterceptors: [],
833
+ export const UserIdDto = DTO.Object({
834
+ id: DTO.String({ minLength: 2, maxLength: 50 }),
352
835
  });
353
836
 
354
- await app.listen(3000);
355
- console.log("🚀 Server running on http://localhost:3000");
356
- ```
837
+ export interface CreateUserType {
838
+ name?: string;
839
+ email: string;
840
+ mobile?: string;
841
+ age?: number;
842
+ }
357
843
 
358
- ```typescript
359
- // controllers/user.controller.ts
360
- import { Controller, Get, Post, Put, Delete, Body, Param, Use } from "wynkjs";
361
- import { injectable, inject } from "tsyringe";
362
- import { UserService } from "../services/user.service";
363
- import { jwtGuard } from "../middleware/jwt.guard";
844
+ // services/email.service.ts
845
+ import { Injectable } from "wynkjs";
364
846
 
365
- @injectable()
847
+ @Injectable()
848
+ export class EmailService {
849
+ async sendWelcomeEmail(email: string, userName: string): Promise<void> {
850
+ console.log(`📧 Sending welcome email to ${email}`);
851
+ // Your email sending logic (SendGrid, AWS SES, etc.)
852
+ }
853
+ }
854
+
855
+ // controllers/user.controller.ts
856
+ import {
857
+ Controller,
858
+ Get,
859
+ Post,
860
+ Patch,
861
+ Body,
862
+ Param,
863
+ Injectable,
864
+ NotFoundException,
865
+ } from "wynkjs";
866
+ import { CreateUserDTO, UserIdDto } from "../dto/user.dto";
867
+ import type { CreateUserType } from "../dto/user.dto";
868
+ import { EmailService } from "../services/email.service";
869
+
870
+ @Injectable()
366
871
  @Controller("/users")
367
- @Use(jwtGuard)
368
872
  export class UserController {
369
- constructor(@inject(UserService) private userService: UserService) {}
873
+ constructor(private emailService: EmailService) {}
370
874
 
371
875
  @Get("/")
372
876
  async list() {
373
- const users = await this.userService.findAll();
374
- return { users };
877
+ return { users: ["Alice", "Bob", "Charlie"] };
375
878
  }
376
879
 
377
- @Get("/:id")
378
- async findOne(@Param("id") id: string) {
379
- const user = await this.userService.findById(id);
380
- return { user };
880
+ @Post({ path: "/", body: CreateUserDTO })
881
+ async create(@Body() body: CreateUserType) {
882
+ // Send welcome email using injected service
883
+ if (body.email && body.name) {
884
+ await this.emailService.sendWelcomeEmail(body.email, body.name);
885
+ }
886
+ return { message: "User created", data: body };
381
887
  }
382
888
 
383
- @Post("/")
384
- async create(@Body() body: any) {
385
- const user = await this.userService.create(body);
386
- return { message: "User created", user };
889
+ @Get({ path: "/:id", params: UserIdDto })
890
+ async findOne(@Param("id") id: string) {
891
+ if (id === "nonexistent") {
892
+ throw new NotFoundException("User not found");
893
+ }
894
+ return { user: { id, name: "Alice" } };
387
895
  }
388
896
 
389
- @Put("/:id")
897
+ @Patch({ path: "/:id", params: UserIdDto })
390
898
  async update(@Param("id") id: string, @Body() body: any) {
391
- const user = await this.userService.update(id, body);
392
- return { message: "User updated", user };
899
+ return { message: "User updated", id, data: body };
393
900
  }
901
+ }
394
902
 
395
- @Delete("/:id")
396
- @HttpCode(204)
397
- async delete(@Param("id") id: string) {
398
- await this.userService.delete(id);
399
- }
903
+ // index.ts
904
+ import { WynkFactory } from "wynkjs";
905
+ import { UserController } from "./controllers/user.controller";
906
+
907
+ const app = WynkFactory.create({
908
+ controllers: [UserController],
909
+ });
910
+
911
+ await app.listen(3000);
912
+ console.log("🚀 Server running on http://localhost:3000");
913
+ ```
914
+
915
+ **Test the API:**
916
+
917
+ ```bash
918
+ # List all users
919
+ curl http://localhost:3000/users
920
+
921
+ # Create a new user (with validation and email)
922
+ curl -X POST http://localhost:3000/users \
923
+ -H "Content-Type: application/json" \
924
+ -d '{"name":"Alice","email":"alice@example.com","age":25}'
925
+
926
+ # Get user by ID
927
+ curl http://localhost:3000/users/123
928
+
929
+ # Update user
930
+ curl -X PATCH http://localhost:3000/users/123 \
931
+ -H "Content-Type: application/json" \
932
+ -d '{"email":"newemail@example.com"}'
933
+
934
+ # Test 404 exception
935
+ curl http://localhost:3000/users/nonexistent
936
+ ```
937
+
938
+ ---
939
+
940
+ ## 📖 API Reference
941
+
942
+ ### Core Decorators
943
+
944
+ #### `@Controller(basePath?: string)`
945
+
946
+ Define a controller class with optional base path.
947
+
948
+ ```typescript
949
+ @Controller("/users")
950
+ export class UserController {
951
+ // All routes will be prefixed with /users
400
952
  }
401
953
  ```
402
954
 
955
+ #### `@Get(path?: string)` / `@Post()` / `@Patch()` / `@Delete()`
956
+
957
+ HTTP method decorators with optional path and options.
958
+
403
959
  ```typescript
404
- // services/user.service.ts
405
- import { injectable } from "tsyringe";
960
+ @Get("/") // GET /users
961
+ @Post({ path: "/", body: CreateUserDTO }) // POST with validation
962
+ @Patch({ path: "/:id", params: UserIdDto }) // PATCH with param validation
963
+ ```
406
964
 
407
- @injectable()
408
- export class UserService {
409
- private users = [
410
- { id: "1", name: "Alice", email: "alice@example.com" },
411
- { id: "2", name: "Bob", email: "bob@example.com" },
412
- ];
965
+ #### `@Body()` / `@Param(key?)` / `@Query(key?)`
413
966
 
414
- async findAll() {
415
- return this.users;
416
- }
967
+ Parameter extraction decorators.
417
968
 
418
- async findById(id: string) {
419
- return this.users.find((u) => u.id === id);
420
- }
969
+ ```typescript
970
+ async create(
971
+ @Body() body: CreateUserType, // Full body
972
+ @Param("id") id: string, // Single param
973
+ @Query() query: UserQueryType // Full query object
974
+ ) {}
975
+ ```
421
976
 
422
- async create(data: any) {
423
- const user = { id: Date.now().toString(), ...data };
424
- this.users.push(user);
425
- return user;
426
- }
977
+ #### `@Injectable()`
427
978
 
428
- async update(id: string, data: any) {
429
- const index = this.users.findIndex((u) => u.id === id);
430
- if (index !== -1) {
431
- this.users[index] = { ...this.users[index], ...data };
432
- return this.users[index];
433
- }
434
- return null;
435
- }
979
+ Mark a class as injectable for dependency injection.
436
980
 
437
- async delete(id: string) {
438
- const index = this.users.findIndex((u) => u.id === id);
439
- if (index !== -1) {
440
- this.users.splice(index, 1);
441
- }
442
- }
981
+ ```typescript
982
+ @Injectable()
983
+ export class EmailService {
984
+ // This service can be injected into controllers
443
985
  }
444
986
  ```
445
987
 
988
+ ### DTO Helpers
989
+
990
+ #### `DTO.Strict(properties, options?)`
991
+
992
+ Create object schema that strips additional properties.
993
+
994
+ ```typescript
995
+ const UserDTO = DTO.Strict({
996
+ name: DTO.String(),
997
+ email: DTO.String({ format: "email" }),
998
+ });
999
+ ```
1000
+
1001
+ #### `CommonDTO` Patterns
1002
+
1003
+ Pre-built validation patterns for common use cases.
1004
+
1005
+ ```typescript
1006
+ CommonDTO.Email(); // Email validation
1007
+ CommonDTO.Name(); // Name (2-50 chars)
1008
+ CommonDTO.Password(); // Password (min 6 chars)
1009
+ CommonDTO.UUID(); // UUID format
1010
+ CommonDTO.PhoneIN(); // Indian phone number
1011
+ ```
1012
+
1013
+ ### Exception Classes
1014
+
1015
+ ```typescript
1016
+ throw new NotFoundException("User not found"); // 404
1017
+ throw new BadRequestException("Invalid input"); // 400
1018
+ throw new UnauthorizedException("Not authenticated"); // 401
1019
+ throw new ForbiddenException("Access denied"); // 403
1020
+ throw new InternalServerErrorException("Error"); // 500
1021
+ ```
1022
+
1023
+ ---
1024
+
1025
+ ## �️ CLI Tool
1026
+
1027
+ ### create-wynkjs
1028
+
1029
+ Quickly scaffold a new WynkJS project with best practices:
1030
+
1031
+ ```bash
1032
+ bunx create-wynkjs
1033
+ # or
1034
+ npx create-wynkjs
1035
+ ```
1036
+
1037
+ **Features:**
1038
+
1039
+ - 🎯 Interactive project setup
1040
+ - ✅ TypeScript configuration (strict mode)
1041
+ - 🔍 ESLint with TypeScript rules
1042
+ - 💅 Prettier for code formatting
1043
+ - 🪝 Husky for Git hooks (optional)
1044
+ - 🔥 Hot reload with `bun --watch` (faster than nodemon)
1045
+ - 📝 Complete working example (CRUD API)
1046
+
1047
+ **Generated Project Structure:**
1048
+
1049
+ ```
1050
+ my-wynkjs-app/
1051
+ ├── src/
1052
+ │ ├── controllers/
1053
+ │ │ └── user.controller.ts
1054
+ │ ├── services/
1055
+ │ │ └── user.service.ts
1056
+ │ ├── dto/
1057
+ │ │ └── user.dto.ts
1058
+ │ └── index.ts
1059
+ ├── .eslintrc.json
1060
+ ├── .prettierrc
1061
+ ├── tsconfig.json
1062
+ └── package.json
1063
+ ```
1064
+
1065
+ **Available Scripts:**
1066
+
1067
+ - `bun run dev` - Development with hot reload
1068
+ - `bun run start` - Production server
1069
+ - `bun run build` - Build TypeScript
1070
+ - `bun run lint` - Run ESLint
1071
+ - `bun run format` - Format with Prettier
1072
+
1073
+ [Learn more about create-wynkjs](./packages/create-wynkjs/README.md)
1074
+
1075
+ ---
1076
+
1077
+ ## �🔗 Resources
1078
+
1079
+ - 📚 [Full Documentation](https://github.com/wynkjs/wynkjs-core)
1080
+ - 🚀 [CLI Tool (create-wynkjs)](./packages/create-wynkjs/README.md)
1081
+ - 💡 [IntelliSense Guide](./docs/INTELLISENSE_GUIDE.md)
1082
+ - 🎨 [Validation Formatters](./VALIDATION_FORMATTERS.md)
1083
+ - 📝 [Changelog](./CHANGELOG.md)
1084
+ - 🐛 [Report Issues](https://github.com/wynkjs/wynkjs-core/issues)
1085
+
1086
+ ---
1087
+
1088
+ ## 🤝 Contributing
1089
+
1090
+ ```
1091
+
446
1092
  ---
447
1093
 
448
1094
  ## 🔧 API Reference
@@ -520,3 +1166,4 @@ Built with:
520
1166
  Made with ❤️ by [Alam Jamal](https://github.com/alamjamal)
521
1167
 
522
1168
  </div>
1169
+ ```