LoginSign up
GitHub

You can use Lightfast agents with Hono, a fast and lightweight web framework that provides excellent Web API compatibility.

Installation

Install Hono:

npm install hono
# or
yarn add hono
# or
pnpm add hono

Examples

The examples start a Hono 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.

Full example: Available in our examples repository

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(),
    framework: 'hono'
  })
});

// Create memory adapter
const memory = new RedisMemory({
  url: process.env.REDIS_URL!,
  token: process.env.REDIS_TOKEN!
});

Basic Integration

Hono's native Web API support makes integration extremely simple:

import { Hono } from 'hono';
import { fetchRequestHandler } from 'lightfast/server/adapters/fetch';

const app = new Hono();

app.post('/agents/:sessionId', async (c) => {
  // Hono provides native Web API Request/Response support!
  return fetchRequestHandler({
    agent,
    sessionId: c.req.param('sessionId'),
    memory,
    req: c.req.raw, // Direct Web API Request
    resourceId: c.get('userId') || 'anonymous',
    createRequestContext: (req) => ({
      userAgent: req.headers.get('user-agent'),
      hono: true,
      runtime: c.env?.runtime || 'unknown'
    })
  });
  // Returns Web API Response - Hono handles it natively
});

export default {
  port: 8080,
  fetch: app.fetch,
};

Advanced Integration with Middleware

import { Hono } from 'hono';
import { cors } from 'hono/cors';
import { rateLimiter } from 'hono/rate-limiter';
import { logger } from 'hono/logger';
import { jwt } from 'hono/jwt';
import { fetchRequestHandler } from 'lightfast/server/adapters/fetch';

type Bindings = {
  REDIS_URL: string;
  REDIS_TOKEN: string;
  JWT_SECRET: string;
}

type Variables = {
  userId: string;
  userRole: string;
}

const app = new Hono<{ Bindings: Bindings; Variables: Variables }>();

// Global middleware
app.use('*', logger());
app.use('*', cors({
  origin: ['http://localhost:3000', 'https://myapp.com'],
  allowHeaders: ['Content-Type', 'Authorization'],
  allowMethods: ['POST', 'GET', 'OPTIONS'],
}));

// Rate limiting
app.use('/agents/*', rateLimiter({
  windowMs: 15 * 60 * 1000, // 15 minutes
  limit: 100, // limit each IP to 100 requests per windowMs
  keyGenerator: (c) => c.req.header('x-forwarded-for') ?? 'anonymous',
}));

// JWT Authentication middleware
app.use('/agents/*', jwt({
  secret: (c) => c.env.JWT_SECRET,
}), async (c, next) => {
  const payload = c.get('jwtPayload');
  c.set('userId', payload.sub);
  c.set('userRole', payload.role || 'user');
  await next();
});

// Agent endpoint with authentication
app.post('/agents/:sessionId', async (c) => {
  const userId = c.get('userId');
  const userRole = c.get('userRole');
  
  return fetchRequestHandler({
    agent,
    sessionId: c.req.param('sessionId'),
    memory,
    req: c.req.raw,
    resourceId: userId,
    createRequestContext: (req) => ({
      userAgent: req.headers.get('user-agent'),
      userRole,
      hono: true,
      cloudflare: !!c.env?.CF_RAY,
      timestamp: Date.now()
    }),
    onError: ({ error }) => {
      console.error(`Agent error for user ${userId}:`, error);
    }
  });
});

// Health check
app.get('/health', (c) => {
  return c.json({ 
    status: 'ok', 
    timestamp: Date.now(),
    runtime: c.env?.runtime || 'unknown'
  });
});

// Session management endpoints
app.get('/agents/:sessionId/history', async (c) => {
  const userId = c.get('userId');
  const sessionId = c.req.param('sessionId');
  
  try {
    // Verify session ownership
    const session = await memory.getSession(sessionId);
    if (!session || session.resourceId !== userId) {
      return c.json({ error: 'Session not found' }, 404);
    }
    
    const messages = await memory.getMessages(sessionId);
    return c.json({ messages });
  } catch (error) {
    console.error('History retrieval error:', error);
    return c.json({ error: 'Failed to retrieve history' }, 500);
  }
});

app.delete('/agents/:sessionId', async (c) => {
  const userId = c.get('userId');
  const sessionId = c.req.param('sessionId');
  
  try {
    // Verify session ownership
    const session = await memory.getSession(sessionId);
    if (!session || session.resourceId !== userId) {
      return c.json({ error: 'Session not found' }, 404);
    }
    
    await memory.deleteSession(sessionId);
    return c.body(null, 204);
  } catch (error) {
    console.error('Session deletion error:', error);
    return c.json({ error: 'Failed to delete session' }, 500);
  }
});

// Error handling
app.onError((err, c) => {
  console.error('Hono error:', err);
  return c.json({ 
    error: 'Internal Server Error',
    message: process.env.NODE_ENV === 'development' ? err.message : undefined
  }, 500);
});

// 404 handler
app.notFound((c) => {
  return c.json({ error: 'Not Found' }, 404);
});

