Framework Handler Detection
How Technical Debt Radar identifies HTTP request handlers across NestJS, Express, Fastify, Koa, and Hapi frameworks.
Framework Handler Detection
Runtime risk detection is scope-aware. Radar only flags dangerous patterns (sync I/O, busy-wait loops, unbounded JSON parsing) when they appear inside HTTP request handlers, not in background jobs, cron tasks, or startup code.
This page documents how Radar identifies request handlers for each supported framework.
Handler Scope Rules
Radar classifies function scopes into three categories:
| Scope | Example | Runtime Risk? |
|---|---|---|
| Request handler | @Get() method, app.get() callback | Yes - flagged |
| Request pipeline | Guards, interceptors, middleware, pipes | Yes - flagged |
| Background | @Cron(), @Process(), @OnEvent(), startup code | No - ignored |
The rationale: blocking the event loop in a request handler affects all concurrent requests. The same operation in a background job only affects that job.
NestJS
Handler Detection
Radar detects methods decorated with HTTP method decorators:
// All detected as request handlers
@Get('users')
async getUsers() { /* handler scope */ }
@Post('users')
async createUser() { /* handler scope */ }
@Put('users/:id')
async updateUser() { /* handler scope */ }
@Delete('users/:id')
async deleteUser() { /* handler scope */ }
@Patch('users/:id')
async patchUser() { /* handler scope */ }
Detected decorators: @Get(), @Post(), @Put(), @Delete(), @Patch(), @All(), @Options(), @Head()
Request Pipeline Detection
Methods in guards, interceptors, pipes, and middleware are also treated as handler scope:
// Guard - detected as request pipeline
@Injectable()
export class AuthGuard implements CanActivate {
canActivate(context: ExecutionContext) {
// This scope IS flagged for runtime risks
const token = context.switchToHttp().getRequest().headers.authorization;
return this.validate(token);
}
}
// Interceptor - detected as request pipeline
@Injectable()
export class LoggingInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler) {
// This scope IS flagged
return next.handle();
}
}
// Middleware - detected as request pipeline
@Injectable()
export class CorsMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: NextFunction) {
// This scope IS flagged
next();
}
}
Background Task Detection
Methods with background decorators are excluded from runtime risk analysis:
// NOT flagged - background context
@Cron('0 */6 * * *')
async cleanupExpiredSessions() {
const data = fs.readFileSync('./config.json', 'utf-8'); // Allowed here
}
@Process('email')
async handleEmailJob(job: Job) {
// NOT flagged - BullMQ processor
}
@OnEvent('order.completed')
async onOrderCompleted(event: OrderCompletedEvent) {
// NOT flagged - event handler
}
@Interval(60000)
async healthCheck() {
// NOT flagged - periodic task
}
Background decorators: @Cron(), @Interval(), @Timeout(), @Process(), @OnEvent()
Microservice Handlers
WebSocket and microservice patterns are treated as request handlers:
// Detected as handler
@MessagePattern('user.create')
async createUser(data: CreateUserDto) { /* handler scope */ }
@EventPattern('notification.send')
async sendNotification(data: NotificationDto) { /* handler scope */ }
@SubscribeMessage('chat')
async handleMessage(client: Socket, payload: any) { /* handler scope */ }
Express
Handler Detection
Radar detects route handler callbacks registered on app or router objects:
// All detected as request handlers
app.get('/users', async (req, res) => {
// handler scope
});
app.post('/users', async (req, res) => {
// handler scope
});
router.get('/orders', async (req, res) => {
// handler scope
});
router.put('/orders/:id', async (req, res) => {
// handler scope
});
Detected patterns:
app.get(),app.post(),app.put(),app.delete(),app.patch(),app.all()router.get(),router.post(),router.put(),router.delete(),router.patch()
Middleware Detection
Express middleware functions (three-parameter callbacks) are detected as request pipeline:
// Detected as middleware (request pipeline)
app.use(async (req, res, next) => {
// This scope IS flagged for runtime risks
next();
});
// Error middleware (four params) - also detected
app.use((err, req, res, next) => {
// This scope IS flagged
});
Pattern Matching
Radar uses AST analysis to match the (req, res) and (req, res, next) parameter patterns:
// Detected by parameter pattern
app.get('/data', (req, res) => {
// Two params: req, res → handler
});
app.use((req, res, next) => {
// Three params: req, res, next → middleware
});
// Also detected via named function
function getUsers(req: Request, res: Response) {
// Named handler function
}
app.get('/users', getUsers);
Fastify
Handler Detection
Radar detects Fastify route handlers:
// All detected as request handlers
fastify.get('/users', async (request, reply) => {
// handler scope
});
fastify.post('/users', async (request, reply) => {
// handler scope
});
// Route shorthand
fastify.route({
method: 'GET',
url: '/orders',
handler: async (request, reply) => {
// handler scope
},
});
Detected patterns:
fastify.get(),fastify.post(),fastify.put(),fastify.delete(),fastify.patch()fastify.route({ handler })with thehandlerproperty
Hook Detection
Fastify lifecycle hooks are detected as request pipeline:
// Detected as request pipeline
fastify.addHook('preHandler', async (request, reply) => {
// flagged for runtime risks
});
fastify.addHook('onRequest', async (request, reply) => {
// flagged for runtime risks
});
Plugin Scope
Handlers registered inside plugins are detected through their encapsulation:
fastify.register(async (instance) => {
instance.get('/scoped', async (request, reply) => {
// Detected as handler
});
});
Koa
Handler Detection
Radar detects Koa router handlers and middleware:
// Router handlers - detected
router.get('/users', async (ctx) => {
// handler scope
});
router.post('/users', async (ctx) => {
// handler scope
});
// Koa middleware - detected as request pipeline
app.use(async (ctx, next) => {
// flagged for runtime risks
await next();
});
Detected patterns:
router.get(),router.post(),router.put(),router.delete(),router.patch()app.use()with(ctx)or(ctx, next)parameter pattern
Context Pattern Matching
Radar identifies Koa handlers by the ctx parameter pattern:
// Detected by ctx parameter
router.get('/data', async (ctx) => {
ctx.body = await getData();
});
// Also detected with next parameter
app.use(async (ctx, next) => {
await next();
});
Hapi
Handler Detection
Radar detects Hapi route definitions via server.route():
// Detected as request handler
server.route({
method: 'GET',
path: '/users',
handler: async (request, h) => {
// handler scope
return h.response(users);
},
});
// Multiple routes
server.route([
{
method: 'POST',
path: '/users',
handler: async (request, h) => {
// handler scope
},
},
{
method: 'GET',
path: '/users/{id}',
handler: async (request, h) => {
// handler scope
},
},
]);
Detected patterns:
server.route({ handler })--- thehandlerproperty function is treated as handler scopeserver.route([...])--- each route object'shandleris detected
Plugin Handlers
Handlers registered in Hapi plugins are also detected:
const plugin = {
name: 'users',
register: async (server) => {
server.route({
method: 'GET',
path: '/users',
handler: async (request, h) => {
// Detected as handler
},
});
},
};
Lifecycle Extensions
Hapi lifecycle extensions are treated as request pipeline:
server.ext('onPreHandler', async (request, h) => {
// Detected as request pipeline
return h.continue;
});
Detection Heuristics
When Radar cannot definitively identify a framework pattern, it falls back to heuristic matching:
-
Function name heuristics: Functions named
handle*,on*,process*,execute*,controller*,handler*,endpoint*,route*are treated as potential handlers. -
Parameter pattern heuristics: Functions with
(req, res),(request, response),(ctx), or(request, h)parameters are treated as likely handlers. -
File path heuristics: Files in
controllers/,routes/,handlers/, ormiddleware/directories receive extra scrutiny.
These heuristics have lower confidence than decorator/AST-based detection and produce medium confidence violations rather than high.