You can use Lightfast agents with a native Node.js HTTP server for maximum control and minimal dependencies.
Examples
The examples start a simple HTTP server that listens on port 8080. You can test it using curl
:
curl -X POST http://localhost:8080/agents/my-session \
-H "Content-Type: application/json" \
-d '{"messages":[{"role":"user","content":"What'\''s the weather like?"}]}'
The examples use the OpenAI gpt-4o
model. Ensure that the OpenAI API key is set in the OPENAI_API_KEY
environment variable.
Setup
First, create your agent and memory configuration:
import { createAgent } from 'lightfast/agent';
import { createTool } from 'lightfast/tool';
import { RedisMemory } from 'lightfast/memory/adapters/redis';
import { openai } from '@ai-sdk/openai';
import { z } from 'zod';
// Create a simple tool
const weatherTool = createTool({
name: 'get_weather',
description: 'Get weather for a location',
parameters: z.object({
location: z.string().describe('The location to get weather for')
}),
execute: async ({ location }) => {
return `Weather in ${location}: Sunny, 72°F`;
}
});
// Create the agent
const agent = createAgent({
name: 'weather-assistant',
model: openai('gpt-4o'),
system: 'You are a helpful weather assistant.',
tools: { weather: weatherTool },
createRuntimeContext: ({ sessionId, resourceId }) => ({
timestamp: Date.now()
})
});
// Create memory adapter
const memory = new RedisMemory({
url: process.env.REDIS_URL!,
token: process.env.REDIS_TOKEN!
});
Basic Server
import { fetchRequestHandler } from 'lightfast/server/adapters/fetch';
import { createServer } from 'http';
const server = createServer(async (req, res) => {
if (req.method === 'POST' && req.url?.startsWith('/agents/')) {
// Extract sessionId from URL
const sessionId = req.url.split('/').pop() || 'default';
// Convert Node.js request to Web API Request
const body = await new Promise<string>((resolve) => {
let data = '';
req.on('data', chunk => data += chunk);
req.on('end', () => resolve(data));
});
const webRequest = new Request(`http://${req.headers.host}${req.url}`, {
method: req.method,
headers: req.headers as any,
body: body || undefined
});
try {
const response = await fetchRequestHandler({
agent,
sessionId,
memory,
req: webRequest,
resourceId: 'user-123', // Replace with actual user ID
createRequestContext: (req) => ({
userAgent: req.headers.get('user-agent'),
ip: req.headers.get('x-forwarded-for')
})
});
// Convert Web API Response back to Node.js response
res.statusCode = response.status;
for (const [key, value] of response.headers) {
res.setHeader(key, value);
}
if (response.body) {
const reader = response.body.getReader();
const pump = async () => {
const { done, value } = await reader.read();
if (done) {
res.end();
return;
}
res.write(value);
pump();
};
pump();
} else {
res.end();
}
} catch (error) {
res.statusCode = 500;
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify({ error: 'Internal Server Error' }));
}
} else {
res.statusCode = 404;
res.end('Not Found');
}
});
server.listen(8080, () => {
console.log('Server running on http://localhost:8080');
});
Advanced Server with Routing
import { fetchRequestHandler } from 'lightfast/server/adapters/fetch';
import { createServer } from 'http';
import { parse } from 'url';
const server = createServer(async (req, res) => {
const { pathname, query } = parse(req.url || '', true);
// Enable CORS
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
if (req.method === 'OPTIONS') {
res.writeHead(204);
res.end();
return;
}
// Route: POST /agents/:sessionId
const agentMatch = pathname?.match(/^\/agents\/(.+)$/);
if (req.method === 'POST' && agentMatch) {
const sessionId = agentMatch[1];
try {
// Parse request body
const body = await new Promise<string>((resolve, reject) => {
let data = '';
req.on('data', chunk => data += chunk);
req.on('end', () => resolve(data));
req.on('error', reject);
});
// Create Web API Request
const webRequest = new Request(`http://${req.headers.host}${req.url}`, {
method: req.method,
headers: req.headers as any,
body: body || undefined
});
const response = await fetchRequestHandler({
agent,
sessionId,
memory,
req: webRequest,
resourceId: extractUserId(req) || 'anonymous',
createRequestContext: (req) => ({
userAgent: req.headers.get('user-agent'),
ip: req.headers.get('x-forwarded-for'),
timestamp: Date.now()
}),
onError: ({ error }) => {
console.error(`Agent error for session ${sessionId}:`, error);
}
});
// Stream response
res.statusCode = response.status;
for (const [key, value] of response.headers) {
res.setHeader(key, value);
}
if (response.body) {
const reader = response.body.getReader();
const pump = async () => {
try {
const { done, value } = await reader.read();
if (done) {
res.end();
return;
}
res.write(value);
pump();
} catch (error) {
console.error('Streaming error:', error);
res.end();
}
};
pump();
} else {
res.end();
}
} catch (error) {
console.error('Request error:', error);
res.statusCode = 500;
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify({
error: 'Internal Server Error',
message: error instanceof Error ? error.message : 'Unknown error'
}));
}
return;
}
// Route: GET /health
if (req.method === 'GET' && pathname === '/health') {
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify({ status: 'ok', timestamp: Date.now() }));
return;
}
// 404 for unmatched routes
res.statusCode = 404;
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify({ error: 'Not Found' }));
});
// Helper function to extract user ID from request
function extractUserId(req: any): string | null {
// Extract from Authorization header, query param, etc.
const authHeader = req.headers.authorization;
if (authHeader?.startsWith('Bearer ')) {
// Parse JWT or API key to get user ID
return 'user-from-token';
}
return null;
}
server.listen(8080, () => {
console.log('Advanced server running on http://localhost:8080');
console.log('Endpoints:');
console.log(' POST /agents/:sessionId - Chat with agent');
console.log(' GET /health - Health check');
});
Production Considerations
Cluster Mode
For production use, consider running multiple processes:
import cluster from 'cluster';
import { cpus } from 'os';
if (cluster.isPrimary) {
const numCPUs = cpus().length;
console.log(`Primary ${process.pid} is running`);
// Fork workers
for (let i = 0; i < numCPUs; i++) {
cluster.fork();
}
cluster.on('exit', (worker, code, signal) => {
console.log(`Worker ${worker.process.pid} died`);
cluster.fork(); // Replace the dead worker
});
} else {
// Worker process - run your server here
createServer(/* your server logic */).listen(8080);
console.log(`Worker ${process.pid} started`);
}
Error Handling
// Global error handlers
process.on('uncaughtException', (error) => {
console.error('Uncaught Exception:', error);
process.exit(1);
});
process.on('unhandledRejection', (reason, promise) => {
console.error('Unhandled Rejection at:', promise, 'reason:', reason);
process.exit(1);
});
// Graceful shutdown
process.on('SIGINT', () => {
console.log('Received SIGINT, shutting down gracefully');
server.close(() => {
console.log('Server closed');
process.exit(0);
});
});
Environment Configuration
// config.ts
export const config = {
port: process.env.PORT ? parseInt(process.env.PORT) : 8080,
host: process.env.HOST || '0.0.0.0',
redis: {
url: process.env.REDIS_URL!,
token: process.env.REDIS_TOKEN!,
},
openai: {
apiKey: process.env.OPENAI_API_KEY!,
}
};
// Validate required environment variables
const requiredEnvVars = ['REDIS_URL', 'REDIS_TOKEN', 'OPENAI_API_KEY'];
for (const envVar of requiredEnvVars) {
if (!process.env[envVar]) {
console.error(`Missing required environment variable: ${envVar}`);
process.exit(1);
}
}
Testing
Test your server with different scenarios:
# Basic chat
curl -X POST http://localhost:8080/agents/test-session \
-H "Content-Type: application/json" \
-d '{"messages":[{"role":"user","content":"Hello!"}]}'
# Weather query
curl -X POST http://localhost:8080/agents/weather-session \
-H "Content-Type: application/json" \
-d '{"messages":[{"role":"user","content":"What'\''s the weather in Tokyo?"}]}'
# Health check
curl http://localhost:8080/health
Advantages
- Full Control: Complete control over the HTTP server
- Minimal Dependencies: No framework overhead
- Performance: Direct access to Node.js HTTP primitives
- Flexibility: Easy to customize routing and middleware
Disadvantages
- More Code: Requires manual request/response handling
- Security: Need to implement security features manually
- Routing: No built-in routing system