Get Started
Development
Adding to Your Codebase
Deployment
CLI Reference
Adding to Your Codebase
NestJS Integration
How to integrate Pickaxe agents with NestJS applications
Learn how to integrate Pickaxe agents into your NestJS application using dependency injection and decorators.
Installation
Install the Pickaxe SDK and required dependencies:
Copy
Ask AI
npm install @hatchet-dev/pickaxe
npm install @nestjs/config # For environment configuration
Agent Setup
Create your agent definition (src/agents/my-agent.ts
):
Copy
Ask AI
import { pickaxe } from "@hatchet-dev/pickaxe";
import z from "zod";
const MyAgentInput = z.object({
message: z.string(),
userId: z.string().optional(),
});
const MyAgentOutput = z.object({
response: z.string(),
processedAt: z.string(),
});
export const myAgent = pickaxe.agent({
name: "my-nestjs-agent",
description: "Agent for NestJS application",
inputSchema: MyAgentInput,
outputSchema: MyAgentOutput,
executionTimeout: "5m",
fn: async (input, ctx) => {
ctx.logger.info(`Processing message for user: ${input.userId}`);
// Your agent logic here
return {
response: `Processed: ${input.message}`,
processedAt: new Date().toISOString(),
};
},
});
Service Integration
Create a service to handle agent operations (src/agents/agent.service.ts
):
Copy
Ask AI
import { Injectable, Logger, OnModuleInit } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { Hatchet } from '@hatchet-dev/typescript-sdk';
import { myAgent } from '@/agents/my-agent';
@Injectable()
export class AgentService implements OnModuleInit {
private readonly logger = new Logger(AgentService.name);
private hatchet: Hatchet;
constructor(private configService: ConfigService) {}
async onModuleInit() {
try {
this.hatchet = Hatchet.init({
token: this.configService.get<string>('HATCHET_CLIENT_TOKEN'),
serverUrl: this.configService.get<string>('HATCHET_SERVER_URL'),
});
await this.hatchet.registerWorkflows([myAgent]);
this.logger.log('Hatchet workflows registered successfully');
} catch (error) {
this.logger.error('Failed to initialize Hatchet', error);
throw error;
}
}
async runAgent(input: { message: string; userId?: string }) {
try {
this.logger.log(`Running agent with input: ${JSON.stringify(input)}`);
const result = await myAgent.run(input);
this.logger.log(`Agent completed successfully`);
return result;
} catch (error) {
this.logger.error('Agent execution failed', error);
throw new Error('Agent execution failed');
}
}
async runAgentAsync(input: { message: string; userId?: string }) {
try {
this.logger.log(`Starting async agent with input: ${JSON.stringify(input)}`);
const workflowRef = await myAgent.runNoWait(input);
this.logger.log(`Agent started with run ID: ${workflowRef.runId}`);
return { runId: workflowRef.runId };
} catch (error) {
this.logger.error('Failed to start agent', error);
throw new Error('Failed to start agent');
}
}
async getAgentResult(runId: string) {
try {
// Implementation depends on your Hatchet client setup
// This is a placeholder for getting results by run ID
this.logger.log(`Getting result for run ID: ${runId}`);
// You would implement this based on your Hatchet client
// return await this.hatchet.getWorkflowResult(runId);
throw new Error('Method not implemented');
} catch (error) {
this.logger.error('Failed to get agent result', error);
throw new Error('Failed to get agent result');
}
}
}
Controller Implementation
Create a controller to expose agent endpoints (src/agents/agent.controller.ts
):
Copy
Ask AI
import {
Controller,
Post,
Body,
Get,
Param,
HttpException,
HttpStatus,
Logger,
} from '@nestjs/common';
import { AgentService } from './agent.service';
export class RunAgentDto {
message: string;
userId?: string;
}
export class AgentResponse {
response: string;
processedAt: string;
}
export class AsyncAgentResponse {
runId: string;
}
@Controller('agents')
export class AgentController {
private readonly logger = new Logger(AgentController.name);
constructor(private readonly agentService: AgentService) {}
@Post('run')
async runAgent(@Body() dto: RunAgentDto): Promise<AgentResponse> {
try {
this.logger.log(`Running agent for message: ${dto.message}`);
const result = await this.agentService.runAgent({
message: dto.message,
userId: dto.userId,
});
return result;
} catch (error) {
this.logger.error('Agent execution failed', error);
throw new HttpException(
'Agent execution failed',
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}
@Post('run-async')
async runAgentAsync(@Body() dto: RunAgentDto): Promise<AsyncAgentResponse> {
try {
this.logger.log(`Starting async agent for message: ${dto.message}`);
const result = await this.agentService.runAgentAsync({
message: dto.message,
userId: dto.userId,
});
return result;
} catch (error) {
this.logger.error('Failed to start agent', error);
throw new HttpException(
'Failed to start agent',
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}
@Get('result/:runId')
async getAgentResult(@Param('runId') runId: string) {
try {
this.logger.log(`Getting result for run ID: ${runId}`);
const result = await this.agentService.getAgentResult(runId);
return result;
} catch (error) {
this.logger.error('Failed to get agent result', error);
throw new HttpException(
'Failed to get agent result',
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}
}
Module Configuration
Create and configure the agent module (src/agents/agent.module.ts
):
Copy
Ask AI
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { AgentService } from './agent.service';
import { AgentController } from './agent.controller';
@Module({
imports: [ConfigModule],
controllers: [AgentController],
providers: [AgentService],
exports: [AgentService],
})
export class AgentModule {}
Update your main app module (src/app.module.ts
):
Copy
Ask AI
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { AgentModule } from './agents/agent.module';
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
}),
AgentModule,
],
})
export class AppModule {}
Environment Configuration
Configure your environment variables (.env
):
Copy
Ask AI
HATCHET_CLIENT_TOKEN=your_token_here
HATCHET_SERVER_URL=https://app.hatchet.run
Advanced Usage
Using Guards and Interceptors
Add authentication and logging:
Copy
Ask AI
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { UseGuards, UseInterceptors } from '@nestjs/common';
@Injectable()
export class AuthGuard implements CanActivate {
canActivate(context: ExecutionContext): boolean {
// Your authentication logic
const request = context.switchToHttp().getRequest();
return !!request.headers.authorization;
}
}
@Controller('agents')
@UseGuards(AuthGuard)
export class AgentController {
// Your controller methods
}
Background Processing with Queues
For production use, consider integrating with Bull Queue:
Copy
Ask AI
npm install @nestjs/bull bull
npm install @types/bull
Copy
Ask AI
import { Injectable } from '@nestjs/common';
import { InjectQueue } from '@nestjs/bull';
import { Queue } from 'bull';
@Injectable()
export class AgentQueueService {
constructor(@InjectQueue('agent') private agentQueue: Queue) {}
async queueAgentRun(data: { message: string; userId?: string }) {
const job = await this.agentQueue.add('run-agent', data, {
attempts: 3,
backoff: {
type: 'exponential',
delay: 2000,
},
});
return { jobId: job.id };
}
}
Testing
Create unit tests for your agent service:
Copy
Ask AI
import { Test, TestingModule } from '@nestjs/testing';
import { ConfigService } from '@nestjs/config';
import { AgentService } from './agent.service';
describe('AgentService', () => {
let service: AgentService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
AgentService,
{
provide: ConfigService,
useValue: {
get: jest.fn((key: string) => {
const config = {
HATCHET_CLIENT_TOKEN: 'test-token',
HATCHET_SERVER_URL: 'http://test-url',
};
return config[key];
}),
},
},
],
}).compile();
service = module.get<AgentService>(AgentService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
// Add more tests here
});
Assistant
Responses are generated using AI and may contain mistakes.