DocumentationReference

Migrate from NestJS to HazelJS

Practical mapping for teams moving a NestJS (or Nest-style) TypeScript backend to HazelJS — especially when adding agents, RAG, or Agent OS.

Quick Reference

  • Purpose: Map NestJS modules, controllers, providers, pipes, and guards to HazelJS equivalents, then add AI packages without a second framework.
  • When to use: You know NestJS and want HazelJS for AI-native backends, or you are piloting a vertical slice (support agent, RAG API).
  • Key concepts: @HazelModule, controllers, DI providers, @AITask, @Agent, @Tool, HCEL, @hazeljs/rag.
  • Dependencies: @hazeljs/core, optionally @hazeljs/ai, @hazeljs/agent, @hazeljs/rag.
  • Related: HazelJS vs NestJS, NestJS + LangChain vs HazelJS, Introduction.

Should you migrate?

Migrate (or start greenfield on HazelJS) when:

  • Agents / RAG are core product surfaces
  • You want one DI container for HTTP and AI
  • You are tired of Nest + LangChain glue

Stay on NestJS when:

  • AI is a thin layer (a few LLM calls)
  • Nest ecosystem packages are hard requirements
  • Political / rewrite cost exceeds glue-code cost

See the honest tradeoffs in HazelJS vs NestJS.

Concept mapping

NestJSHazelJSNotes
@Module()@HazelModule()imports, controllers, providers
@Controller()@Controller({ path })Route prefix on options object
@Get / @Post / …Same decorator names from @hazeljs/coreFamiliar HTTP verbs
@Injectable()@Injectable()Provider registration
Constructor DIConstructor DISame mental model
Guards / pipes / interceptorsGuards / pipes / interceptorsSee core guides
ConfigModule@hazeljs/configEnv / typed config
Passport / custom auth@hazeljs/auth, @hazeljs/oauth, CASLPackage docs
TypeORM / Prisma@hazeljs/typeorm, @hazeljs/prismaFirst-class modules
LangChain in a Nest service@hazeljs/ai HCEL, @hazeljs/agent, @hazeljs/ragPrefer native over glue

Step 1 — Install and bootstrap

npm install @hazeljs/core
# Add when you need AI:
npm install @hazeljs/ai @hazeljs/agent @hazeljs/rag
import { HazelApp, HazelModule, Controller, Get } from '@hazeljs/core';

@Controller({ path: '/health' })
class HealthController {
  @Get()
  check() {
    return { ok: true };
  }
}

@HazelModule({
  controllers: [HealthController],
})
class AppModule {}

async function bootstrap() {
  const app = new HazelApp(AppModule);
  await app.listen(3000);
}

bootstrap();

Step 2 — Port a Nest controller

Nest-style:

@Controller('tasks')
export class TasksController {
  constructor(private readonly tasks: TasksService) {}

  @Get()
  findAll() {
    return this.tasks.findAll();
  }
}

HazelJS:

import { Controller, Get } from '@hazeljs/core';

@Controller({ path: '/tasks' })
export class TasksController {
  constructor(private readonly tasks: TasksService) {}

  @Get()
  findAll() {
    return this.tasks.findAll();
  }
}

Differences are mostly package imports and path on @Controller. Keep service classes as @Injectable() providers and register them on @HazelModule.

Step 3 — Replace LangChain glue with native AI

If a Nest service wrapped LangChain, prefer HCEL or an agent:

import { Injectable } from '@hazeljs/core';
import { AIService } from '@hazeljs/ai';

@Injectable()
export class AssistService {
  constructor(private ai: AIService) {}

  async answer(question: string) {
    return this.ai.hazel
      .prompt('Answer using knowledge base: {{input}}')
      .rag('product-docs')
      .execute(question);
  }
}

Or a decorator agent:

import { Agent, Tool } from '@hazeljs/agent';

@Agent({
  name: 'support-agent',
  systemPrompt: 'Help customers with orders.',
  enableMemory: true,
  enableRAG: true,
})
export class SupportAgent {
  @Tool({
    description: 'Get order status',
    parameters: [{ name: 'orderId', type: 'string', required: true }],
  })
  async getOrder(input: { orderId: string }) {
    return { orderId: input.orderId, status: 'shipped' };
  }
}

Incremental migration strategies

  1. Greenfield AI service — New HazelJS service for agents/RAG; Nest keeps classic APIs behind a gateway.
  2. Vertical slice — Move one domain (e.g. support chat) to HazelJS end-to-end.
  3. Strangler — Route traffic gradually via @hazeljs/gateway or your existing edge proxy.

Avoid a big-bang rewrite of unrelated Nest CRUD unless AI is the reason to move.

Checklist

  • Core HTTP module boots with @hazeljs/core
  • Controllers/providers mapped; smoke-test routes
  • Auth/guards ported or temporarily behind edge auth
  • Replace LangChain entrypoints with HCEL / Agent / RAG
  • Add observability (@hazeljs/observability / Inspector) for LLM paths
  • Read Agent OS if you need loops, policies, and CI tests

Next steps