Reference

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:

ScopeExampleRuntime Risk?
Request handler@Get() method, app.get() callbackYes - flagged
Request pipelineGuards, interceptors, middleware, pipesYes - flagged
Background@Cron(), @Process(), @OnEvent(), startup codeNo - 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 the handler property

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 }) --- the handler property function is treated as handler scope
  • server.route([...]) --- each route object's handler is 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:

  1. Function name heuristics: Functions named handle*, on*, process*, execute*, controller*, handler*, endpoint*, route* are treated as potential handlers.

  2. Parameter pattern heuristics: Functions with (req, res), (request, response), (ctx), or (request, h) parameters are treated as likely handlers.

  3. File path heuristics: Files in controllers/, routes/, handlers/, or middleware/ directories receive extra scrutiny.

These heuristics have lower confidence than decorator/AST-based detection and produce medium confidence violations rather than high.

Technical Debt Radar Documentation