@flink-app/debug-plugin
v0.12.1-alpha.45
Published
Flink plugin that make it possbile to debug requests
Readme
Debug Plugin
A Flink plugin for debugging HTTP requests and responses in your application. Capture and inspect request/response data, enable/disable debugging on demand, and integrate with the Management API for admin panel access.
Installation
Install the plugin to your Flink app project:
npm install @flink-app/debug-pluginConfiguration
Basic Setup
Configure the plugin in your app startup:
import { FlinkApp } from "@flink-app/flink";
import { debugPlugin } from "@flink-app/debug-plugin";
function start() {
new FlinkApp<AppContext>({
name: "My app",
plugins: [
debugPlugin({
enabledAtStart: false, // Start with debugging disabled
logToConsole: false, // Don't log to console
keepLogs: 100 // Keep last 100 requests
})
],
}).start();
}Plugin Options:
interface StaticOptions {
logToConsole: boolean; // Log requests/responses to console
enabledAtStart: boolean; // Enable debugging when app starts
keepLogs?: number; // Number of requests to keep in memory (default: 100)
}TypeScript Setup
The debug plugin doesn't require adding types to your context, but it does add properties to ctx.plugins.debugPlugin:
ctx.plugins.debugPlugin: {
requests: request[]; // Array of captured requests
enabled: boolean; // Current enabled state
}How It Works
The debug plugin uses Express middleware to:
- Intercept incoming HTTP requests
- Capture request details (method, path, body, headers)
- Intercept outgoing responses by wrapping
res.writeandres.end - Store request/response pairs in memory
- Keep a rolling window of the most recent N requests
What's Captured:
- Request method (GET, POST, etc.)
- Request path and query parameters
- Request headers
- Request body
- Response body
- Request start and end timestamps
What's NOT Captured:
- Management API requests (routes starting with
/managementapi) - Requests when debugging is disabled
Accessing Debug Data
From Handlers
Access captured requests directly from context:
import { Handler } from "@flink-app/flink";
import { Ctx } from "../Ctx";
const GetDebugInfo: Handler<Ctx, any, any> = async ({ ctx, req }) => {
const debugData = ctx.plugins.debugPlugin;
return {
data: {
enabled: debugData.enabled,
requestCount: debugData.requests.length,
recentRequests: debugData.requests.slice(0, 10)
}
};
};
export default GetDebugInfo;Enabling/Disabling Debug Mode
Toggle debug mode programmatically:
// Enable debugging
ctx.plugins.debugPlugin.enabled = true;
// Disable debugging
ctx.plugins.debugPlugin.enabled = false;
// Clear captured requests
ctx.plugins.debugPlugin.requests = [];Management API Integration
The debug plugin can be exposed as a Management API module for easy access from admin panels:
Setup Management Module
import { FlinkApp } from "@flink-app/flink";
import { Ctx } from "./Ctx";
import {
debugPlugin,
GetManagementModule as GetDebugManagementModule
} from "@flink-app/debug-plugin";
import { managementApiPlugin } from "@flink-app/management-api-plugin";
const debugManagementModule = GetDebugManagementModule({
ui: true,
uiSettings: {
title: "Debug Console"
}
});
function start() {
new FlinkApp<Ctx>({
name: "My flink app",
debug: true,
db: {
uri: "mongodb://localhost:27017/my-flink-app"
},
plugins: [
debugPlugin({
enabledAtStart: false,
logToConsole: false,
keepLogs: 100
}),
managementApiPlugin({
token: process.env.MGMT_TOKEN!,
jwtSecret: process.env.JWT_SECRET!,
modules: [debugManagementModule]
})
]
}).start();
}
start();Management Module Options:
interface GetManagementModuleConfig {
pluginId?: string; // Default: "debug"
ui: boolean; // Enable UI in admin panel
uiSettings?: {
title: string; // Module title (default: "Debug")
};
}Management Endpoints
When using the Management Module, these endpoints are registered under /managementapi/{pluginId}:
GET /managementapi/debug/
Get debug status and captured requests.
Response:
{
data: {
enabled: boolean; // Current debug state
requests: request[]; // Array of captured requests
}
}Example:
curl http://localhost:3000/managementapi/debug/POST /managementapi/debug/enable
Enable debug mode.
Response:
{
data: {
enabled: true
}
}Example:
curl -X POST http://localhost:3000/managementapi/debug/enablePOST /managementapi/debug/disable
Disable debug mode.
Response:
{
data: {
enabled: false
}
}Example:
curl -X POST http://localhost:3000/managementapi/debug/disableAPI Reference
Request Type
Each captured request has the following structure:
interface request {
start: Date; // When request started
end?: Date; // When response finished
method: string; // HTTP method (GET, POST, etc.)
path: string; // Request path with query params
headers?: IncomingHttpHeaders; // Request headers
body?: any; // Parsed request body
response?: string; // Response body as string
}Context Properties
ctx.plugins.debugPlugin: {
requests: request[]; // Array of captured requests (newest first)
enabled: boolean; // Whether debugging is currently enabled
}Complete Example
Here's a complete example showing how to use the debug plugin:
import { FlinkApp } from "@flink-app/flink";
import { Ctx } from "./Ctx";
import {
debugPlugin,
GetManagementModule as GetDebugManagementModule
} from "@flink-app/debug-plugin";
import { managementApiPlugin } from "@flink-app/management-api-plugin";
// Create debug management module
const debugManagementModule = GetDebugManagementModule({
ui: true,
uiSettings: {
title: "Request Debugger"
}
});
function start() {
new FlinkApp<Ctx>({
name: "My API",
debug: true,
db: {
uri: process.env.MONGODB_URI!
},
plugins: [
// Debug plugin - starts disabled, keeps last 200 requests
debugPlugin({
enabledAtStart: false,
logToConsole: true, // Also log to console
keepLogs: 200
}),
// Management API with debug module
managementApiPlugin({
token: process.env.MGMT_TOKEN!,
jwtSecret: process.env.JWT_SECRET!,
modules: [debugManagementModule]
})
]
}).start();
}
start();Custom Debug Handler
Create a custom handler to analyze debug data:
import { Handler } from "@flink-app/flink";
import { Ctx } from "../Ctx";
interface DebugStatsResponse {
totalRequests: number;
methodCounts: { [method: string]: number };
slowestRequests: Array<{
path: string;
duration: number;
}>;
errorResponses: number;
}
const GetDebugStats: Handler<Ctx, any, DebugStatsResponse> = async ({ ctx }) => {
const requests = ctx.plugins.debugPlugin.requests;
// Count requests by method
const methodCounts: { [method: string]: number } = {};
for (const req of requests) {
methodCounts[req.method] = (methodCounts[req.method] || 0) + 1;
}
// Find slowest requests
const slowestRequests = requests
.filter(req => req.end)
.map(req => ({
path: req.path,
duration: req.end!.getTime() - req.start.getTime()
}))
.sort((a, b) => b.duration - a.duration)
.slice(0, 10);
// Count error responses (5xx, 4xx)
const errorResponses = requests.filter(req => {
if (!req.response) return false;
try {
const response = JSON.parse(req.response);
return response.status >= 400;
} catch {
return false;
}
}).length;
return {
data: {
totalRequests: requests.length,
methodCounts,
slowestRequests,
errorResponses
}
};
};
export default GetDebugStats;Use Cases
1. Development Debugging
Enable debug mode during development to inspect requests:
debugPlugin({
enabledAtStart: true,
logToConsole: true,
keepLogs: 50
})2. Production Issue Investigation
Keep debug mode off by default, enable on demand when investigating:
debugPlugin({
enabledAtStart: false,
logToConsole: false,
keepLogs: 200 // Keep more logs for analysis
})Then enable via Management API when needed.
3. API Testing
Use debug data to verify request/response pairs:
// Make test request
await fetch("http://localhost:3000/api/users", {
method: "POST",
body: JSON.stringify({ name: "John" })
});
// Check debug data
const debugData = ctx.plugins.debugPlugin.requests[0];
expect(debugData.body).toEqual({ name: "John" });
expect(JSON.parse(debugData.response!)).toHaveProperty("id");4. Performance Monitoring
Track request durations to identify slow endpoints:
const slowRequests = ctx.plugins.debugPlugin.requests
.filter(req => req.end)
.filter(req => {
const duration = req.end!.getTime() - req.start.getTime();
return duration > 1000; // Slower than 1 second
});
console.log(`Found ${slowRequests.length} slow requests`);Best Practices
Memory Management
The plugin keeps requests in memory. Be careful with settings:
// Good for development
keepLogs: 50
// Good for temporary production debugging
keepLogs: 200
// Be careful - could use significant memory
keepLogs: 10000 // Avoid in productionEach request object includes:
- Headers (can be large)
- Request body (can be large)
- Response body (can be large)
Security Considerations
- Disable in Production: Don't enable debug mode in production by default
- Sensitive Data: Be aware that debug logs capture request bodies and headers
- Access Control: Use Management API with proper authentication
- Clear Logs: Regularly clear captured requests to avoid memory leaks
// Clear debug logs periodically
setInterval(() => {
if (ctx.plugins.debugPlugin.requests.length > 500) {
ctx.plugins.debugPlugin.requests = [];
}
}, 60000); // Every minuteConsole Logging
Use logToConsole: true carefully:
// Development - useful
debugPlugin({
logToConsole: true,
enabledAtStart: true
})
// Production - avoid (too noisy)
debugPlugin({
logToConsole: false,
enabledAtStart: false
})Filtering Sensitive Data
Consider wrapping the plugin to filter sensitive data:
// In your handler
const sanitizedRequests = ctx.plugins.debugPlugin.requests.map(req => ({
...req,
headers: {
...req.headers,
authorization: req.headers?.authorization ? "[REDACTED]" : undefined
},
body: req.body?.password ? { ...req.body, password: "[REDACTED]" } : req.body
}));
return { data: { requests: sanitizedRequests } };Implementation Details
Middleware Order
The debug plugin middleware is registered during plugin initialization and runs for all requests except:
- Routes starting with
/managementapi(to avoid recursion) - Requests when
enabledisfalse
Response Interception
The plugin intercepts responses by:
- Saving original
res.writeandres.endfunctions - Replacing them with custom implementations
- Collecting response chunks in a buffer
- Restoring original functions after response completes
This ensures the plugin captures the full response without affecting normal operation.
Rolling Window
When the request array exceeds keepLogs:
requests = requests.splice(0, keep);This keeps only the most recent N requests, with newest first (unshift).
Troubleshooting
Debug Mode Not Working
Check enabled state:
console.log(ctx.plugins.debugPlugin.enabled);Verify plugin initialization:
console.log(app.ctx.plugins.debugPlugin); // Should existCheck Management API routes:
# Should work curl http://localhost:3000/managementapi/debug/
Requests Not Being Captured
Enable debug mode:
curl -X POST http://localhost:3000/managementapi/debug/enableCheck if route is excluded:
- Management API routes are not captured
- Verify your route doesn't start with
/managementapi
Check keepLogs setting:
- If you have many requests, older ones get removed
- Increase
keepLogsvalue
Memory Usage High
Reduce keepLogs:
keepLogs: 50 // Keep fewer logsClear logs periodically:
ctx.plugins.debugPlugin.requests = [];Disable when not needed:
curl -X POST http://localhost:3000/managementapi/debug/disable
Console Too Noisy
Set logToConsole: false:
debugPlugin({
enabledAtStart: true,
logToConsole: false, // No console output
keepLogs: 100
})Notes
- The plugin automatically excludes Management API routes to avoid recursion
- Request bodies are captured after body-parser middleware processes them
- Response bodies are captured as strings (includes JSON, HTML, etc.)
- The plugin adds minimal performance overhead when disabled
- Debug data is stored in memory only (not persisted to database)
- The
enabledflag can be toggled at runtime without restarting the app
