azurajs 1.0.2 → 1.0.4
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/LICENSE +673 -673
- package/README.md +501 -501
- package/package.json +1 -1
- package/src/decorators/Route.ts +141 -141
- package/src/decorators/index.ts +24 -24
- package/src/index.ts +27 -27
- package/src/infra/Router.ts +56 -56
- package/src/infra/Server.ts +297 -274
- package/src/infra/utils/GetIp.ts +15 -15
- package/src/infra/utils/GetOpenPort.ts +15 -15
- package/src/infra/utils/HttpError.ts +9 -9
- package/src/infra/utils/RequestHandler.ts +33 -33
- package/src/infra/utils/route/Node.ts +15 -15
- package/src/middleware/LoggingMiddleware.ts +108 -108
- package/src/middleware/index.ts +14 -14
- package/src/shared/config/ConfigModule.ts +115 -113
- package/src/shared/plugins/CORSPlugin.ts +28 -0
- package/src/shared/plugins/RateLimitPlugin.ts +32 -0
- package/src/types/common.type.ts +9 -4
- package/src/types/http/request.type.ts +20 -20
- package/src/types/http/response.type.ts +32 -32
- package/src/types/plugins/cors.type.ts +5 -0
- package/src/types/routes.type.ts +23 -23
- package/src/types/validations.type.ts +1 -1
- package/src/utils/Logger.ts +23 -5
- package/src/utils/Parser.ts +19 -19
- package/src/utils/cookies/ParserCookie.ts +9 -9
- package/src/utils/cookies/SerializeCookie.ts +15 -15
- package/src/utils/validators/DTOValidator.ts +30 -30
- package/src/utils/validators/SchemaValidator.ts +37 -37
package/README.md
CHANGED
|
@@ -1,501 +1,501 @@
|
|
|
1
|
-
# AzuraJS
|
|
2
|
-
|
|
3
|
-
⚡ Modern, fast, and TypeScript-first web framework for Node.js and Bun with decorator-based routing.
|
|
4
|
-
|
|
5
|
-
[](https://www.npmjs.com/package/azurajs)
|
|
6
|
-
[](https://github.com/0xviny/azurajs/blob/main/LICENSE)
|
|
7
|
-
[](https://www.typescriptlang.org/)
|
|
8
|
-
|
|
9
|
-
## Features
|
|
10
|
-
|
|
11
|
-
✨ **Decorator-based routing** - Express-style syntax with TypeScript decorators
|
|
12
|
-
🚀 **High performance** - Built for speed with minimal overhead
|
|
13
|
-
📦 **Zero dependencies** - Lightweight and efficient
|
|
14
|
-
🔧 **TypeScript first** - Full type safety out of the box
|
|
15
|
-
🎯 **Parameter injection** - `@Body`, `@Query`, `@Param`, `@Req`, `@Res`, etc.
|
|
16
|
-
🔌 **Middleware support** - Express-compatible middleware system
|
|
17
|
-
⚙️ **Configurable** - File-based configuration (TS, JSON, YAML)
|
|
18
|
-
🍪 **Cookie handling** - Built-in cookie parser and serializer
|
|
19
|
-
🌐 **Cluster mode** - Multi-core support built-in
|
|
20
|
-
📝 **Smart logging** - Environment-aware request/response logging
|
|
21
|
-
|
|
22
|
-
## Installation
|
|
23
|
-
|
|
24
|
-
```bash
|
|
25
|
-
npm install azurajs
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
or with Bun:
|
|
29
|
-
|
|
30
|
-
```bash
|
|
31
|
-
bun add azurajs
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
## Quick Start
|
|
35
|
-
|
|
36
|
-
### 1. Create `azura.config.ts`
|
|
37
|
-
|
|
38
|
-
```typescript
|
|
39
|
-
import type { ConfigTypes } from "azurajs";
|
|
40
|
-
|
|
41
|
-
const config: ConfigTypes = {
|
|
42
|
-
environment: "development",
|
|
43
|
-
server: {
|
|
44
|
-
port: 3000,
|
|
45
|
-
cluster: false,
|
|
46
|
-
ipHost: true,
|
|
47
|
-
https: false,
|
|
48
|
-
},
|
|
49
|
-
logging: {
|
|
50
|
-
enabled: true,
|
|
51
|
-
showDetails: true,
|
|
52
|
-
},
|
|
53
|
-
plugins: {
|
|
54
|
-
cors: {
|
|
55
|
-
enabled: true,
|
|
56
|
-
origins: ["*"],
|
|
57
|
-
},
|
|
58
|
-
rateLimit: {
|
|
59
|
-
enabled: false,
|
|
60
|
-
limit: 100,
|
|
61
|
-
timeframe: 60000,
|
|
62
|
-
},
|
|
63
|
-
},
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
export default config;
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
### 2. Create your server
|
|
70
|
-
|
|
71
|
-
```typescript
|
|
72
|
-
import {
|
|
73
|
-
AzuraClient,
|
|
74
|
-
Controller,
|
|
75
|
-
Get,
|
|
76
|
-
Post,
|
|
77
|
-
Body,
|
|
78
|
-
Param,
|
|
79
|
-
Query,
|
|
80
|
-
Res,
|
|
81
|
-
applyDecorators,
|
|
82
|
-
createLoggingMiddleware
|
|
83
|
-
} from "azurajs";
|
|
84
|
-
import type { ResponseServer } from "azurajs";
|
|
85
|
-
|
|
86
|
-
@Controller("/api")
|
|
87
|
-
class UserController {
|
|
88
|
-
@Get("/users")
|
|
89
|
-
getAllUsers(@Res() res: ResponseServer) {
|
|
90
|
-
res.json({
|
|
91
|
-
users: [
|
|
92
|
-
{ id: 1, name: "John" },
|
|
93
|
-
{ id: 2, name: "Jane" }
|
|
94
|
-
]
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
@Get("/users/:id")
|
|
99
|
-
getUser(@Param("id") id: string, @Res() res: ResponseServer) {
|
|
100
|
-
res.json({ id: Number(id), name: `User ${id}` });
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
@Post("/users")
|
|
104
|
-
createUser(@Body() body: any, @Res() res: ResponseServer) {
|
|
105
|
-
res.status(201).json({
|
|
106
|
-
id: Date.now(),
|
|
107
|
-
...body
|
|
108
|
-
});
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
const app = new AzuraClient();
|
|
113
|
-
const logger = createLoggingMiddleware(app.getConfig());
|
|
114
|
-
app.use(logger);
|
|
115
|
-
applyDecorators(app, [UserController]);
|
|
116
|
-
await app.listen();
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
### 3. Run your server
|
|
120
|
-
|
|
121
|
-
```bash
|
|
122
|
-
bun run index.ts
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
## API Reference
|
|
126
|
-
|
|
127
|
-
### Decorators
|
|
128
|
-
|
|
129
|
-
#### Class Decorators
|
|
130
|
-
|
|
131
|
-
**`@Controller(prefix?: string)`**
|
|
132
|
-
|
|
133
|
-
Define a controller with optional route prefix.
|
|
134
|
-
|
|
135
|
-
```typescript
|
|
136
|
-
@Controller("/api/v1")
|
|
137
|
-
class MyController {
|
|
138
|
-
|
|
139
|
-
}
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
#### Method Decorators
|
|
143
|
-
|
|
144
|
-
**HTTP Methods:**
|
|
145
|
-
- `@Get(path?: string)`
|
|
146
|
-
- `@Post(path?: string)`
|
|
147
|
-
- `@Put(path?: string)`
|
|
148
|
-
- `@Delete(path?: string)`
|
|
149
|
-
- `@Patch(path?: string)`
|
|
150
|
-
- `@Head(path?: string)`
|
|
151
|
-
- `@Options(path?: string)`
|
|
152
|
-
|
|
153
|
-
```typescript
|
|
154
|
-
@Get("/users")
|
|
155
|
-
getUsers() { }
|
|
156
|
-
|
|
157
|
-
@Post("/users/:id")
|
|
158
|
-
updateUser() { }
|
|
159
|
-
```
|
|
160
|
-
|
|
161
|
-
#### Parameter Decorators
|
|
162
|
-
|
|
163
|
-
**`@Req()`** - Inject request object
|
|
164
|
-
|
|
165
|
-
```typescript
|
|
166
|
-
@Get("/info")
|
|
167
|
-
getInfo(@Req() req: RequestServer) {
|
|
168
|
-
console.log(req.method, req.url);
|
|
169
|
-
}
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
**`@Res()`** - Inject response object
|
|
173
|
-
|
|
174
|
-
```typescript
|
|
175
|
-
@Get("/data")
|
|
176
|
-
getData(@Res() res: ResponseServer) {
|
|
177
|
-
res.json({ data: "value" });
|
|
178
|
-
}
|
|
179
|
-
```
|
|
180
|
-
|
|
181
|
-
**`@Body()`** - Inject request body
|
|
182
|
-
|
|
183
|
-
```typescript
|
|
184
|
-
@Post("/users")
|
|
185
|
-
createUser(@Body() body: any) {
|
|
186
|
-
console.log(body);
|
|
187
|
-
}
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
**`@Query(key?: string)`** - Inject query parameters
|
|
191
|
-
|
|
192
|
-
```typescript
|
|
193
|
-
@Get("/search")
|
|
194
|
-
search(@Query("q") query: string) {
|
|
195
|
-
console.log(query);
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
@Get("/filter")
|
|
199
|
-
filter(@Query() allParams: Record<string, string>) {
|
|
200
|
-
console.log(allParams);
|
|
201
|
-
}
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
**`@Param(key: string)`** - Inject route parameters
|
|
205
|
-
|
|
206
|
-
```typescript
|
|
207
|
-
@Get("/users/:id")
|
|
208
|
-
getUser(@Param("id") id: string) {
|
|
209
|
-
console.log(id);
|
|
210
|
-
}
|
|
211
|
-
```
|
|
212
|
-
|
|
213
|
-
**`@Headers(key?: string)`** - Inject headers
|
|
214
|
-
|
|
215
|
-
```typescript
|
|
216
|
-
@Get("/info")
|
|
217
|
-
getInfo(@Headers("user-agent") ua: string) {
|
|
218
|
-
console.log(ua);
|
|
219
|
-
}
|
|
220
|
-
```
|
|
221
|
-
|
|
222
|
-
**`@Ip()`** - Inject client IP address
|
|
223
|
-
|
|
224
|
-
```typescript
|
|
225
|
-
@Get("/visitor")
|
|
226
|
-
trackVisitor(@Ip() ip: string) {
|
|
227
|
-
console.log(`Visitor from ${ip}`);
|
|
228
|
-
}
|
|
229
|
-
```
|
|
230
|
-
|
|
231
|
-
### Response Methods
|
|
232
|
-
|
|
233
|
-
```typescript
|
|
234
|
-
res.status(code: number)
|
|
235
|
-
res.json(data: any)
|
|
236
|
-
res.send(data: any)
|
|
237
|
-
res.redirect(url: string)
|
|
238
|
-
res.redirect(status: number, url: string)
|
|
239
|
-
res.cookie(name: string, value: string, options?: CookieOptions)
|
|
240
|
-
res.clearCookie(name: string, options?: CookieOptions)
|
|
241
|
-
res.set(field: string, value: string | number | string[])
|
|
242
|
-
res.get(field: string)
|
|
243
|
-
res.type(contentType: string)
|
|
244
|
-
res.location(url: string)
|
|
245
|
-
```
|
|
246
|
-
|
|
247
|
-
### Middleware
|
|
248
|
-
|
|
249
|
-
```typescript
|
|
250
|
-
import { createLoggingMiddleware } from "azurajs";
|
|
251
|
-
|
|
252
|
-
const app = new AzuraClient();
|
|
253
|
-
|
|
254
|
-
const logger = createLoggingMiddleware(app.getConfig());
|
|
255
|
-
app.use(logger);
|
|
256
|
-
|
|
257
|
-
app.use((req, res, next) => {
|
|
258
|
-
console.log(`${req.method} ${req.url}`);
|
|
259
|
-
next();
|
|
260
|
-
});
|
|
261
|
-
```
|
|
262
|
-
|
|
263
|
-
### Functional Routes
|
|
264
|
-
|
|
265
|
-
```typescript
|
|
266
|
-
app.get("/hello", (req, res) => {
|
|
267
|
-
res.json({ message: "Hello World" });
|
|
268
|
-
});
|
|
269
|
-
|
|
270
|
-
app.post("/data", (req, res) => {
|
|
271
|
-
res.json({ received: req.body });
|
|
272
|
-
});
|
|
273
|
-
```
|
|
274
|
-
|
|
275
|
-
## Configuration
|
|
276
|
-
|
|
277
|
-
The framework looks for configuration files in this order:
|
|
278
|
-
1. `azura.config.ts`
|
|
279
|
-
2. `azura.config.json`
|
|
280
|
-
3. `azura.config.yaml`
|
|
281
|
-
4. `azura.config.yml`
|
|
282
|
-
|
|
283
|
-
### Configuration Options
|
|
284
|
-
|
|
285
|
-
```typescript
|
|
286
|
-
type ConfigTypes = {
|
|
287
|
-
environment?: "development" | "production";
|
|
288
|
-
server?: {
|
|
289
|
-
port?: number;
|
|
290
|
-
cluster?: boolean;
|
|
291
|
-
ipHost?: boolean;
|
|
292
|
-
https?: boolean;
|
|
293
|
-
};
|
|
294
|
-
logging?: {
|
|
295
|
-
enabled?: boolean;
|
|
296
|
-
showDetails?: boolean;
|
|
297
|
-
};
|
|
298
|
-
plugins?: {
|
|
299
|
-
cors?: {
|
|
300
|
-
enabled: boolean;
|
|
301
|
-
origins: string[];
|
|
302
|
-
};
|
|
303
|
-
rateLimit?: {
|
|
304
|
-
enabled: boolean;
|
|
305
|
-
limit: number;
|
|
306
|
-
timeframe: number;
|
|
307
|
-
};
|
|
308
|
-
};
|
|
309
|
-
};
|
|
310
|
-
```
|
|
311
|
-
|
|
312
|
-
## Examples
|
|
313
|
-
|
|
314
|
-
### Complete CRUD API
|
|
315
|
-
|
|
316
|
-
```typescript
|
|
317
|
-
import { AzuraClient, Controller, Get, Post, Put, Delete, Body, Param, Res, applyDecorators } from "azurajs";
|
|
318
|
-
import type { ResponseServer } from "azurajs";
|
|
319
|
-
|
|
320
|
-
interface User {
|
|
321
|
-
id: number;
|
|
322
|
-
name: string;
|
|
323
|
-
email: string;
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
const users: User[] = [];
|
|
327
|
-
|
|
328
|
-
@Controller("/api/users")
|
|
329
|
-
class UserController {
|
|
330
|
-
@Get("/")
|
|
331
|
-
list(@Res() res: ResponseServer) {
|
|
332
|
-
res.json(users);
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
@Get("/:id")
|
|
336
|
-
get(@Param("id") id: string, @Res() res: ResponseServer) {
|
|
337
|
-
const user = users.find(u => u.id === Number(id));
|
|
338
|
-
if (!user) return res.status(404).json({ error: "User not found" });
|
|
339
|
-
res.json(user);
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
@Post("/")
|
|
343
|
-
create(@Body() body: Omit<User, "id">, @Res() res: ResponseServer) {
|
|
344
|
-
const user = { id: Date.now(), ...body };
|
|
345
|
-
users.push(user);
|
|
346
|
-
res.status(201).json(user);
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
@Put("/:id")
|
|
350
|
-
update(@Param("id") id: string, @Body() body: Partial<User>, @Res() res: ResponseServer) {
|
|
351
|
-
const index = users.findIndex(u => u.id === Number(id));
|
|
352
|
-
if (index === -1) return res.status(404).json({ error: "User not found" });
|
|
353
|
-
users[index] = { ...users[index], ...body };
|
|
354
|
-
res.json(users[index]);
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
@Delete("/:id")
|
|
358
|
-
delete(@Param("id") id: string, @Res() res: ResponseServer) {
|
|
359
|
-
const index = users.findIndex(u => u.id === Number(id));
|
|
360
|
-
if (index === -1) return res.status(404).json({ error: "User not found" });
|
|
361
|
-
users.splice(index, 1);
|
|
362
|
-
res.status(204).send();
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
const app = new AzuraClient();
|
|
367
|
-
applyDecorators(app, [UserController]);
|
|
368
|
-
await app.listen();
|
|
369
|
-
```
|
|
370
|
-
|
|
371
|
-
### Authentication Example
|
|
372
|
-
|
|
373
|
-
```typescript
|
|
374
|
-
@Controller("/auth")
|
|
375
|
-
class AuthController {
|
|
376
|
-
@Post("/login")
|
|
377
|
-
login(@Body() body: any, @Res() res: ResponseServer) {
|
|
378
|
-
const token = generateToken(body.username, body.password);
|
|
379
|
-
res.cookie("auth_token", token, {
|
|
380
|
-
httpOnly: true,
|
|
381
|
-
maxAge: 3600000,
|
|
382
|
-
secure: true,
|
|
383
|
-
});
|
|
384
|
-
res.json({ success: true });
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
@Post("/logout")
|
|
388
|
-
logout(@Res() res: ResponseServer) {
|
|
389
|
-
res.clearCookie("auth_token");
|
|
390
|
-
res.json({ success: true });
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
@Get("/profile")
|
|
394
|
-
profile(@Req() req: RequestServer, @Res() res: ResponseServer) {
|
|
395
|
-
const token = req.cookies.auth_token;
|
|
396
|
-
if (!token) return res.status(401).json({ error: "Unauthorized" });
|
|
397
|
-
const user = verifyToken(token);
|
|
398
|
-
res.json(user);
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
```
|
|
402
|
-
|
|
403
|
-
### File Upload Example
|
|
404
|
-
|
|
405
|
-
```typescript
|
|
406
|
-
@Controller("/upload")
|
|
407
|
-
class UploadController {
|
|
408
|
-
@Post("/")
|
|
409
|
-
async upload(@Req() req: RequestServer, @Res() res: ResponseServer) {
|
|
410
|
-
const contentType = req.get("content-type") || "";
|
|
411
|
-
|
|
412
|
-
if (contentType.includes("multipart/form-data")) {
|
|
413
|
-
res.json({ message: "File uploaded successfully" });
|
|
414
|
-
} else {
|
|
415
|
-
res.status(400).json({ error: "Invalid content type" });
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
```
|
|
420
|
-
|
|
421
|
-
## Production Mode
|
|
422
|
-
|
|
423
|
-
```bash
|
|
424
|
-
NODE_ENV=production bun run index.ts
|
|
425
|
-
```
|
|
426
|
-
|
|
427
|
-
Or in your config:
|
|
428
|
-
|
|
429
|
-
```typescript
|
|
430
|
-
const config: ConfigTypes = {
|
|
431
|
-
environment: process.env.NODE_ENV === "production" ? "production" : "development",
|
|
432
|
-
logging: {
|
|
433
|
-
enabled: true,
|
|
434
|
-
showDetails: process.env.NODE_ENV !== "production",
|
|
435
|
-
},
|
|
436
|
-
};
|
|
437
|
-
```
|
|
438
|
-
|
|
439
|
-
## Cluster Mode
|
|
440
|
-
|
|
441
|
-
Enable cluster mode for multi-core systems:
|
|
442
|
-
|
|
443
|
-
```typescript
|
|
444
|
-
const config: ConfigTypes = {
|
|
445
|
-
server: {
|
|
446
|
-
cluster: true,
|
|
447
|
-
},
|
|
448
|
-
};
|
|
449
|
-
```
|
|
450
|
-
|
|
451
|
-
## Performance
|
|
452
|
-
|
|
453
|
-
AzuraJS is designed for high performance:
|
|
454
|
-
- Zero runtime dependencies
|
|
455
|
-
- Optimized routing with radix tree
|
|
456
|
-
- Minimal overhead middleware system
|
|
457
|
-
- Native Node.js http module
|
|
458
|
-
|
|
459
|
-
## TypeScript Support
|
|
460
|
-
|
|
461
|
-
Full TypeScript support with complete type definitions:
|
|
462
|
-
|
|
463
|
-
```typescript
|
|
464
|
-
import type {
|
|
465
|
-
RequestServer,
|
|
466
|
-
ResponseServer,
|
|
467
|
-
ConfigTypes,
|
|
468
|
-
RequestHandler
|
|
469
|
-
} from "azurajs";
|
|
470
|
-
```
|
|
471
|
-
|
|
472
|
-
> ⚠️ Azura is TypeScript-only.
|
|
473
|
-
|
|
474
|
-
> This package ships uncompiled TypeScript source code.
|
|
475
|
-
> You must use a runtime or build tool that supports TypeScript:
|
|
476
|
-
> - Bun
|
|
477
|
-
> - ts-node
|
|
478
|
-
> - tsx
|
|
479
|
-
> - Deno
|
|
480
|
-
> - Vite
|
|
481
|
-
|
|
482
|
-
## Contributing
|
|
483
|
-
|
|
484
|
-
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
485
|
-
|
|
486
|
-
## License
|
|
487
|
-
|
|
488
|
-
MIT License - see LICENSE file for details
|
|
489
|
-
|
|
490
|
-
## Links
|
|
491
|
-
|
|
492
|
-
- [GitHub Repository](https://github.com/0xviny/azurajs)
|
|
493
|
-
- [NPM Package](https://www.npmjs.com/package/azurajs)
|
|
494
|
-
- [Documentation](https://azurajs.dev)
|
|
495
|
-
- [Examples](https://github.com/0xviny/azurajs/tree/main/examples)
|
|
496
|
-
|
|
497
|
-
## Support
|
|
498
|
-
|
|
499
|
-
- 🐛 [Issue Tracker](https://github.com/0xviny/azurajs/issues)
|
|
500
|
-
- 💬 [Discussions](https://github.com/0xviny/azurajs/discussions)
|
|
501
|
-
- 📧 Email: 0xviny.dev@gmail.com
|
|
1
|
+
# AzuraJS
|
|
2
|
+
|
|
3
|
+
⚡ Modern, fast, and TypeScript-first web framework for Node.js and Bun with decorator-based routing.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/azurajs)
|
|
6
|
+
[](https://github.com/0xviny/azurajs/blob/main/LICENSE)
|
|
7
|
+
[](https://www.typescriptlang.org/)
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
✨ **Decorator-based routing** - Express-style syntax with TypeScript decorators
|
|
12
|
+
🚀 **High performance** - Built for speed with minimal overhead
|
|
13
|
+
📦 **Zero dependencies** - Lightweight and efficient
|
|
14
|
+
🔧 **TypeScript first** - Full type safety out of the box
|
|
15
|
+
🎯 **Parameter injection** - `@Body`, `@Query`, `@Param`, `@Req`, `@Res`, etc.
|
|
16
|
+
🔌 **Middleware support** - Express-compatible middleware system
|
|
17
|
+
⚙️ **Configurable** - File-based configuration (TS, JSON, YAML)
|
|
18
|
+
🍪 **Cookie handling** - Built-in cookie parser and serializer
|
|
19
|
+
🌐 **Cluster mode** - Multi-core support built-in
|
|
20
|
+
📝 **Smart logging** - Environment-aware request/response logging
|
|
21
|
+
|
|
22
|
+
## Installation
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install azurajs
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
or with Bun:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
bun add azurajs
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Quick Start
|
|
35
|
+
|
|
36
|
+
### 1. Create `azura.config.ts`
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
import type { ConfigTypes } from "azurajs";
|
|
40
|
+
|
|
41
|
+
const config: ConfigTypes = {
|
|
42
|
+
environment: "development",
|
|
43
|
+
server: {
|
|
44
|
+
port: 3000,
|
|
45
|
+
cluster: false,
|
|
46
|
+
ipHost: true,
|
|
47
|
+
https: false,
|
|
48
|
+
},
|
|
49
|
+
logging: {
|
|
50
|
+
enabled: true,
|
|
51
|
+
showDetails: true,
|
|
52
|
+
},
|
|
53
|
+
plugins: {
|
|
54
|
+
cors: {
|
|
55
|
+
enabled: true,
|
|
56
|
+
origins: ["*"],
|
|
57
|
+
},
|
|
58
|
+
rateLimit: {
|
|
59
|
+
enabled: false,
|
|
60
|
+
limit: 100,
|
|
61
|
+
timeframe: 60000,
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export default config;
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### 2. Create your server
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
import {
|
|
73
|
+
AzuraClient,
|
|
74
|
+
Controller,
|
|
75
|
+
Get,
|
|
76
|
+
Post,
|
|
77
|
+
Body,
|
|
78
|
+
Param,
|
|
79
|
+
Query,
|
|
80
|
+
Res,
|
|
81
|
+
applyDecorators,
|
|
82
|
+
createLoggingMiddleware
|
|
83
|
+
} from "azurajs";
|
|
84
|
+
import type { ResponseServer } from "azurajs";
|
|
85
|
+
|
|
86
|
+
@Controller("/api")
|
|
87
|
+
class UserController {
|
|
88
|
+
@Get("/users")
|
|
89
|
+
getAllUsers(@Res() res: ResponseServer) {
|
|
90
|
+
res.json({
|
|
91
|
+
users: [
|
|
92
|
+
{ id: 1, name: "John" },
|
|
93
|
+
{ id: 2, name: "Jane" }
|
|
94
|
+
]
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
@Get("/users/:id")
|
|
99
|
+
getUser(@Param("id") id: string, @Res() res: ResponseServer) {
|
|
100
|
+
res.json({ id: Number(id), name: `User ${id}` });
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
@Post("/users")
|
|
104
|
+
createUser(@Body() body: any, @Res() res: ResponseServer) {
|
|
105
|
+
res.status(201).json({
|
|
106
|
+
id: Date.now(),
|
|
107
|
+
...body
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const app = new AzuraClient();
|
|
113
|
+
const logger = createLoggingMiddleware(app.getConfig());
|
|
114
|
+
app.use(logger);
|
|
115
|
+
applyDecorators(app, [UserController]);
|
|
116
|
+
await app.listen();
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### 3. Run your server
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
bun run index.ts
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## API Reference
|
|
126
|
+
|
|
127
|
+
### Decorators
|
|
128
|
+
|
|
129
|
+
#### Class Decorators
|
|
130
|
+
|
|
131
|
+
**`@Controller(prefix?: string)`**
|
|
132
|
+
|
|
133
|
+
Define a controller with optional route prefix.
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
@Controller("/api/v1")
|
|
137
|
+
class MyController {
|
|
138
|
+
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
#### Method Decorators
|
|
143
|
+
|
|
144
|
+
**HTTP Methods:**
|
|
145
|
+
- `@Get(path?: string)`
|
|
146
|
+
- `@Post(path?: string)`
|
|
147
|
+
- `@Put(path?: string)`
|
|
148
|
+
- `@Delete(path?: string)`
|
|
149
|
+
- `@Patch(path?: string)`
|
|
150
|
+
- `@Head(path?: string)`
|
|
151
|
+
- `@Options(path?: string)`
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
@Get("/users")
|
|
155
|
+
getUsers() { }
|
|
156
|
+
|
|
157
|
+
@Post("/users/:id")
|
|
158
|
+
updateUser() { }
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
#### Parameter Decorators
|
|
162
|
+
|
|
163
|
+
**`@Req()`** - Inject request object
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
@Get("/info")
|
|
167
|
+
getInfo(@Req() req: RequestServer) {
|
|
168
|
+
console.log(req.method, req.url);
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
**`@Res()`** - Inject response object
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
@Get("/data")
|
|
176
|
+
getData(@Res() res: ResponseServer) {
|
|
177
|
+
res.json({ data: "value" });
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
**`@Body()`** - Inject request body
|
|
182
|
+
|
|
183
|
+
```typescript
|
|
184
|
+
@Post("/users")
|
|
185
|
+
createUser(@Body() body: any) {
|
|
186
|
+
console.log(body);
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
**`@Query(key?: string)`** - Inject query parameters
|
|
191
|
+
|
|
192
|
+
```typescript
|
|
193
|
+
@Get("/search")
|
|
194
|
+
search(@Query("q") query: string) {
|
|
195
|
+
console.log(query);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
@Get("/filter")
|
|
199
|
+
filter(@Query() allParams: Record<string, string>) {
|
|
200
|
+
console.log(allParams);
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
**`@Param(key: string)`** - Inject route parameters
|
|
205
|
+
|
|
206
|
+
```typescript
|
|
207
|
+
@Get("/users/:id")
|
|
208
|
+
getUser(@Param("id") id: string) {
|
|
209
|
+
console.log(id);
|
|
210
|
+
}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
**`@Headers(key?: string)`** - Inject headers
|
|
214
|
+
|
|
215
|
+
```typescript
|
|
216
|
+
@Get("/info")
|
|
217
|
+
getInfo(@Headers("user-agent") ua: string) {
|
|
218
|
+
console.log(ua);
|
|
219
|
+
}
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
**`@Ip()`** - Inject client IP address
|
|
223
|
+
|
|
224
|
+
```typescript
|
|
225
|
+
@Get("/visitor")
|
|
226
|
+
trackVisitor(@Ip() ip: string) {
|
|
227
|
+
console.log(`Visitor from ${ip}`);
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### Response Methods
|
|
232
|
+
|
|
233
|
+
```typescript
|
|
234
|
+
res.status(code: number)
|
|
235
|
+
res.json(data: any)
|
|
236
|
+
res.send(data: any)
|
|
237
|
+
res.redirect(url: string)
|
|
238
|
+
res.redirect(status: number, url: string)
|
|
239
|
+
res.cookie(name: string, value: string, options?: CookieOptions)
|
|
240
|
+
res.clearCookie(name: string, options?: CookieOptions)
|
|
241
|
+
res.set(field: string, value: string | number | string[])
|
|
242
|
+
res.get(field: string)
|
|
243
|
+
res.type(contentType: string)
|
|
244
|
+
res.location(url: string)
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### Middleware
|
|
248
|
+
|
|
249
|
+
```typescript
|
|
250
|
+
import { createLoggingMiddleware } from "azurajs";
|
|
251
|
+
|
|
252
|
+
const app = new AzuraClient();
|
|
253
|
+
|
|
254
|
+
const logger = createLoggingMiddleware(app.getConfig());
|
|
255
|
+
app.use(logger);
|
|
256
|
+
|
|
257
|
+
app.use((req, res, next) => {
|
|
258
|
+
console.log(`${req.method} ${req.url}`);
|
|
259
|
+
next();
|
|
260
|
+
});
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### Functional Routes
|
|
264
|
+
|
|
265
|
+
```typescript
|
|
266
|
+
app.get("/hello", (req, res) => {
|
|
267
|
+
res.json({ message: "Hello World" });
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
app.post("/data", (req, res) => {
|
|
271
|
+
res.json({ received: req.body });
|
|
272
|
+
});
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
## Configuration
|
|
276
|
+
|
|
277
|
+
The framework looks for configuration files in this order:
|
|
278
|
+
1. `azura.config.ts`
|
|
279
|
+
2. `azura.config.json`
|
|
280
|
+
3. `azura.config.yaml`
|
|
281
|
+
4. `azura.config.yml`
|
|
282
|
+
|
|
283
|
+
### Configuration Options
|
|
284
|
+
|
|
285
|
+
```typescript
|
|
286
|
+
type ConfigTypes = {
|
|
287
|
+
environment?: "development" | "production";
|
|
288
|
+
server?: {
|
|
289
|
+
port?: number;
|
|
290
|
+
cluster?: boolean;
|
|
291
|
+
ipHost?: boolean;
|
|
292
|
+
https?: boolean;
|
|
293
|
+
};
|
|
294
|
+
logging?: {
|
|
295
|
+
enabled?: boolean;
|
|
296
|
+
showDetails?: boolean;
|
|
297
|
+
};
|
|
298
|
+
plugins?: {
|
|
299
|
+
cors?: {
|
|
300
|
+
enabled: boolean;
|
|
301
|
+
origins: string[];
|
|
302
|
+
};
|
|
303
|
+
rateLimit?: {
|
|
304
|
+
enabled: boolean;
|
|
305
|
+
limit: number;
|
|
306
|
+
timeframe: number;
|
|
307
|
+
};
|
|
308
|
+
};
|
|
309
|
+
};
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
## Examples
|
|
313
|
+
|
|
314
|
+
### Complete CRUD API
|
|
315
|
+
|
|
316
|
+
```typescript
|
|
317
|
+
import { AzuraClient, Controller, Get, Post, Put, Delete, Body, Param, Res, applyDecorators } from "azurajs";
|
|
318
|
+
import type { ResponseServer } from "azurajs";
|
|
319
|
+
|
|
320
|
+
interface User {
|
|
321
|
+
id: number;
|
|
322
|
+
name: string;
|
|
323
|
+
email: string;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
const users: User[] = [];
|
|
327
|
+
|
|
328
|
+
@Controller("/api/users")
|
|
329
|
+
class UserController {
|
|
330
|
+
@Get("/")
|
|
331
|
+
list(@Res() res: ResponseServer) {
|
|
332
|
+
res.json(users);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
@Get("/:id")
|
|
336
|
+
get(@Param("id") id: string, @Res() res: ResponseServer) {
|
|
337
|
+
const user = users.find(u => u.id === Number(id));
|
|
338
|
+
if (!user) return res.status(404).json({ error: "User not found" });
|
|
339
|
+
res.json(user);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
@Post("/")
|
|
343
|
+
create(@Body() body: Omit<User, "id">, @Res() res: ResponseServer) {
|
|
344
|
+
const user = { id: Date.now(), ...body };
|
|
345
|
+
users.push(user);
|
|
346
|
+
res.status(201).json(user);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
@Put("/:id")
|
|
350
|
+
update(@Param("id") id: string, @Body() body: Partial<User>, @Res() res: ResponseServer) {
|
|
351
|
+
const index = users.findIndex(u => u.id === Number(id));
|
|
352
|
+
if (index === -1) return res.status(404).json({ error: "User not found" });
|
|
353
|
+
users[index] = { ...users[index], ...body };
|
|
354
|
+
res.json(users[index]);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
@Delete("/:id")
|
|
358
|
+
delete(@Param("id") id: string, @Res() res: ResponseServer) {
|
|
359
|
+
const index = users.findIndex(u => u.id === Number(id));
|
|
360
|
+
if (index === -1) return res.status(404).json({ error: "User not found" });
|
|
361
|
+
users.splice(index, 1);
|
|
362
|
+
res.status(204).send();
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
const app = new AzuraClient();
|
|
367
|
+
applyDecorators(app, [UserController]);
|
|
368
|
+
await app.listen();
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
### Authentication Example
|
|
372
|
+
|
|
373
|
+
```typescript
|
|
374
|
+
@Controller("/auth")
|
|
375
|
+
class AuthController {
|
|
376
|
+
@Post("/login")
|
|
377
|
+
login(@Body() body: any, @Res() res: ResponseServer) {
|
|
378
|
+
const token = generateToken(body.username, body.password);
|
|
379
|
+
res.cookie("auth_token", token, {
|
|
380
|
+
httpOnly: true,
|
|
381
|
+
maxAge: 3600000,
|
|
382
|
+
secure: true,
|
|
383
|
+
});
|
|
384
|
+
res.json({ success: true });
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
@Post("/logout")
|
|
388
|
+
logout(@Res() res: ResponseServer) {
|
|
389
|
+
res.clearCookie("auth_token");
|
|
390
|
+
res.json({ success: true });
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
@Get("/profile")
|
|
394
|
+
profile(@Req() req: RequestServer, @Res() res: ResponseServer) {
|
|
395
|
+
const token = req.cookies.auth_token;
|
|
396
|
+
if (!token) return res.status(401).json({ error: "Unauthorized" });
|
|
397
|
+
const user = verifyToken(token);
|
|
398
|
+
res.json(user);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
### File Upload Example
|
|
404
|
+
|
|
405
|
+
```typescript
|
|
406
|
+
@Controller("/upload")
|
|
407
|
+
class UploadController {
|
|
408
|
+
@Post("/")
|
|
409
|
+
async upload(@Req() req: RequestServer, @Res() res: ResponseServer) {
|
|
410
|
+
const contentType = req.get("content-type") || "";
|
|
411
|
+
|
|
412
|
+
if (contentType.includes("multipart/form-data")) {
|
|
413
|
+
res.json({ message: "File uploaded successfully" });
|
|
414
|
+
} else {
|
|
415
|
+
res.status(400).json({ error: "Invalid content type" });
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
## Production Mode
|
|
422
|
+
|
|
423
|
+
```bash
|
|
424
|
+
NODE_ENV=production bun run index.ts
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
Or in your config:
|
|
428
|
+
|
|
429
|
+
```typescript
|
|
430
|
+
const config: ConfigTypes = {
|
|
431
|
+
environment: process.env.NODE_ENV === "production" ? "production" : "development",
|
|
432
|
+
logging: {
|
|
433
|
+
enabled: true,
|
|
434
|
+
showDetails: process.env.NODE_ENV !== "production",
|
|
435
|
+
},
|
|
436
|
+
};
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
## Cluster Mode
|
|
440
|
+
|
|
441
|
+
Enable cluster mode for multi-core systems:
|
|
442
|
+
|
|
443
|
+
```typescript
|
|
444
|
+
const config: ConfigTypes = {
|
|
445
|
+
server: {
|
|
446
|
+
cluster: true,
|
|
447
|
+
},
|
|
448
|
+
};
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
## Performance
|
|
452
|
+
|
|
453
|
+
AzuraJS is designed for high performance:
|
|
454
|
+
- Zero runtime dependencies
|
|
455
|
+
- Optimized routing with radix tree
|
|
456
|
+
- Minimal overhead middleware system
|
|
457
|
+
- Native Node.js http module
|
|
458
|
+
|
|
459
|
+
## TypeScript Support
|
|
460
|
+
|
|
461
|
+
Full TypeScript support with complete type definitions:
|
|
462
|
+
|
|
463
|
+
```typescript
|
|
464
|
+
import type {
|
|
465
|
+
RequestServer,
|
|
466
|
+
ResponseServer,
|
|
467
|
+
ConfigTypes,
|
|
468
|
+
RequestHandler
|
|
469
|
+
} from "azurajs";
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
> ⚠️ Azura is TypeScript-only.
|
|
473
|
+
|
|
474
|
+
> This package ships uncompiled TypeScript source code.
|
|
475
|
+
> You must use a runtime or build tool that supports TypeScript:
|
|
476
|
+
> - Bun
|
|
477
|
+
> - ts-node
|
|
478
|
+
> - tsx
|
|
479
|
+
> - Deno
|
|
480
|
+
> - Vite
|
|
481
|
+
|
|
482
|
+
## Contributing
|
|
483
|
+
|
|
484
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
485
|
+
|
|
486
|
+
## License
|
|
487
|
+
|
|
488
|
+
MIT License - see LICENSE file for details
|
|
489
|
+
|
|
490
|
+
## Links
|
|
491
|
+
|
|
492
|
+
- [GitHub Repository](https://github.com/0xviny/azurajs)
|
|
493
|
+
- [NPM Package](https://www.npmjs.com/package/azurajs)
|
|
494
|
+
- [Documentation](https://azurajs.dev)
|
|
495
|
+
- [Examples](https://github.com/0xviny/azurajs/tree/main/examples)
|
|
496
|
+
|
|
497
|
+
## Support
|
|
498
|
+
|
|
499
|
+
- 🐛 [Issue Tracker](https://github.com/0xviny/azurajs/issues)
|
|
500
|
+
- 💬 [Discussions](https://github.com/0xviny/azurajs/discussions)
|
|
501
|
+
- 📧 Email: 0xviny.dev@gmail.com
|