Express Integration

FOON provides first-class Express.js support with automatic request transformation.

Installation

npm install foon-sdk

No additional packages needed - Express integration is included.

Basic Setup

createFonRouter()

Create a router that automatically transforms request bodies:

import express from 'express';
import { createFonRouter } from 'foon-sdk/express';
import { OpenAIProvider } from 'foon-sdk';

const app = express();
app.use(express.json());

const fonRouter = createFonRouter({
  provider: new OpenAIProvider({
    apiKey: process.env.OPENAI_API_KEY,
    model: 'gpt-5-nano'
  }),
  prefix: '/foon',
  confidenceThreshold: 0.85
});

// Mount the router
app.use(fonRouter.getRouter());

app.listen(3000);

Defining Routes

With Schema

Register routes that transform incoming data to your schema:

const customerSchema = {
  type: 'object',
  properties: {
    name: {
      type: 'object',
      properties: {
        given: { type: 'string' },
        family: { type: 'string' }
      },
      required: ['given', 'family']
    },
    email: { type: 'string', format: 'email' }
  },
  required: ['name', 'email']
};

fonRouter.post('/users', {
  schema: customerSchema,
  handler: (req, res) => {
    // req.body is already transformed and validated!
    res.json({ success: true, user: req.body });
  }
});

Dual Routes

FOON automatically creates two routes for each registration:

RouteDescription
POST /usersOriginal route (untransformed)
POST /foon/usersFOON route (transformed)

Requests to FOON routes:

  • Transform req.body using the configured schema
  • Replace req.body with validated output
  • Add trace ID header (X-FON-Trace-Id)
  • Forward to the same handler

Router Configuration

interface FonRouterConfig {
  provider: Provider;              // LLM provider (required)
  prefix?: string;                 // Route prefix (default: '/foon')
  methods?: HttpMethod[];          // Methods to transform (default: ['POST', 'PUT', 'PATCH'])
  confidenceThreshold?: number;    // Confidence threshold (default: 0.85)
  cache?: Cache;                   // Cache instance
  security?: SecurityOptions;      // Security options
  traceHeader?: string;            // Trace header name (default: 'X-FON-Trace-Id')
  onError?: ErrorHandler;          // Custom error handler
  createOriginalRoutes?: boolean;  // Create original routes (default: true)
  verbose?: boolean;               // Verbose logging (default: false)
}

Route Configuration

interface RouteConfig {
  schema: object;                   // JSON Schema for this route
  handler: RequestHandler;          // Express handler
  createOriginal?: boolean;         // Override: create original route
  confidenceThreshold?: number;     // Override: confidence threshold for this route
}

Error Handling

Default Error Handler

When transformation fails, the default error handler returns:

{
  "error": "CONFIDENCE_TOO_LOW",
  "message": "1 assignment(s) below confidence threshold 0.85",
  "traceId": "uuid",
  "details": { ... }
}

HTTP status codes:

  • 400 - Bad Request (validation error, confidence too low)
  • 413 - Payload Too Large (security limits exceeded)
  • 500 - Internal Server Error (execution error)
  • 502 - Bad Gateway (provider error)

Custom Error Handler

const fonRouter = createFonRouter({
  provider,
  onError: (error, req, res, next) => {
    res.status(400).json({
      error: error.category,
      message: error.message,
      traceId: error.traceId,
      trace: req.fonTrace
    });
  }
});

Configuration Examples

Disable Original Routes

const fonRouter = createFonRouter({
  provider,
  createOriginalRoutes: false  // Only create FOON routes
});

// Now only /foon/users exists, not /users

Custom Prefix

const fonRouter = createFonRouter({
  provider,
  prefix: '/api/semantic'
});

fonRouter.post('/users', { schema, handler });
// Creates: POST /api/semantic/users

Per-Route Configuration

fonRouter.post('/users', {
  schema: userSchema,
  handler: createUserHandler,
  createOriginal: false,       // Don't create /users for this route
  confidenceThreshold: 0.9     // Higher threshold for this route
});

Trace Headers

FOON routes automatically add headers to responses:

  • X-FON-Trace-Id: Unique trace ID for debugging
  • X-FON-Timing-Total: Total processing time (verbose mode)
  • X-FON-Timing-Proposal: LLM call time (verbose mode)
  • X-FON-Cache-Hit: Whether cache was hit (verbose mode)

Accessing Trace Data

The trace is attached to the request object:

fonRouter.post('/users', {
  schema: userSchema,
  handler: (req, res) => {
    const trace = (req as any).fonTrace;
    console.log('Confidence:', trace.confidenceSummary);
    console.log('Timings:', trace.timings);
    res.json(req.body);
  }
});

With Cache

import { createFonRouter } from 'foon-sdk/express';
import { OpenAIProvider, LRUCache } from 'foon-sdk';

const cache = new LRUCache({ max: 100, ttl: 3600000 });

const fonRouter = createFonRouter({
  provider: new OpenAIProvider({
    apiKey: process.env.OPENAI_API_KEY,
    model: 'gpt-5-nano'
  }),
  cache
});

With Security Options

const fonRouter = createFonRouter({
  provider,
  security: {
    maxInputSize: 1024 * 1024,
    maxDepth: 10,
    maxKeys: 1000,
    redactKeys: ['password', 'token'],
    sanitizePrompt: true
  }
});