App
The application crate is where most of your code lives.
In particular, it's where you define your Blueprint
.
Note
As a convention, the application crate is named app
.
You can use {project_name}_app
if you need to disambiguate between
multiple Pavex applications in the same workspace.
Blueprint
Every Pavex project has, at its core, a Blueprint
.
It's the type you use to declare the structure of your API:
routes, middlewares, constructors, error handlers, error observers, configuration, etc.
// [...]
pub fn blueprint() -> Blueprint {
let mut bp = Blueprint::new();
ApiKit::new().register(&mut bp);
telemetry::register(&mut bp);
configuration::register(&mut bp);
routes::register(&mut bp);
bp
}
Think of a Blueprint
as the specification for your API, a plan for how your application should behave at
runtime.
But you can't run or execute a Blueprint
as-is. That's where code generation comes in.
Code generation
To convert a Blueprint
into an executable toolkit, you need pavex generate
.
It's a CLI command that takes a Blueprint
as input and outputs a
Rust crate, the server SDK for your Pavex project.
cargo-px
If you went through the Quickstart tutorial, you might be
wondering: I've never run pavex generate
! How comes my project worked?
That's thanks to cargo-px
!
If you look into the Cargo.toml
manifest for the server_sdk
crate in the demo
project,
you'll find this section:
# [...]
[package.metadata.px.generate]
generator_type = "cargo_workspace_binary"
generator_name = "bp"
It's a cargo-px
configuration section.
The server_sdk
crate is telling cargo-px
to generate the whole crate
by executing a binary called bp
(short for blueprint
) from the current Cargo workspace.
That binary is defined in the demo
crate:
use app::blueprint;
use cargo_px_env::generated_pkg_manifest_path;
use pavex_cli_client::Client;
use std::env::args;
use std::error::Error;
/// Generate the `server_sdk` crate using Pavex's CLI.
///
/// Pavex will automatically wire all our routes, constructors and error handlers
/// into a "server SDK" that can be used by the final API server binary to launch
/// the application.
///
/// If `--check` is passed as an argument, it only verifies that the server SDK
/// crate is up-to-date. An error is returned if it isn't.
fn main() -> Result<(), Box<dyn Error>> {
let generated_dir = generated_pkg_manifest_path()?.parent().unwrap().into();
let mut cmd = Client::new().generate(blueprint(), generated_dir);
if args().any(|arg| arg == "--check") {
cmd = cmd.check()
};
if let Err(e) = cmd.execute() {
eprintln!("{e}");
std::process::exit(1);
}
Ok(())
}
Client::generate
takes care of serializing the Blueprint
and passing it as input to pavex generate
.
All this is done automatically for you when you run cargo px build
or cargo px run
.
cargo-px
examines all the crates in your workspace, generates the ones
that need it, and then goes on to complete the build process.