Domain guards
A domain guard restricts a group of routes to a specific domain.
With domain guards you can serve multiple websites and/or APIs from the same Pavex application.
use pavex::blueprint::router::GET;
use pavex::blueprint::Blueprint;
use pavex::f;
pub fn bp() -> Blueprint {
let mut bp = Blueprint::new();
// Serve the website from the root domain...
bp.domain("pavex.dev").nest(website_bp());
// ...while reserving a subdomain for the REST API.
bp.domain("api.pavex.dev").prefix("/v1").nest(api_bp());
bp
}
fn website_bp() -> Blueprint {
let mut bp = Blueprint::new();
bp.route(GET, "/", f!(super::index));
// Other web pages...
bp
}
fn api_bp() -> Blueprint {
let mut bp = Blueprint::new();
bp.route(GET, "/users", f!(super::users));
// Other API routes...
bp
}
Static guards
The simplest case is a static domain, a domain guard that matches a single, predetermined domain:
use pavex::blueprint::router::GET;
use pavex::blueprint::Blueprint;
use pavex::f;
pub fn bp() -> Blueprint {
let mut bp = Blueprint::new();
bp.domain("pavex.dev").nest(pavex_bp());
bp
}
fn pavex_bp() -> Blueprint {
let mut bp = Blueprint::new();
bp.route(GET, "/", f!(super::index));
// Other routes...
bp
}
It will only match requests to pavex.dev
.
It won't match, for example, requests to api.pavex.dev
or www.pavex.dev
.
Domain parameters
If your needs are more complex, you can make your domain guards dynamic:
use pavex::blueprint::router::GET;
use pavex::blueprint::Blueprint;
use pavex::f;
pub fn bp() -> Blueprint {
let mut bp = Blueprint::new();
bp.domain("{sub}.pavex.dev").nest(sub_bp());
bp
}
fn sub_bp() -> Blueprint {
let mut bp = Blueprint::new();
bp.route(GET, "/", f!(super::index));
// Other routes...
bp
}
{sub}
is a domain parameter.
It matches everything before .pavex.dev
, up to the previous .
or the beginning of the domain.
It matches, for example, api.pavex.dev
and ui.pavex.dev
. It won't match admin.api.pavex.dev
or pavex.dev
though!
You can have multiple domain parameters in the same domain guard, as long as they are separated by a .
:
use pavex::blueprint::router::GET;
use pavex::blueprint::Blueprint;
use pavex::f;
pub fn bp() -> Blueprint {
let mut bp = Blueprint::new();
bp.domain("{user_id}.{tenant_id}.pavex.dev").nest(user_bp());
bp
}
fn user_bp() -> Blueprint {
let mut bp = Blueprint::new();
bp.route(GET, "/", f!(super::index));
// Other routes...
bp
}
Catch-all parameters
Normal domain parameters are limited to a single DNS label—i.e. they stop at the previous .
or at the end of the domain.
You can use the *
character to craft a catch-all domain parameter. It matches the rest of the domain, regardless of its contents:
use pavex::blueprint::router::GET;
use pavex::blueprint::Blueprint;
use pavex::f;
pub fn bp() -> Blueprint {
let mut bp = Blueprint::new();
bp.domain("{*any}.example.dev").nest(sub_bp());
bp
}
fn sub_bp() -> Blueprint {
let mut bp = Blueprint::new();
bp.route(GET, "/", f!(super::handler));
// Other routes...
bp
}
{*any}
matches everything before example.dev
, even if it contains .
separators.
{*any}.example.dev
matches, for example, api.example.dev
and ui.example.dev
, but it also matches admin.api.example.dev
.
To avoid ambiguity, you can have at most one catch-all parameter in a domain guard and it must be located at the very beginning of the domain.
Domain detection
The domain requested by the client is determined using the Host
header.
If the header is not present, none of your domain guards will match.
Security
The Host
header can be easily spoofed by the client.
You shouldn't rely on domain guards for auth or other security-sensitive checks.
Restrictions
Domain guards are an all-or-nothing deal.
Either you specify a domain guard for all routes in a blueprint, or you don't specify any at all.
We recommend specifying domain guards at the very top, clearly partitioning your routes according to the domain they should be served on, as shown in the very first example for this guide.
The only exception to this rule are fallbacks: you can register a top-level fallback that will be invoked when no domain guard matches.
use pavex::blueprint::Blueprint;
use pavex::f;
pub fn bp() -> Blueprint {
let mut bp = Blueprint::new();
bp.domain("pavex.dev").nest(website_bp());
bp.domain("api.pavex.dev").prefix("/v1").nest(api_bp());
// If no domain matches, serve a 404 page.
bp.fallback(f!(super::unknown_domain));
bp
}
Absolute form
Pavex doesn't make a distinction between absolute and relative domain names.
If there a single trailing .
at the end of a domain name, it will be stripped. For example,
Pavex treats pavex.dev
and pavex.dev.
as the same domain.