OrkaJS
Orka.JS
NestJS

Intégration NestJS

@orka-js/nestjs — Agents IA enterprise avec l'IoC NestJS

La première intégration NestJS vraiment idiomatique pour les agents IA. Exposez les agents via DI, décorateurs, guards, pipes, CQRS et transports microservices — avec les patterns que vous connaissez déjà.

Ce qui est inclus

OrkaModule DI

forRoot / forRootAsync / forMicroservice — pattern NestJS DynamicModule standard avec support ConfigService.

@OrkaAgent & @InjectAgent

Décorateurs pour marquer et injecter des agents n'importe où dans votre graphe DI — services, controllers, guards.

@AgentReact (Event-Driven)

Décorateur de méthode qui délègue automatiquement n'importe quel @OnEvent() à un agent. Zéro boilerplate.

OrkaSemanticGuard

Guard CanActivate alimenté par LLM. Décrivez votre politique en anglais — le LLM décide ALLOW ou DENY.

AgentValidationPipe

PipeTransform qui accepte du langage naturel et retourne un DTO typé via llm.generateObject().

CQRS + Microservices

OrkaQueryHandler, @AgentQueryHandler, OrkaMessageHandler, AgentClient — patterns enterprise complets.

Installation

npm install @orka-js/nestjs
# or
pnpm add @orka-js/nestjs
 
# Optional — install only what you use:
npm install @nestjs/cqrs # For CQRS features
npm install @nestjs/microservices # For microservice transport

Configuration du module

Importez OrkaModule dans votre AppModule. Tous les agents sont auto-enregistrés comme providers DI nommés.

app.module.ts
// app.module.ts
import { Module } from '@nestjs/common';
import { OrkaModule } from '@orka-js/nestjs';
import { Agent } from '@orka-js/agent';
import { AnthropicAdapter } from '@orka-js/anthropic';
 
const llm = new AnthropicAdapter({ apiKey: process.env.ANTHROPIC_API_KEY! });
 
const salesAgent = new Agent({ goal: 'Sales assistant', tools: [] }, llm);
const supportAgent = new Agent({ goal: 'Support assistant', tools: [] }, llm);
 
@Module({
imports: [
OrkaModule.forRoot({
agents: {
sales: salesAgent,
support: supportAgent,
},
path: 'ai', // Routes at: /ai, /ai/:agent, /ai/:agent/stream
}),
],
})
export class AppModule {}

Config asynchrone (avec ConfigService)

Utilisez forRootAsync quand vos clés LLM viennent des variables d'environnement via ConfigService.

app.module.ts
// app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { OrkaModule } from '@orka-js/nestjs';
import { Agent } from '@orka-js/agent';
import { AnthropicAdapter } from '@orka-js/anthropic';
 
@Module({
imports: [
ConfigModule.forRoot(),
OrkaModule.forRootAsync({
imports: [ConfigModule],
path: 'ai',
useFactory: (config: ConfigService) => ({
agents: {
assistant: new Agent(
{ goal: 'Helpful assistant', tools: [] },
new AnthropicAdapter({ apiKey: config.get('ANTHROPIC_API_KEY')! })
),
},
}),
inject: [ConfigService],
}),
],
})
export class AppModule {}

Routes HTTP auto-générées

Quand path est défini dans OrkaModule.forRoot(), OrkaJS monte automatiquement ces routes :

MethodPathDescription
GET/{path}Liste tous les agents enregistrés
GET/{path}/:agentInformations sur l'agent
POST/{path}/:agentExécuter l'agent (sans streaming)
POST/{path}/:agent/streamExécuter avec SSE streaming

Décorateurs

Injectez des agents n'importe où dans votre application. Utilisez @OrkaAgent pour marquer les classes avec des métadonnées.

order.service.ts
// order.service.ts
import { Injectable } from '@nestjs/common';
import { InjectAgent } from '@orka-js/nestjs';
import type { BaseAgent } from '@orka-js/agent';
 
@Injectable()
export class OrderService {
constructor(
@InjectAgent('sales') private salesAgent: BaseAgent,
@InjectAgent('support') private supportAgent: BaseAgent,
) {}
 
async processOrder(description: string) {
return this.salesAgent.run(description);
}
 
async handleComplaint(issue: string) {
return this.supportAgent.run(issue);
}
}

@AgentReact — Agents Event-Driven

Décorez n'importe quelle méthode @OnEvent() pour déléguer automatiquement le payload à un agent.

order-events.handler.ts
// order-events.handler.ts
import { Injectable } from '@nestjs/common';
import { OnEvent } from '@nestjs/event-emitter';
import { InjectAgent, AgentReact } from '@orka-js/nestjs';
import type { BaseAgent } from '@orka-js/agent';
 
@Injectable()
export class OrderEventsHandler {
constructor(
@InjectAgent('fulfillment') private agent: BaseAgent,
@InjectAgent('retention') private retentionAgent: BaseAgent,
) {}
 
// Awaits the agent result
@OnEvent('order.created')
@AgentReact()
async onOrderCreated(payload: OrderCreatedEvent) {}
 
// Fire-and-forget — doesn't block the event loop
@OnEvent('customer.churned')
@AgentReact({ agent: 'retentionAgent', async: true })
onCustomerChurned(payload: ChurnEvent): void {}
}

