HazelJS Core Package
@hazeljs/core is the foundation of every HazelJS application. The HazelJS Core package provides modules, controllers, routing, dependency injection, middleware, guards, interceptors, pipes, exception filters, lifecycle hooks, health checks, testing utilities, and security helpers. Every other HazelJS package depends on @hazeljs/core.
Quick Reference
- Purpose:
@hazeljs/coreis the foundation package for HazelJS. It provides the module system, dependency injection container, decorator-based routing, request pipeline (middleware, guards, interceptors, pipes, exception filters), application bootstrap, lifecycle hooks, health checks, and testing utilities. - When to use: Every HazelJS application requires
@hazeljs/core. Install@hazeljs/coreas the first dependency for any HazelJS project. - Key concepts: HazelApp, HazelModule, Controller, Service, Injectable, DI Container, Guards (CanActivate), Interceptors, Pipes (PipeTransform), Exception Filters, Middleware, Scopes (singleton, transient, request).
- Inputs: HTTP requests routed to controller methods via decorators.
- Outputs: JSON responses, status codes, response headers.
- Dependencies: Node.js 18+, TypeScript with
experimentalDecorators: trueandemitDecoratorMetadata: true,reflect-metadata. - Common patterns: Controller delegates to Service; Service contains business logic; Module groups Controller + Service; Guard checks auth before handler; Interceptor wraps handler for logging/caching; Pipe validates input DTO.
- Common mistakes: Forgetting to register a provider in a module's
providersarray; forgetting to export a provider that another module needs; putting business logic in controllers instead of services; using@Injectable()for business services instead of@Service(); not enablingexperimentalDecoratorsin tsconfig.json.
Installation
npm install @hazeljs/core
Peer dependencies (for validation):
npm install class-validator class-transformer
TypeScript configuration required in tsconfig.json:
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}
Architecture Mental Model
Every HTTP request in a HazelJS application flows through the @hazeljs/core request pipeline:
HTTP Request → Router → Middleware → Guards → Interceptors (before) → Pipes → Controller Handler → Interceptors (after) → Response
↓ (on error)
Exception Filters → Error Response
The module system organizes code:
AppModule (root)
├── imports: [UserModule, AuthModule, AIModule]
│
├── UserModule
│ ├── controllers: [UserController]
│ ├── providers: [UserService]
│ └── exports: [UserService]
│
└── AuthModule
├── controllers: [AuthController]
├── providers: [AuthService, JwtService]
└── exports: [AuthService]
The DI container resolves dependencies:
UserController ← constructor(UserService) ← DI Container resolves UserService automatically
What HazelJS Core Provides
| Feature | Description |
|---|---|
HazelApp | Application bootstrap class. Creates and starts the HTTP server. |
@HazelModule | Module decorator. Groups controllers, providers, imports, and exports. |
@Controller | Controller decorator. Defines route prefix and registers HTTP handlers. |
@Get, @Post, @Put, @Delete, @Patch | HTTP method decorators for controller methods. |
@Body, @Param, @Query, @Headers, @Req, @Res | Parameter decorators for extracting request data. |
@Service, @Injectable | Provider decorators for dependency injection registration. |
@Inject | Explicit injection by token (string, symbol, or class). |
@UseGuards | Apply guard classes to controllers or methods for authorization. |
@UseInterceptors | Apply interceptor classes to controllers or methods. |
@UsePipes | Apply pipe classes for input validation and transformation. |
@HttpCode, @Header, @Redirect | Response decorators for status codes, headers, and redirects. |
@Public, @SkipAuth | Mark routes as public (skip authentication guards). |
@Timeout, @Retry | Per-route timeout and retry decorators. |
ValidationPipe, ParseIntPipe | Built-in pipes for DTO validation and type conversion. |
HttpExceptionFilter | Built-in exception filter for HTTP errors. |
PerformanceService | Core service for managing traces, spans, and metrics. |
@Trace | Decorator for automatic performance monitoring and tracing via OTel. |
EnhancedErrors | Helper for creating unified, type-safe API error responses. |
Container | Singleton DI container with resolve(), register(), registerProvider(). |
Test | Testing utility for creating isolated test modules with provider overrides. |
Quick Start: Minimal HazelJS Application
// File: src/main.ts
import { HazelApp, HazelModule, Controller, Get, Service } from '@hazeljs/core';
@Service()
class HealthService {
getStatus() {
return { ok: true, timestamp: new Date().toISOString() };
}
}
@Controller('health')
class HealthController {
constructor(private readonly health: HealthService) {}
@Get()
getHealth() {
return this.health.getStatus();
}
}
@HazelModule({
controllers: [HealthController],
providers: [HealthService],
})
class AppModule {}
async function bootstrap() {
const app = new HazelApp(AppModule);
await app.listen(3000);
}
bootstrap();
// GET http://localhost:3000/health → { "ok": true, "timestamp": "2024-01-01T00:00:00.000Z" }
Recipes
Recipe: Build a CRUD REST API
// File: src/user/user.service.ts
import { Service } from '@hazeljs/core';
interface User {
id: number;
name: string;
email: string;
}
@Service()
export class UserService {
private users: User[] = [];
findAll(): User[] {
return this.users;
}
findOne(id: number): User | undefined {
return this.users.find(u => u.id === id);
}
create(data: { name: string; email: string }): User {
const user = { id: this.users.length + 1, ...data };
this.users.push(user);
return user;
}
remove(id: number): boolean {
const idx = this.users.findIndex(u => u.id === id);
if (idx === -1) return false;
this.users.splice(idx, 1);
return true;
}
}
// File: src/user/user.controller.ts
import { Controller, Get, Post, Delete, Param, Body, HttpCode, NotFoundException } from '@hazeljs/core';
import { UserService } from './user.service';
@Controller('users')
export class UserController {
constructor(private readonly userService: UserService) {}
@Get()
findAll() {
return this.userService.findAll();
}
@Get(':id')
findOne(@Param('id') id: string) {
const user = this.userService.findOne(parseInt(id, 10));
if (!user) throw new NotFoundException(`User #${id} not found`);
return user;
}
@Post()
@HttpCode(201)
create(@Body() body: { name: string; email: string }) {
return this.userService.create(body);
}
@Delete(':id')
@HttpCode(204)
remove(@Param('id') id: string) {
const removed = this.userService.remove(parseInt(id, 10));
if (!removed) throw new NotFoundException(`User #${id} not found`);
}
}
// File: src/user/user.module.ts
import { HazelModule } from '@hazeljs/core';
import { UserController } from './user.controller';
import { UserService } from './user.service';
@HazelModule({
controllers: [UserController],
providers: [UserService],
exports: [UserService],
})
export class UserModule {}
// File: src/main.ts
import { HazelApp, HazelModule } from '@hazeljs/core';
import { UserModule } from './user/user.module';
@HazelModule({ imports: [UserModule] })
class AppModule {}
async function bootstrap() {
const app = new HazelApp(AppModule);
await app.listen(3000);
}
bootstrap();
HazelJS Core Request Pipeline
Step 1: Routing
The HazelJS router matches the HTTP method and URL path to a controller method registered with @Controller and @Get/@Post/@Put/@Delete/@Patch decorators.
Step 2: Middleware
HazelJS middleware runs before guards. Built-in HazelJS middleware includes CorsMiddleware, LoggerMiddleware, SecurityHeadersMiddleware, RateLimitMiddleware, and TimeoutMiddleware.
Step 3: Guards
HazelJS guards implement CanActivate and decide whether a request is allowed. Guards run after routing and before the handler. Use guards for authentication and authorization.
Step 4: Interceptors
HazelJS interceptors implement Interceptor and wrap the handler execution. Interceptors run before and after the handler. Use interceptors for logging, caching, response transformation, and retry logic.
Step 5: Pipes
HazelJS pipes implement PipeTransform and validate or transform individual input values (route params, query params, request body). The built-in ValidationPipe validates DTOs using class-validator.
Step 6: Controller Handler
The controller method executes and returns data. HazelJS automatically serializes the return value as JSON.
Step 7: Exception Filters
If any step throws an error, HazelJS exception filters catch the error and format the HTTP error response. The built-in HttpExceptionFilter handles HttpError subclasses.
HazelJS Core Dependency Injection
The HazelJS DI container supports three provider scopes:
| Scope | Behavior | When to use |
|---|---|---|
singleton (default) | One instance shared across the entire application | Stateless services, configuration, shared resources |
transient | New instance for every injection point | Per-consumer state, lightweight services |
request | New instance for each HTTP request | Request-specific context, user state per request |
Decorator Conventions
| Decorator | Use for |
|---|---|
@Service() | Business logic services, repositories, data access |
@Injectable() | Guards, interceptors, pipes, framework constructs |
@Inject(token) | Explicit injection by string/symbol token |
When to Use HazelJS Core Features
| Feature | Use this when... | Avoid this when... |
|---|---|---|
| Guard | Checking authentication or authorization before a handler | Transforming response data (use Interceptor) |
| Interceptor | Wrapping handler execution for logging, caching, or retry | Checking auth (use Guard) |
| Pipe | Validating or transforming a single input value | Wrapping the whole handler (use Interceptor) |
| Middleware | Running logic before routing (CORS, body parsing) | Needing access to the matched route or handler metadata (use Guard) |
| Exception Filter | Formatting error responses consistently | Normal response handling |
Recipe: Add Validation to a POST Endpoint
// File: src/user/create-user.dto.ts
import { IsString, IsEmail, MinLength } from 'class-validator';
export class CreateUserDto {
@IsString()
@MinLength(2)
name: string;
@IsEmail()
email: string;
}
// File: src/user/user.controller.ts
import { Controller, Post, Body, UsePipes, ValidationPipe } from '@hazeljs/core';
import { CreateUserDto } from './create-user.dto';
import { UserService } from './user.service';
@Controller('users')
export class UserController {
constructor(private readonly userService: UserService) {}
@Post()
@UsePipes(new ValidationPipe())
create(@Body(CreateUserDto) dto: CreateUserDto) {
return this.userService.create(dto);
}
}
Recipe: Add an Auth Guard
// File: src/auth/auth.guard.ts
import { Injectable, CanActivate, ExecutionContext, UnauthorizedException } from '@hazeljs/core';
@Injectable()
export class AuthGuard implements CanActivate {
canActivate(context: ExecutionContext): boolean {
const req = context.switchToHttp().getRequest();
const token = req.headers?.authorization;
if (!token) throw new UnauthorizedException('Missing authorization header');
// Validate token here
return true;
}
}
// File: src/user/user.controller.ts
import { Controller, Get, UseGuards } from '@hazeljs/core';
import { AuthGuard } from '../auth/auth.guard';
@Controller('users')
@UseGuards(AuthGuard)
export class UserController {
@Get()
findAll() {
return []; // protected endpoint
}
}
Recipe: Write a Unit Test
// File: src/user/user.service.spec.ts
import { Test } from '@hazeljs/core';
import { UserService } from './user.service';
describe('UserService', () => {
let service: UserService;
beforeEach(async () => {
const module = await Test.createTestingModule({
providers: [UserService],
}).compile();
service = module.get(UserService);
});
it('should create a user', () => {
const user = service.create({ name: 'Alice', email: 'alice@example.com' });
expect(user.id).toBe(1);
expect(user.name).toBe('Alice');
});
it('should find all users', () => {
service.create({ name: 'Alice', email: 'alice@example.com' });
expect(service.findAll()).toHaveLength(1);
});
});
HazelJS Core Error Classes
| Error Class | Alias | HTTP Status | Usage |
|---|---|---|---|
BadRequestError | BadRequestException | 400 | Invalid input, validation failure |
UnauthorizedError | UnauthorizedException | 401 | Missing or invalid authentication |
ForbiddenError | ForbiddenException | 403 | Authenticated but insufficient permissions |
NotFoundError | NotFoundException | 404 | Resource not found |
RequestTimeoutError | — | 408 | Handler exceeded timeout |
ConflictError | ConflictException | 409 | Resource conflict (duplicate) |
InternalServerError | InternalServerErrorException | 500 | Unexpected server error |
Unified Error Responses with EnhancedErrors
HazelJS 0.3.0 introduces EnhancedErrors, a helper for creating consistent error responses across your entire application.
import { EnhancedErrors } from '@hazeljs/core';
// In a service or controller
throw EnhancedErrors.notFound('User not found in our database');
throw EnhancedErrors.badRequest('Validation failed: name is required');
throw EnhancedErrors.internal('Database connection failed');
Performance Monitoring & Tracing
HazelJS provides native support for performance monitoring and OpenTelemetry-based tracing via PerformanceService and the @Trace decorator.
Using @Trace
Automatically measure method execution time and report to your observability backend.
import { Service, Trace } from '@hazeljs/core';
@Service()
export class DataService {
@Trace('data.expensive_operation')
async expensiveOperation() {
// Execution time is tracked automatically
return await this.process();
}
}
Programmatic Tracing
import { Service, PerformanceService } from '@hazeljs/core';
@Service()
export class PaymentService {
constructor(private readonly performance: PerformanceService) {}
async checkout() {
const span = this.performance.startSpan('payment.checkout');
try {
await this.processPayment();
} finally {
span.end();
}
}
}
HazelJS Core API Reference
For complete symbol-level documentation, see:
- API Reference — Full list of decorators, classes, interfaces, and types
- Controllers Guide — Routing, parameter extraction, response handling
- Providers Guide — Dependency injection, scopes, custom providers
- Modules Guide — Module organization, imports, exports, dynamic modules
- Guards Guide — Authentication and authorization
- Interceptors Guide — Wrapping handler execution
- Pipes Guide — Input validation and transformation
- Exception Filters Guide — Error handling
- Middleware Guide — Cross-cutting concerns
Related Pages
- Config Package — Type-safe configuration for HazelJS applications
- Auth Package — Authentication, JWT, and role-based guards
- Swagger Package — OpenAPI documentation generation
- Cache Package — Response caching with Redis or in-memory
- AI Package — LLM provider integration (builds on
@hazeljs/core)
Prerequisites
- Installation — Install Node.js, TypeScript, and
@hazeljs/core
Used Together With
@hazeljs/ai— AI integration requires@hazeljs/corefor modules and DI@hazeljs/agent— Agent Runtime requires@hazeljs/corefor modules and DI@hazeljs/auth— Authentication guards require@hazeljs/coreguard system@hazeljs/config— Configuration requires@hazeljs/coremodule system