Skip to content

URL

@voltage/url generates URLs from controller classes. Point it at a controller and an action method, pass any route parameters, and it returns the correct path. Routes are read directly from @Controller() and route decorator metadata — you do not register anything manually.

Terminal window
yarn add @voltage/url

Call forRoot() once in your root module:

import { URLModule } from '@voltage/url';
@Module({
imports: [URLModule.forRoot()],
})
export class AppModule {}

The module is global — inject URLGenerator anywhere without importing the module again:

import { URLGenerator } from '@voltage/url';
@Injectable()
export class AuthService {
constructor(private readonly url: URLGenerator) {}
async loginUrl() {
return this.url.generate(AuthController, 'login');
}
}

generate() accepts a controller class and an optional action name. The action is typed to only accept method names on the controller — passing a non-existent method is a compile error:

// Controller root path
await this.url.generate(AuthController);
// → "/auth"
// Specific action
await this.url.generate(AuthController, 'login');
// → "/auth/login"

Pass params to substitute :param placeholders. Undefined values are omitted from the path:

@Controller('account/verify/:id')
export class VerifyController {
@Get() view() {}
@Post('resend') resend() {}
}
await this.url.generate(VerifyController, { params: { id: token.id } });
// → "/account/verify/abc-123"
await this.url.generate(VerifyController, 'resend', { params: { id: token.id } });
// → "/account/verify/abc-123/resend"

Pass query to append a query string. Undefined values are omitted:

await this.url.generate(HotelController, 'list', {
query: { page: 2, city: 'Berlin' },
});
// → "/hotels/list?page=2&city=Berlin"
// Full URL with host
await this.url.generate(AuthController, 'login', {
host: 'https://app.example.com',
});
// → "https://app.example.com/auth/login"
// Prefix and suffix
await this.url.generate(AuthController, 'login', {
prefix: '/v2',
suffix: '.json',
});
// → "/v2/auth/login.json"
interface GenerateOptions {
/** Route parameters substituted into `:param` placeholders. Undefined values are omitted. */
params?: Record<string, string | number | boolean | undefined>;
/** Query string parameters. Undefined values are omitted. */
query?: Record<string, string | number | boolean | undefined>;
/** Prepended before the route path. */
prefix?: string;
/** Appended after the route path. */
suffix?: string;
/** Full host prepended to the URL, e.g. `'https://app.example.com'`. */
host?: string;
}

Before assembling the final URL, URLGenerator emits a GenerateEvent. Listeners can modify event.options — this is how @voltage/intl injects locale prefixes transparently. You can use the same mechanism to add custom logic, such as always prepending an API base path for a specific controller:

import { OnEvent } from '@voltage/event-manager';
import { GenerateEvent } from '@voltage/url';
@Injectable()
export class ApiPrefixListener {
@OnEvent(GenerateEvent)
onGenerate(event: GenerateEvent) {
if (event.controller === ApiController) {
event.options.prefix = '/api/v2';
}
}
}