OrkaSemanticGuard

Protégez les routes avec une autorisation sémantique alimentée par LLM. Échoue fermé si le LLM est indisponible.

admin.controller.ts
// admin.controller.ts
import { Controller, Get, UseGuards } from '@nestjs/common';
import { OrkaSemanticGuard } from '@orka-js/nestjs';
import { AnthropicAdapter } from '@orka-js/anthropic';
 
const llm = new AnthropicAdapter({ apiKey: process.env.ANTHROPIC_API_KEY! });
 
@Controller('admin')
@UseGuards(
new OrkaSemanticGuard(
llm,
'Only allow requests that include a valid Authorization header with an admin Bearer token'
)
)
export class AdminController {
@Get('dashboard')
getDashboard() {
return { stats: '...' };
}
}
 
// The guard asks the LLM: "Based on the policy, should this request be ALLOWED or DENIED?"
// → Fails closed (returns false) if the LLM is unavailable

AgentValidationPipe — NLP → DTO

Acceptez du langage naturel depuis les clients et extrayez des données structurées. Retour direct à la validation de schéma pour les inputs structurés (pas de coût LLM).

products.controller.ts
// products.controller.ts
import { Controller, Post, Body } from '@nestjs/common';
import { AgentValidationPipe } from '@orka-js/nestjs';
import { AnthropicAdapter } from '@orka-js/anthropic';
import { z } from 'zod';
 
const llm = new AnthropicAdapter({ apiKey: process.env.ANTHROPIC_API_KEY! });
 
const SearchSchema = z.object({
category: z.string(),
color: z.string().optional(),
maxPrice: z.number().optional(),
size: z.string().optional(),
});
 
@Controller('products')
export class ProductsController {
@Post('search')
async search(
@Body(new AgentValidationPipe(SearchSchema, llm, { description: 'product search filters' }))
query: z.infer<typeof SearchSchema>
) {
return this.productService.search(query);
}
}
 
// Client sends: { "input": "red shoes under $50 in size 42" }
// Pipe returns: { category: "shoes", color: "red", maxPrice: 50, size: "42" }
 
// Client sends: { "category": "shoes", "color": "red" }
// Pipe validates directly — no LLM call (zero cost)

Intégration CQRS

Connectez directement des agents à des queries et commands CQRS. Nécessite @nestjs/cqrs.

recommendations.handler.ts
// Import from the /cqrs sub-path (requires @nestjs/cqrs)
import { OrkaQueryHandler, AgentQueryHandler } from '@orka-js/nestjs/cqrs';
import { InjectAgent } from '@orka-js/nestjs';
import type { BaseAgent } from '@orka-js/agent';
 
// 1. Define the query
export class GetProductRecommendationsQuery {
constructor(public readonly userId: string, public readonly category: string) {}
}
 
// 2. Create the handler — extends OrkaQueryHandler, zero boilerplate
@AgentQueryHandler(GetProductRecommendationsQuery)
export class RecommendationsHandler extends OrkaQueryHandler<GetProductRecommendationsQuery> {
constructor(@InjectAgent('recommendations') protected agent: BaseAgent) {
super();
}
// execute() is provided by OrkaQueryHandler:
// → calls agent.run(JSON.stringify(query)) automatically
}
 
// 3. Register in module
@Module({
imports: [CqrsModule, OrkaModule.forRoot({ agents: { recommendations: recoAgent } })],
providers: [RecommendationsHandler],
})
export class ProductsModule {}

Agent as Microservice

Déployez des agents comme microservices NestJS indépendants — scalables, agnostiques au transport (Redis, NATS, Kafka, TCP). Nécessite @nestjs/microservices.

microservice-setup.ts
// Import from the /microservice sub-path (requires @nestjs/microservices)
import { OrkaClientModule } from '@orka-js/nestjs/microservice';
import { InjectAgentClient, AgentClient } from '@orka-js/nestjs';
import { Transport } from '@nestjs/microservices';
 
// ── SERVER APP (agent microservice) ─────────────────────────────────────────
 
// main.ts
const app = await NestFactory.createMicroservice(AppModule, {
transport: Transport.REDIS,
options: { host: 'localhost', port: 6379 },
});
await app.listen();
 
// app.module.ts
@Module({
imports: [await OrkaModule.forMicroservice({
agents: { sales: salesAgent, support: supportAgent }
})]
})
export class AppModule {}
 
// ── CONSUMER APP ─────────────────────────────────────────────────────────────
 
@Module({
imports: [
OrkaClientModule.forRoot({
clients: [{
name: 'agents',
options: { transport: Transport.REDIS, options: { host: 'localhost', port: 6379 } },
}],
}),
],
})
export class ConsumerModule {}
 
@Injectable()
export class OrderService {
constructor(@InjectAgentClient('agents') private client: AgentClient) {}
 
async processOrder(order: Order) {
return this.client.run('sales', JSON.stringify(order));
}
}

📦 Dépendances peer optionnelles

Les features CQRS et microservices nécessitent des packages optionnels. Installez seulement ce dont vous avez besoin :

npm install @nestjs/cqrs # @orka-js/nestjs/cqrs
npm install @nestjs/microservices # @orka-js/nestjs/microservice