export default app;

Multi-Runtime Support

Hono works across multiple runtimes. Here are examples for different deployment targets:

Node.js

// server.ts
import { serve } from '@hono/node-server';
import app from './app';

const port = process.env.PORT ? parseInt(process.env.PORT) : 8080;

console.log(`Server is running on port ${port}`);

serve({
  fetch: app.fetch,
  port,
});

Cloudflare Workers

// worker.ts
import app from './app';

export default {
  fetch: app.fetch,
} satisfies ExportedHandler<Env>;

Bun

// server.ts
import app from './app';

export default {
  port: 8080,
  fetch: app.fetch,
};

Deno

// server.ts
import app from './app';

Deno.serve({ port: 8080 }, app.fetch);

Middleware Examples

Custom Authentication

import { createMiddleware } from 'hono/factory';

const customAuth = createMiddleware<{
  Variables: {
    userId: string;
    apiKey: string;
  }
}>(async (c, next) => {
  const apiKey = c.req.header('x-api-key');
  
  if (!apiKey) {
    return c.json({ error: 'Missing API key' }, 401);
  }
  
  // Validate API key (implement your logic)
  const userId = await validateApiKey(apiKey);
  if (!userId) {
    return c.json({ error: 'Invalid API key' }, 401);
  }
  
  c.set('userId', userId);
  c.set('apiKey', apiKey);
  await next();
});

app.use('/agents/*', customAuth);

Request Logging

const requestLogger = createMiddleware(async (c, next) => {
  const start = Date.now();
  const requestId = crypto.randomUUID();
  
  console.log(`[${requestId}] ${c.req.method} ${c.req.url} - Start`);
  
  await next();
  
  const duration = Date.now() - start;
  console.log(`[${requestId}] ${c.req.method} ${c.req.url} - ${c.res.status} (${duration}ms)`);
});

app.use('*', requestLogger);

Error Tracking

const errorTracking = createMiddleware(async (c, next) => {
  try {
    await next();
  } catch (error) {
    // Send to error tracking service
    await trackError(error, {
      url: c.req.url,
      method: c.req.method,
      userId: c.get('userId'),
      timestamp: Date.now()
    });
    
    throw error; // Re-throw for default error handler
  }
});

app.use('*', errorTracking);

Testing

// test/agents.test.ts
import { describe, it, expect } from 'vitest';
import app from '../src/app';

describe('Agents API', () => {
  it('should handle chat requests', async () => {
    const req = new Request('http://localhost/agents/test-session', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': 'Bearer valid-token'
      },
      body: JSON.stringify({
        messages: [
          { role: 'user', content: 'Hello' }
        ]
      })
    });

    const res = await app.fetch(req);
    expect(res.status).toBe(200);
    expect(res.headers.get('content-type')).toMatch(/text\/plain/);
  });

  it('should require authentication', async () => {
    const req = new Request('http://localhost/agents/test-session', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        messages: [{ role: 'user', content: 'Hello' }]
      })
    });

    const res = await app.fetch(req);
    expect(res.status).toBe(401);
  });
});

Environment Configuration

// config.ts
import { z } from 'zod';

const envSchema = z.object({
  REDIS_URL: z.string(),
  REDIS_TOKEN: z.string(),
  JWT_SECRET: z.string(),
  OPENAI_API_KEY: z.string(),
  NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),
  PORT: z.string().transform(Number).default('8080'),
});

export const env = envSchema.parse(process.env);

Performance Optimizations

Streaming with Compression

import { compress } from 'hono/compress';

app.use('*', compress({
  threshold: 1024, // Only compress responses larger than 1KB
}));

Caching Headers

app.get('/agents/:sessionId/history', async (c) => {
  const history = await getSessionHistory(c.req.param('sessionId'));
  
  // Cache for 5 minutes
  c.header('Cache-Control', 'public, max-age=300');
  c.header('ETag', generateETag(history));
  
  return c.json({ messages: history });
});

Advantages

  • Web API Native: Perfect compatibility with fetchRequestHandler
  • Multi-Runtime: Works on Node.js, Cloudflare Workers, Bun, Deno
  • Performance: Extremely fast and lightweight
  • TypeScript: Excellent TypeScript support with type inference
  • Minimal Setup: Very simple integration with Lightfast

Disadvantages

  • Newer Framework: Smaller ecosystem compared to Express
  • Learning Curve: Different patterns from traditional Express apps
  • Documentation: Less third-party tutorials and examples

Deployment Examples

Cloudflare Workers

// wrangler.toml
name = "lightfast-agent"
main = "src/worker.ts"
compatibility_date = "2024-01-01"

[vars]
NODE_ENV = "production"

[secrets]
REDIS_URL = "..."
REDIS_TOKEN = "..."
JWT_SECRET = "..."
OPENAI_API_KEY = "..."

Vercel

{
  "functions": {
    "api/**/*.ts": {
      "runtime": "@vercel/node"
    }
  }
}

Hono provides the best integration experience with Lightfast due to its native Web API compatibility and multi-runtime support.