Flink plugin that make it possbile to debug requests
npm install @flink-app/debug-pluginA 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.
Install the plugin to your Flink app project:
``bash`
npm install @flink-app/debug-plugin
Configure the plugin in your app startup:
`typescript
import { FlinkApp } from "@flink-app/flink";
import { debugPlugin } from "@flink-app/debug-plugin";
function start() {
new FlinkApp
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:
`typescript`
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)
}
The debug plugin doesn't require adding types to your context, but it does add properties to ctx.plugins.debugPlugin:
`typescript`
ctx.plugins.debugPlugin: {
requests: request[]; // Array of captured requests
enabled: boolean; // Current enabled state
}
The debug plugin uses Express middleware to:
1. Intercept incoming HTTP requests
2. Capture request details (method, path, body, headers)
3. Intercept outgoing responses by wrapping res.write and res.end
4. Store request/response pairs in memory
5. 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
Access captured requests directly from context:
`typescript
import { Handler } from "@flink-app/flink";
import { Ctx } from "../Ctx";
const GetDebugInfo: Handler
const debugData = ctx.plugins.debugPlugin;
return {
data: {
enabled: debugData.enabled,
requestCount: debugData.requests.length,
recentRequests: debugData.requests.slice(0, 10)
}
};
};
export default GetDebugInfo;
`
Toggle debug mode programmatically:
`typescript
// Enable debugging
ctx.plugins.debugPlugin.enabled = true;
// Disable debugging
ctx.plugins.debugPlugin.enabled = false;
// Clear captured requests
ctx.plugins.debugPlugin.requests = [];
`
The debug plugin can be exposed as a Management API module for easy access from admin panels:
`typescript
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
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:
`typescript`
interface GetManagementModuleConfig {
pluginId?: string; // Default: "debug"
ui: boolean; // Enable UI in admin panel
uiSettings?: {
title: string; // Module title (default: "Debug")
};
}
When using the Management Module, these endpoints are registered under /managementapi/{pluginId}:
#### GET /managementapi/debug/
Get debug status and captured requests.
Response:
`typescript`
{
data: {
enabled: boolean; // Current debug state
requests: request[]; // Array of captured requests
}
}
Example:
`bash`
curl http://localhost:3000/managementapi/debug/
#### POST /managementapi/debug/enable
Enable debug mode.
Response:
`typescript`
{
data: {
enabled: true
}
}
Example:
`bash`
curl -X POST http://localhost:3000/managementapi/debug/enable
#### POST /managementapi/debug/disable
Disable debug mode.
Response:
`typescript`
{
data: {
enabled: false
}
}
Example:
`bash`
curl -X POST http://localhost:3000/managementapi/debug/disable
Each captured request has the following structure:
`typescript`
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
}
`typescript`
ctx.plugins.debugPlugin: {
requests: request[]; // Array of captured requests (newest first)
enabled: boolean; // Whether debugging is currently enabled
}
Here's a complete example showing how to use the debug plugin:
`typescript
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
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();
`
Create a custom handler to analyze debug data:
`typescript
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
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;
`
Enable debug mode during development to inspect requests:
`typescript`
debugPlugin({
enabledAtStart: true,
logToConsole: true,
keepLogs: 50
})
Keep debug mode off by default, enable on demand when investigating:
`typescript`
debugPlugin({
enabledAtStart: false,
logToConsole: false,
keepLogs: 200 // Keep more logs for analysis
})
Then enable via Management API when needed.
Use debug data to verify request/response pairs:
`typescript
// 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");
`
Track request durations to identify slow endpoints:
`typescript
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);`
The plugin keeps requests in memory. Be careful with settings:
`typescript
// Good for development
keepLogs: 50
// Good for temporary production debugging
keepLogs: 200
// Be careful - could use significant memory
keepLogs: 10000 // Avoid in production
`
Each request object includes:
- Headers (can be large)
- Request body (can be large)
- Response body (can be large)
1. Disable in Production: Don't enable debug mode in production by default
2. Sensitive Data: Be aware that debug logs capture request bodies and headers
3. Access Control: Use Management API with proper authentication
4. Clear Logs: Regularly clear captured requests to avoid memory leaks
`typescript`
// Clear debug logs periodically
setInterval(() => {
if (ctx.plugins.debugPlugin.requests.length > 500) {
ctx.plugins.debugPlugin.requests = [];
}
}, 60000); // Every minute
Use logToConsole: true carefully:
`typescript
// Development - useful
debugPlugin({
logToConsole: true,
enabledAtStart: true
})
// Production - avoid (too noisy)
debugPlugin({
logToConsole: false,
enabledAtStart: false
})
`
Consider wrapping the plugin to filter sensitive data:
`typescript
// 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 } };
`
The debug plugin middleware is registered during plugin initialization and runs for all requests except:
- Routes starting with /managementapi (to avoid recursion)enabled
- Requests when is false
The plugin intercepts responses by:
1. Saving original res.write and res.end functions
2. Replacing them with custom implementations
3. Collecting response chunks in a buffer
4. Restoring original functions after response completes
This ensures the plugin captures the full response without affecting normal operation.
When the request array exceeds keepLogs:`typescript`
requests = requests.splice(0, keep);
This keeps only the most recent N requests, with newest first (unshift).
1. Check enabled state:
`typescript`
console.log(ctx.plugins.debugPlugin.enabled);
2. Verify plugin initialization:
`typescript`
console.log(app.ctx.plugins.debugPlugin); // Should exist
3. Check Management API routes:
`bash`
# Should work
curl http://localhost:3000/managementapi/debug/
1. Enable debug mode:
`bash`
curl -X POST http://localhost:3000/managementapi/debug/enable
2. Check if route is excluded:
- Management API routes are not captured
- Verify your route doesn't start with /managementapi
3. Check keepLogs setting:
- If you have many requests, older ones get removed
- Increase keepLogs value
1. Reduce keepLogs:
`typescript`
keepLogs: 50 // Keep fewer logs
2. Clear logs periodically:
`typescript`
ctx.plugins.debugPlugin.requests = [];
3. Disable when not needed:
`bash`
curl -X POST http://localhost:3000/managementapi/debug/disable
Set logToConsole: false:
`typescript`
debugPlugin({
enabledAtStart: true,
logToConsole: false, // No console output
keepLogs: 100
})
- 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 enabled` flag can be toggled at runtime without restarting the app