HazelJS Core Package

npm downloads

@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/core is 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/core as 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: true and emitDecoratorMetadata: 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 providers array; 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 enabling experimentalDecorators in 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

FeatureDescription
HazelAppApplication bootstrap class. Creates and starts the HTTP server.
@HazelModuleModule decorator. Groups controllers, providers, imports, and exports.
@ControllerController decorator. Defines route prefix and registers HTTP handlers.
@Get, @Post, @Put, @Delete, @PatchHTTP method decorators for controller methods.
@Body, @Param, @Query, @Headers, @Req, @ResParameter decorators for extracting request data.
@Service, @InjectableProvider decorators for dependency injection registration.
@InjectExplicit injection by token (string, symbol, or class).
@UseGuardsApply guard classes to controllers or methods for authorization.
@UseInterceptorsApply interceptor classes to controllers or methods.
@UsePipesApply pipe classes for input validation and transformation.
@HttpCode, @Header, @RedirectResponse decorators for status codes, headers, and redirects.
@Public, @SkipAuthMark routes as public (skip authentication guards).
@Timeout, @RetryPer-route timeout and retry decorators.
ValidationPipe, ParseIntPipeBuilt-in pipes for DTO validation and type conversion.
HttpExceptionFilterBuilt-in exception filter for HTTP errors.
PerformanceServiceCore service for managing traces, spans, and metrics.
@TraceDecorator for automatic performance monitoring and tracing via OTel.
EnhancedErrorsHelper for creating unified, type-safe API error responses.
ContainerSingleton DI container with resolve(), register(), registerProvider().
TestTesting 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:

ScopeBehaviorWhen to use
singleton (default)One instance shared across the entire applicationStateless services, configuration, shared resources
transientNew instance for every injection pointPer-consumer state, lightweight services
requestNew instance for each HTTP requestRequest-specific context, user state per request

Decorator Conventions

DecoratorUse 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

FeatureUse this when...Avoid this when...
GuardChecking authentication or authorization before a handlerTransforming response data (use Interceptor)
InterceptorWrapping handler execution for logging, caching, or retryChecking auth (use Guard)
PipeValidating or transforming a single input valueWrapping the whole handler (use Interceptor)
MiddlewareRunning logic before routing (CORS, body parsing)Needing access to the matched route or handler metadata (use Guard)
Exception FilterFormatting error responses consistentlyNormal 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 ClassAliasHTTP StatusUsage
BadRequestErrorBadRequestException400Invalid input, validation failure
UnauthorizedErrorUnauthorizedException401Missing or invalid authentication
ForbiddenErrorForbiddenException403Authenticated but insufficient permissions
NotFoundErrorNotFoundException404Resource not found
RequestTimeoutError408Handler exceeded timeout
ConflictErrorConflictException409Resource conflict (duplicate)
InternalServerErrorInternalServerErrorException500Unexpected 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:

Prerequisites

  • Installation — Install Node.js, TypeScript, and @hazeljs/core

Used Together With

  • @hazeljs/ai — AI integration requires @hazeljs/core for modules and DI
  • @hazeljs/agent — Agent Runtime requires @hazeljs/core for modules and DI
  • @hazeljs/auth — Authentication guards require @hazeljs/core guard system
  • @hazeljs/config — Configuration requires @hazeljs/core module system