@frontman-ai/nextjs
v0.6.2
Published
Next.js integration for Frontman - AI-powered development tools and observability
Readme
@frontman-ai/nextjs
Next.js integration for Frontman - provides development tools and observability for Next.js applications.
Installation
Quick Install (Recommended)
The fastest way to install Frontman is using our CLI installer:
npx @frontman-ai/nextjs install
# Or with a custom server host
npx @frontman-ai/nextjs install --server frontman.company.comThe installer will:
- Detect your Next.js version (15 or 16+)
- Create the appropriate middleware/proxy file
- Set up OpenTelemetry instrumentation
- Configure everything to connect to your Frontman server
CLI Options
npx @frontman-ai/nextjs install [options]
Options:
--server <host> Frontman server host (default: api.frontman.sh)
--prefix <path> Target directory (default: current directory)
--dry-run Preview changes without writing files
--skip-deps Skip dependency installation
--help Show help messageManual Installation
If you prefer to set things up manually or need to integrate with an existing configuration:
npm install @frontman-ai/nextjsThen follow the Manual Setup instructions below.
Quick Start
After running the installer, you're ready to go! Start your Next.js dev server:
npm run devThen open your browser to http://localhost:3000/frontman to access the Frontman UI.
Manual Setup
Next.js 15 (middleware.ts)
Create middleware.ts in your project root:
import { createMiddleware } from '@frontman-ai/nextjs';
import { NextRequest, NextResponse } from 'next/server';
const frontman = createMiddleware({
host: 'api.frontman.sh', // or 'frontman.local:4000' for local development
});
export async function middleware(req: NextRequest) {
const response = await frontman(req);
if (response) return response;
return NextResponse.next();
}
export const config = {
matcher: ['/frontman', '/frontman/:path*'],
};Next.js 16+ (proxy.ts)
Create proxy.ts in your project root:
import { createMiddleware } from '@frontman-ai/nextjs';
import { NextRequest, NextResponse } from 'next/server';
const frontman = createMiddleware({
host: 'api.frontman.sh', // or 'frontman.local:4000' for local development
});
export function proxy(req: NextRequest): NextResponse | Promise<NextResponse> {
if (req.nextUrl.pathname === '/frontman' || req.nextUrl.pathname.startsWith('/frontman/')) {
return frontman(req) || NextResponse.next();
}
return NextResponse.next();
}
export const config = {
matcher: ['/frontman', '/frontman/:path*'],
};OpenTelemetry Setup (Recommended)
Create instrumentation.ts in your project root (or src/ if you use that directory):
export async function register() {
if (process.env.NEXT_RUNTIME === 'nodejs') {
const { NodeSDK } = await import('@opentelemetry/sdk-node');
const { setup } = await import('@frontman-ai/nextjs/Instrumentation');
const [logProcessor, spanProcessor] = setup();
new NodeSDK({
logRecordProcessors: [logProcessor],
spanProcessors: [spanProcessor],
}).start();
}
}That's it! Frontman will now:
- Capture console logs, build output, and errors
- Track Next.js HTTP requests, API routes, and rendering
- Make all logs available via the Frontman UI at
/frontman
Adding to Existing Files
If you already have middleware.ts, proxy.ts, or instrumentation.ts files, the installer will show you manual integration steps. Here's how to add Frontman to existing files:
Existing middleware.ts (Next.js 15)
import { createMiddleware } from '@frontman-ai/nextjs';
import { NextRequest, NextResponse } from 'next/server';
// ... your other imports
const frontman = createMiddleware({
host: 'api.frontman.sh', // or 'frontman.local:4000' for local development
});
export async function middleware(req: NextRequest) {
// Add Frontman handler first - it will handle /frontman/* routes
const response = await frontman(req);
if (response) return response;
// ... your existing middleware logic below
return NextResponse.next();
}
export const config = {
// Add Frontman matcher alongside your existing matchers
matcher: ['/frontman', '/frontman/:path*', '/your-other-routes/:path*'],
};Existing proxy.ts (Next.js 16+)
import { createMiddleware } from '@frontman-ai/nextjs';
import { NextRequest, NextResponse } from 'next/server';
// ... your other imports
const frontman = createMiddleware({
host: 'api.frontman.sh', // or 'frontman.local:4000' for local development
});
export function proxy(req: NextRequest): NextResponse | Promise<NextResponse> {
// Add Frontman handler first - it will handle /frontman/* routes
if (req.nextUrl.pathname === '/frontman' || req.nextUrl.pathname.startsWith('/frontman/')) {
return frontman(req) || NextResponse.next();
}
// ... your existing proxy logic below
return NextResponse.next();
}
export const config = {
// Add Frontman matcher alongside your existing matchers
matcher: ['/frontman', '/frontman/:path*', '/your-other-routes/:path*'],
};Existing instrumentation.ts
If you don't have OpenTelemetry set up yet:
export async function register() {
if (process.env.NEXT_RUNTIME === 'nodejs') {
const { NodeSDK } = await import('@opentelemetry/sdk-node');
const { setup } = await import('@frontman-ai/nextjs/Instrumentation');
const [logProcessor, spanProcessor] = setup();
new NodeSDK({
logRecordProcessors: [logProcessor],
spanProcessors: [spanProcessor],
}).start();
}
// ... your existing instrumentation logic
}If you already have OpenTelemetry set up, add the Frontman processors to your existing configuration:
export async function register() {
if (process.env.NEXT_RUNTIME === 'nodejs') {
const { setup } = await import('@frontman-ai/nextjs/Instrumentation');
const [logProcessor, spanProcessor] = setup();
new NodeSDK({
// ... your existing OTEL config
logRecordProcessors: [logProcessor, ...yourExistingLogProcessors],
spanProcessors: [spanProcessor, ...yourExistingSpanProcessors],
}).start();
}
}What Gets Captured
Automatic Console Patching (Node.js only)
LogCapture automatically initializes when the module is imported and patches:
- All console methods:
console.log(),console.error(),console.warn(),console.info(),console.debug() process.stdout.write()for build output (webpack/turbopack compilation messages)process.on('uncaughtException')for unhandled errorsprocess.on('unhandledRejection')for unhandled promise rejections
Browser environments are automatically skipped - no console patching occurs in the browser.
Via OpenTelemetry Spans (Optional)
When you set up instrumentation.ts:
- HTTP requests (
BaseServer.handleRequest) - Route rendering (
AppRender.getBodyResult) - API route execution (
AppRouteRouteHandlers.runHandler) - Request method, path, status code, duration
Storage & Cross-Context Sharing
All captured data is stored in a circular buffer (1024 entries by default) using a globalThis singleton pattern. This ensures logs are shared across Next.js/Turbopack execution contexts:
- Instrumentation context (startup)
- Page render context
- API route context
- Middleware context (Edge runtime - read-only)
The buffer persists for the lifetime of the Node.js process and is accessible through the Frontman UI and get_logs tool.
Configuration Options
Middleware Options
createMiddleware({
host: string, // Frontman server host (required) - the client UI connects here via WebSocket
basePath: string, // Base path for Frontman routes (default: "frontman")
serverName: string, // Server name (default: "frontman-nextjs")
serverVersion: string, // Server version (default: package version)
clientUrl: string, // Custom client bundle URL
clientCssUrl: string, // Custom client CSS URL
entrypointUrl: string, // Custom entrypoint URL
isLightTheme: boolean, // Use light theme (default: false)
projectRoot: string, // Project root directory (default: process.cwd())
})Understanding the host Option
The host option specifies the Frontman server that the client UI will connect to for AI capabilities. When you visit /frontman in your Next.js app:
- The middleware serves the Frontman UI HTML
- The UI loads the client JavaScript with
?host=<your-host> - The client establishes a WebSocket connection to
wss://<host>/socket - AI interactions and tool calls flow through this connection
The middleware itself doesn't connect to the Frontman server - it only passes the host to the client.
Examples:
- Production:
host: 'api.frontman.sh'→ client connects towss://api.frontman.sh/socket - Local dev:
host: 'frontman.local:4000'→ client connects towss://frontman.local:4000/socket
Supported Next.js Versions
| Version | Middleware File | Status |
|---------|----------------|--------|
| Next.js 15.x | middleware.ts | Fully supported |
| Next.js 16.x | proxy.ts | Fully supported |
Both versions have built-in OpenTelemetry support with no additional configuration required.
Architecture
Next.js App (Turbopack/Webpack)
│
├─> Module Import (first context - instrumentation)
│ └─> LogCapture auto-initializes at module level
│ ├─> Creates globalThis.__FRONTMAN_INSTANCE__
│ ├─> Patches console.log/warn/error/info/debug
│ ├─> Intercepts process.stdout.write
│ └─> Listens to uncaughtException/unhandledRejection
│
├─> Module Import (second context - page render)
│ └─> LogCapture reuses existing globalThis.__FRONTMAN_INSTANCE__
│ └─> Same buffer, no re-patching (guarded by __FRONTMAN_CONSOLE_PATCHED__ flag)
│
├─> instrumentation.ts (startup) - OPTIONAL
│ └─> setup() returns OTEL processors that write to same buffer
│
├─> middleware.ts / proxy.ts (per-request)
│ └─> Serves Frontman UI at /frontman
│ └─> Connects to Frontman server for AI tools
│
└─> OpenTelemetry SDK (optional)
├─> LogRecordProcessor → globalThis.__FRONTMAN_INSTANCE__.buffer
└─> SpanProcessor → globalThis.__FRONTMAN_INSTANCE__.bufferKey Technical Details
Cross-Context Buffer Sharing
- Next.js 15+ with Turbopack runs code in multiple isolated contexts
globalThis.__FRONTMAN_INSTANCE__stores the singleton buffer instance- All contexts read/write to the same circular buffer
- Console patching happens only once (protected by
__FRONTMAN_CONSOLE_PATCHED__flag)
Circular Buffer
- Fixed capacity: 1024 entries (configurable)
- Oldest entries automatically evicted when full
- Entries include: timestamp, level, message, attributes, consoleMethod
- Thread-safe for concurrent writes from different contexts
Advanced Usage
Custom OTEL Configuration
If you need more control over OpenTelemetry setup:
import { setup } from '@frontman-ai/nextjs/Instrumentation';
import { NodeSDK } from '@opentelemetry/sdk-node';
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
export async function register() {
if (process.env.NEXT_RUNTIME === 'nodejs') {
const [logProcessor, spanProcessor] = setup();
new NodeSDK({
serviceName: 'my-app',
resource: resourceFromAttributes({
'service.version': '1.0.0',
}),
traceExporter: new OTLPTraceExporter({
url: process.env.OTEL_EXPORTER_OTLP_ENDPOINT,
}),
logRecordProcessors: [logProcessor],
spanProcessors: [spanProcessor],
}).start();
}
}Without OpenTelemetry
Frontman works without OpenTelemetry! If you only set up middleware (skip instrumentation.ts):
- Console logs are still captured (auto-initialized at module import)
- Build output is tracked
- Errors are logged
- Frontman UI available at
/frontman - HTTP spans are not captured (requires OTEL)
LogCapture auto-initializes when the module is imported, so console patching happens automatically in Node.js environments - no explicit initialization needed.
Custom LogCapture Configuration
You can customize the buffer size and stdout patterns:
import { initialize } from '@frontman-ai/nextjs/LogCapture';
// Call this BEFORE any console.log() calls (e.g., in instrumentation.ts)
initialize({
bufferCapacity: 2048, // Default: 1024
stdoutPatterns: ['webpack', 'turbopack', 'Compiled', 'Failed', 'custom-pattern'],
});Note: Configuration only takes effect on the first call. Subsequent calls are ignored because the singleton instance is already created.
Troubleshooting
Logs not being captured
Check 1: Verify module is imported LogCapture only initializes when the module is imported in a Node.js context. Make sure either:
- You have
instrumentation.tsthat imports from@frontman-ai/nextjs/Instrumentation - OR you have
middleware.tsthat imports from@frontman-ai/nextjs
Check 2: Verify Node.js runtime LogCapture doesn't run in browser or Edge runtime. Check your environment:
console.log('Runtime:', process.env.NEXT_RUNTIME); // Should be 'nodejs'Check 3: Verify buffer contents Query the buffer directly to see if logs are present:
import { getLogs } from '@frontman-ai/nextjs/LogCapture';
const allLogs = getLogs();
console.log('Buffer contains', allLogs.length, 'logs');Check 4: Multiple contexts In Next.js 15+, code may run in different contexts. Verify all contexts share the same buffer:
console.log('Instance:', globalThis.__FRONTMAN_INSTANCE__);
console.log('Buffer size:', globalThis.__FRONTMAN_INSTANCE__?.buffer.contents.items.length);Console logs appear twice
This is normal behavior - LogCapture captures logs AND calls the original console method so logs still appear in your terminal/browser console.
Build output not captured
By default, only these patterns are captured from process.stdout:
- "webpack"
- "turbopack"
- "Compiled"
- "Failed"
To capture additional patterns, use custom configuration (see above).
Installer shows "manual modification required"
This happens when you have existing middleware, proxy, or instrumentation files that don't already include Frontman. The installer won't overwrite your existing code. Follow the Adding to Existing Files instructions to manually integrate Frontman.
API
createMiddleware(options)
Creates a Next.js middleware handler that serves the Frontman UI and connects to your Frontman server.
import { createMiddleware } from '@frontman-ai/nextjs';
const middleware = createMiddleware({
host: string, // Frontman server host (required)
basePath: string, // Base path (default: "frontman")
serverName: string, // Server name (default: "frontman-nextjs")
serverVersion: string, // Version (default: package version)
projectRoot: string, // Project root (default: process.cwd())
});Returns: (request: NextRequest) => Promise<NextResponse | undefined>
setup()
Initializes LogCapture (console patching, error handlers) and returns OTEL processors for use with OpenTelemetry SDK.
import { setup } from '@frontman-ai/nextjs/Instrumentation';
const [logProcessor, spanProcessor] = setup();Returns: [LogRecordProcessor, SpanProcessor]
initialize(config?)
Manually initialize LogCapture with custom configuration. Usually not needed since auto-initialization happens at module import.
import { initialize } from '@frontman-ai/nextjs/LogCapture';
initialize({
bufferCapacity: number, // Buffer size (default: 1024)
stdoutPatterns: string[], // Patterns to capture from stdout
});Returns: void
getLogs(options?)
Query the log buffer with optional filters.
import { getLogs } from '@frontman-ai/nextjs/LogCapture';
const logs = getLogs({
pattern: string, // Regex pattern to match messages (case-insensitive)
level: 'console' | 'build' | 'error', // Filter by log level
since: number, // Unix timestamp - only logs after this time
tail: number, // Limit to last N logs
});Returns: LogEntry[]
LogEntry type:
type LogEntry = {
timestamp: string; // ISO 8601 timestamp
level: 'console' | 'build' | 'error'; // Log level
message: string; // Log message (ANSI codes stripped)
attributes?: Record<string, any>; // Additional attributes
resource?: Record<string, any>; // Resource info
consoleMethod?: 'log' | 'info' | 'warn' | 'error' | 'debug'; // Original console method
};License
MIT
