bootifyjs 0.1.0 → 0.1.1
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 +326 -76
- package/package.json +1 -1
package/README.md
CHANGED
@@ -2,6 +2,16 @@
|
|
2
2
|
|
3
3
|
A Spring Boot inspired Node.js framework with TypeScript, Dependency Injection, and Validation.
|
4
4
|
|
5
|
+
## Features
|
6
|
+
|
7
|
+
- **Dependency Injection**: Automatic constructor injection with `@Injectable`, `@Service`, and `@Repository` decorators
|
8
|
+
- **REST API**: Easy controller definition with `@Controller`, `@Get`, `@Post`, etc.
|
9
|
+
- **Validation**: Request validation with Zod schemas
|
10
|
+
- **Configuration**: Environment-based configuration with automatic mapping
|
11
|
+
- **OpenAPI**: Automatic OpenAPI documentation generation
|
12
|
+
- **Logging**: Comprehensive logging system with context tracking
|
13
|
+
- **Event System**: Type-safe event system with middleware support
|
14
|
+
|
5
15
|
## Installation
|
6
16
|
|
7
17
|
```bash
|
@@ -44,130 +54,370 @@ async function main() {
|
|
44
54
|
main().catch(console.error);
|
45
55
|
```
|
46
56
|
|
47
|
-
##
|
57
|
+
## Core Concepts
|
58
|
+
|
59
|
+
### Controllers
|
60
|
+
|
61
|
+
Controllers handle HTTP requests and define your API endpoints.
|
48
62
|
|
49
|
-
|
63
|
+
```typescript
|
64
|
+
import { Controller, Get, Post, Put, Delete, Param, Body, Query } from 'bootifyjs';
|
50
65
|
|
51
|
-
|
66
|
+
@Controller('/users')
|
67
|
+
export class UserController {
|
68
|
+
constructor(private userService: UserService) {}
|
69
|
+
|
70
|
+
@Get('/')
|
71
|
+
getAllUsers(@Query('limit') limit?: string) {
|
72
|
+
return this.userService.getAllUsers(limit ? parseInt(limit) : undefined);
|
73
|
+
}
|
74
|
+
|
75
|
+
@Get('/:id')
|
76
|
+
getUserById(@Param('id') id: string) {
|
77
|
+
return this.userService.getUserById(id);
|
78
|
+
}
|
79
|
+
|
80
|
+
@Post('/')
|
81
|
+
createUser(@Body() userData: CreateUserDto) {
|
82
|
+
return this.userService.createUser(userData);
|
83
|
+
}
|
84
|
+
|
85
|
+
@Put('/:id')
|
86
|
+
updateUser(@Param('id') id: string, @Body() userData: UpdateUserDto) {
|
87
|
+
return this.userService.updateUser(id, userData);
|
88
|
+
}
|
89
|
+
|
90
|
+
@Delete('/:id')
|
91
|
+
deleteUser(@Param('id') id: string) {
|
92
|
+
return this.userService.deleteUser(id);
|
93
|
+
}
|
94
|
+
}
|
95
|
+
```
|
52
96
|
|
53
|
-
|
54
|
-
2. **Automatic Mapping**: Environment variables with `BOOTIFY_` prefix are automatically mapped to nested properties
|
55
|
-
3. **Type Conversion**: Values are automatically converted to numbers and booleans when possible
|
97
|
+
### Services
|
56
98
|
|
57
|
-
|
99
|
+
Services contain your business logic and can be injected into controllers or other services.
|
58
100
|
|
59
|
-
**1. Define Your Configuration Structure:**
|
60
101
|
```typescript
|
61
|
-
import {
|
102
|
+
import { Service } from 'bootifyjs';
|
62
103
|
|
63
|
-
@
|
104
|
+
@Service()
|
105
|
+
export class UserService {
|
106
|
+
constructor(private userRepository: UserRepository) {}
|
107
|
+
|
108
|
+
getAllUsers(limit?: number) {
|
109
|
+
return this.userRepository.findAll(limit);
|
110
|
+
}
|
111
|
+
|
112
|
+
getUserById(id: string) {
|
113
|
+
return this.userRepository.findById(id);
|
114
|
+
}
|
115
|
+
|
116
|
+
// More methods...
|
117
|
+
}
|
118
|
+
```
|
119
|
+
|
120
|
+
### Repositories
|
121
|
+
|
122
|
+
Repositories handle data access and can be injected into services.
|
123
|
+
|
124
|
+
```typescript
|
125
|
+
import { Repository } from 'bootifyjs';
|
126
|
+
|
127
|
+
@Repository()
|
128
|
+
export class UserRepository {
|
129
|
+
private users = []; // In a real app, this would be a database connection
|
130
|
+
|
131
|
+
findAll(limit?: number) {
|
132
|
+
if (limit) {
|
133
|
+
return this.users.slice(0, limit);
|
134
|
+
}
|
135
|
+
return this.users;
|
136
|
+
}
|
137
|
+
|
138
|
+
findById(id: string) {
|
139
|
+
return this.users.find(user => user.id === id);
|
140
|
+
}
|
141
|
+
|
142
|
+
// More methods...
|
143
|
+
}
|
144
|
+
```
|
145
|
+
|
146
|
+
### Validation
|
147
|
+
|
148
|
+
Use Zod schemas to validate request data.
|
149
|
+
|
150
|
+
```typescript
|
151
|
+
import { z } from 'zod';
|
152
|
+
import { Controller, Post, Body, ValidateBody } from 'bootifyjs';
|
153
|
+
|
154
|
+
const createUserSchema = z.object({
|
155
|
+
email: z.string().email(),
|
156
|
+
name: z.string().min(2).max(100),
|
157
|
+
age: z.number().min(18).optional()
|
158
|
+
});
|
159
|
+
|
160
|
+
@Controller('/users')
|
161
|
+
export class UserController {
|
162
|
+
@Post('/')
|
163
|
+
@ValidateBody(createUserSchema)
|
164
|
+
createUser(@Body() userData: z.infer<typeof createUserSchema>) {
|
165
|
+
// userData is now validated and typed
|
166
|
+
return this.userService.createUser(userData);
|
167
|
+
}
|
168
|
+
}
|
169
|
+
```
|
170
|
+
|
171
|
+
### Configuration
|
172
|
+
|
173
|
+
Define your configuration structure and automatically map environment variables.
|
174
|
+
|
175
|
+
```typescript
|
176
|
+
import { Config, getConfigInstance } from 'bootifyjs';
|
177
|
+
|
178
|
+
@Config('APP')
|
64
179
|
export class AppConfig {
|
65
|
-
SERVICE_NAME: string = '
|
180
|
+
SERVICE_NAME: string = 'my-service';
|
66
181
|
|
67
182
|
server: {
|
68
183
|
port: number;
|
69
184
|
host: string;
|
70
|
-
name?: string;
|
71
185
|
} = {
|
72
186
|
port: 3000,
|
73
187
|
host: 'localhost'
|
74
188
|
};
|
75
189
|
|
76
190
|
database: {
|
77
|
-
|
78
|
-
|
79
|
-
name: string;
|
191
|
+
url: string;
|
192
|
+
poolSize: number;
|
80
193
|
} = {
|
81
|
-
|
82
|
-
|
83
|
-
name: 'myapp'
|
194
|
+
url: 'postgres://localhost:5432/mydb',
|
195
|
+
poolSize: 10
|
84
196
|
};
|
85
197
|
}
|
198
|
+
|
199
|
+
// Environment variables like APP_SERVER_PORT=8080 will be automatically mapped
|
200
|
+
const config = getConfigInstance(AppConfig);
|
201
|
+
console.log(config.server.port); // 8080
|
86
202
|
```
|
87
203
|
|
88
|
-
|
89
|
-
```bash
|
90
|
-
# Maps to appConfig.SERVICE_NAME
|
91
|
-
BOOTIFY_SERVICE_NAME=my-awesome-app
|
204
|
+
### Event System
|
92
205
|
|
93
|
-
|
94
|
-
BOOTIFY_SERVER_PORT=8080
|
206
|
+
Create a type-safe event system for decoupled communication.
|
95
207
|
|
96
|
-
|
97
|
-
|
208
|
+
```typescript
|
209
|
+
import { Event, EventEmitter, EventListener, EventHandler } from 'bootifyjs';
|
210
|
+
|
211
|
+
// Define an event
|
212
|
+
@Event('user.created')
|
213
|
+
class UserCreatedEvent {
|
214
|
+
id: string;
|
215
|
+
email: string;
|
216
|
+
timestamp: Date = new Date();
|
217
|
+
}
|
98
218
|
|
99
|
-
|
100
|
-
|
219
|
+
// Emit events from services
|
220
|
+
@Service()
|
221
|
+
@EventEmitter()
|
222
|
+
class UserService {
|
223
|
+
private eventBus; // Injected by @EventEmitter
|
224
|
+
|
225
|
+
async createUser(userData) {
|
226
|
+
const user = await this.userRepository.create(userData);
|
227
|
+
|
228
|
+
// Emit event
|
229
|
+
await this.eventBus.emit('user.created', {
|
230
|
+
id: user.id,
|
231
|
+
email: user.email
|
232
|
+
});
|
233
|
+
|
234
|
+
return user;
|
235
|
+
}
|
236
|
+
}
|
237
|
+
|
238
|
+
// Handle events
|
239
|
+
@EventListener()
|
240
|
+
class UserEventHandlers {
|
241
|
+
@EventHandler('user.created')
|
242
|
+
async onUserCreated(event: UserCreatedEvent) {
|
243
|
+
console.log(`User created: ${event.email}`);
|
244
|
+
// Send welcome email, update analytics, etc.
|
245
|
+
}
|
246
|
+
}
|
247
|
+
```
|
101
248
|
|
102
|
-
|
103
|
-
BOOTIFY_DATABASE_HOST=postgres-server
|
249
|
+
### Logging
|
104
250
|
|
105
|
-
|
106
|
-
|
251
|
+
Comprehensive logging system with context tracking.
|
252
|
+
|
253
|
+
```typescript
|
254
|
+
import { Logger, Log } from 'bootifyjs';
|
255
|
+
|
256
|
+
@Service()
|
257
|
+
@Logger('UserService')
|
258
|
+
export class UserService {
|
259
|
+
private logger; // Injected by @Logger
|
260
|
+
|
261
|
+
@Log({ logArgs: true, logDuration: true })
|
262
|
+
async createUser(userData) {
|
263
|
+
this.logger.info('Creating new user', { email: userData.email });
|
264
|
+
|
265
|
+
// Business logic...
|
266
|
+
|
267
|
+
this.logger.info('User created successfully');
|
268
|
+
return user;
|
269
|
+
}
|
270
|
+
}
|
107
271
|
```
|
108
272
|
|
109
|
-
|
273
|
+
## Bootstrapping Your Application
|
274
|
+
|
275
|
+
There are two ways to bootstrap your application:
|
276
|
+
|
277
|
+
### 1. Using createBootifyApp (Recommended)
|
278
|
+
|
110
279
|
```typescript
|
111
|
-
import {
|
112
|
-
import {
|
280
|
+
import { createBootifyApp } from 'bootifyjs';
|
281
|
+
import { UserController } from './controllers/user.controller';
|
113
282
|
|
114
|
-
|
283
|
+
async function main() {
|
284
|
+
const { app, start, stop } = await createBootifyApp({
|
285
|
+
port: 3000,
|
286
|
+
controllers: [UserController],
|
287
|
+
enableSwagger: true,
|
288
|
+
enableCors: true
|
289
|
+
});
|
290
|
+
|
291
|
+
await start();
|
292
|
+
console.log('Server running at http://localhost:3000');
|
293
|
+
console.log('API docs available at http://localhost:3000/api-docs');
|
294
|
+
}
|
115
295
|
|
116
|
-
|
117
|
-
console.log(appConfig.server.port); // 8080
|
118
|
-
console.log(appConfig.server.host); // '0.0.0.0'
|
119
|
-
console.log(appConfig.database.host); // 'postgres-server'
|
296
|
+
main().catch(console.error);
|
120
297
|
```
|
121
298
|
|
122
|
-
|
123
|
-
```typescript
|
124
|
-
import { InjectConfig } from './core/decorators';
|
299
|
+
### 2. Manual Bootstrap
|
125
300
|
|
126
|
-
|
127
|
-
|
128
|
-
|
301
|
+
```typescript
|
302
|
+
import {
|
303
|
+
Application,
|
304
|
+
configureLogging,
|
305
|
+
configureEventSystem,
|
306
|
+
corsMiddleware,
|
307
|
+
contextMiddleware,
|
308
|
+
createRequestLoggingMiddleware,
|
309
|
+
swaggerMiddleware,
|
310
|
+
OpenAPIGenerator
|
311
|
+
} from 'bootifyjs';
|
312
|
+
import { UserController } from './controllers/user.controller';
|
313
|
+
|
314
|
+
async function bootstrap() {
|
315
|
+
// Initialize logging
|
316
|
+
const { logger, startupLogger } = configureLogging();
|
129
317
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
318
|
+
// Initialize event system
|
319
|
+
const eventBus = configureEventSystem();
|
320
|
+
|
321
|
+
// Generate OpenAPI documentation
|
322
|
+
const openApiGenerator = new OpenAPIGenerator({
|
323
|
+
title: 'My API',
|
324
|
+
version: '1.0.0',
|
325
|
+
description: 'My API description'
|
326
|
+
});
|
327
|
+
|
328
|
+
const controllers = [UserController];
|
329
|
+
openApiGenerator.addControllers(controllers);
|
330
|
+
const openApiSpec = openApiGenerator.getSpec();
|
331
|
+
|
332
|
+
// Create application
|
333
|
+
const app = new Application({
|
334
|
+
controllers,
|
335
|
+
middlewares: [
|
336
|
+
contextMiddleware,
|
337
|
+
corsMiddleware,
|
338
|
+
createRequestLoggingMiddleware(),
|
339
|
+
swaggerMiddleware(openApiSpec)
|
340
|
+
],
|
341
|
+
port: 3000,
|
342
|
+
hostname: 'localhost'
|
343
|
+
});
|
344
|
+
|
345
|
+
await app.start();
|
346
|
+
logger.info('Server started');
|
134
347
|
}
|
348
|
+
|
349
|
+
bootstrap().catch(console.error);
|
135
350
|
```
|
136
351
|
|
137
|
-
|
352
|
+
## Middleware
|
138
353
|
|
139
|
-
|
354
|
+
Create custom middleware to handle cross-cutting concerns.
|
140
355
|
|
141
|
-
|
142
|
-
|
143
|
-
- `BOOTIFY_SERVER_HOST` → `appConfig.server.host`
|
144
|
-
- `BOOTIFY_DATABASE_HOST` → `appConfig.database.host`
|
145
|
-
- `BOOTIFY_DATABASE_PORT` → `appConfig.database.port`
|
356
|
+
```typescript
|
357
|
+
import { Middleware } from 'bootifyjs';
|
146
358
|
|
147
|
-
|
359
|
+
export const loggingMiddleware: Middleware = async (req, res, next) => {
|
360
|
+
const start = Date.now();
|
361
|
+
console.log(`${req.method} ${req.url} - Request started`);
|
362
|
+
|
363
|
+
await next();
|
364
|
+
|
365
|
+
const duration = Date.now() - start;
|
366
|
+
console.log(`${req.method} ${req.url} - Request completed in ${duration}ms`);
|
367
|
+
};
|
368
|
+
```
|
148
369
|
|
149
|
-
|
150
|
-
- **Booleans**: `"true"` → `true`, `"false"` → `false`
|
151
|
-
- **Strings**: Everything else remains as string
|
370
|
+
## OpenAPI Documentation
|
152
371
|
|
153
|
-
|
372
|
+
BootifyJS automatically generates OpenAPI documentation for your API.
|
154
373
|
|
155
|
-
|
156
|
-
|
157
|
-
|
374
|
+
```typescript
|
375
|
+
import { Controller, Get, ApiTags, ApiOperation, ApiResponse } from 'bootifyjs';
|
376
|
+
|
377
|
+
@Controller('/health')
|
378
|
+
@ApiTags('Health')
|
379
|
+
export class HealthController {
|
380
|
+
@Get('/')
|
381
|
+
@ApiOperation({
|
382
|
+
summary: 'Health check',
|
383
|
+
description: 'Check if the API is running'
|
384
|
+
})
|
385
|
+
@ApiResponse(200, {
|
386
|
+
description: 'API is healthy',
|
387
|
+
schema: healthResponseSchema
|
388
|
+
})
|
389
|
+
health() {
|
390
|
+
return { status: 'OK', timestamp: new Date().toISOString() };
|
391
|
+
}
|
392
|
+
}
|
393
|
+
```
|
158
394
|
|
159
|
-
|
395
|
+
## Error Handling
|
160
396
|
|
161
|
-
|
397
|
+
BootifyJS provides built-in error classes for common HTTP errors.
|
162
398
|
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
399
|
+
```typescript
|
400
|
+
import { NotFoundError, ValidationError } from 'bootifyjs';
|
401
|
+
|
402
|
+
@Service()
|
403
|
+
export class UserService {
|
404
|
+
getUserById(id: string) {
|
405
|
+
const user = this.userRepository.findById(id);
|
406
|
+
if (!user) {
|
407
|
+
throw new NotFoundError(`User with id ${id} not found`);
|
408
|
+
}
|
409
|
+
return user;
|
410
|
+
}
|
411
|
+
|
412
|
+
createUser(userData) {
|
413
|
+
if (!userData.email) {
|
414
|
+
throw new ValidationError('Email is required');
|
415
|
+
}
|
416
|
+
// ...
|
417
|
+
}
|
418
|
+
}
|
419
|
+
```
|
170
420
|
|
171
|
-
##
|
421
|
+
## License
|
172
422
|
|
173
|
-
|
423
|
+
MIT
|