DEV Community

Sarwar Hossain
Sarwar Hossain

Posted on

🧭 How to Exclude a Route from Global Prefix & Versioning in NestJS

In NestJS apps, it's common to use global route prefixes and versioning like /api/v1. But sometimes you need a public-facing endpoint — such as a payment gateway callback — that must live outside those constraints.

Here’s how to expose routes like /payment/callback without the global /api/v1 prefix.

🔧 Problem
You're using global prefixing and versioning like this:

main.ts:

app.setGlobalPrefix('api');
app.enableVersioning({
  type: VersioningType.URI,
  defaultVersion: '1',
});

Enter fullscreen mode Exit fullscreen mode

✅ Resulting in routes like:

/api/v1/auth/login

/api/v1/user/profile

❌ But now you need /payment/callback to be exposed at:

http://localhost:3000/payment/callback

Instead of:

http://localhost:3000/api/v1/payment/callback

✅ Solution Overview
You need to do two things:

⛔ Exclude the route from the global prefix

⛔ Mark the route as version-neutral

  1. Exclude the Route from Global Prefix Update main.ts:
import { RequestMethod } from '@nestjs/common';

app.setGlobalPrefix('api', {
  exclude: [{ path: 'payment/callback', method: RequestMethod.GET }],
});

Enter fullscreen mode Exit fullscreen mode

This tells NestJS to ignore prefixing for GET /payment/callback

🚫 No leading slash in path (correct: payment/callback)

  1. Mark Route as Version-Neutral Update your controller:
import { Controller, Get, Version, VERSION_NEUTRAL } from '@nestjs/common';

@Controller('payment')
export class PaymentController {
  @Get('callback')
  @Version(VERSION_NEUTRAL)
  callback() {
    return 'Payment callback hit!';
  }
}

Enter fullscreen mode Exit fullscreen mode

✅ VERSION_NEUTRAL tells NestJS: “do not apply any version prefix”

🧪 Final Result
http://localhost:3000/payment/callback

http://localhost:3000/api/v1/payment/callback

All other routes still work normally under /api/v1.

🧵 Conclusion
NestJS makes it easy to maintain clean API versioning — but when you need an exception like a public payment callback, it only takes

two steps:

  • Exclude the path in setGlobalPrefix()
  • Add @version(VERSION_NEUTRAL) to the controller

Top comments (0)