Quick Start

Install Rust

If you haven't installed Rust yet, you can use the official (rustup)[https://doc.rust-lang.org/book/ch01-01-installation.html] to install Rust.

Tip

The minimum supported Rust version for Salvo is 1.85. Run rustup update to ensure you have a compatible Rust version installed.

Write Your First Salvo Program

Create a new project:

cargo new hello --bin

Add dependencies to Cargo.toml:

[package]
name = "example-hello"
version.workspace = true
edition.workspace = true
publish.workspace = true
rust-version.workspace = true

[dependencies]
salvo = { workspace = true }
tokio = { workspace = true, features = ["macros"] }
tracing.workspace = true
tracing-subscriber.workspace = true

In main.rs, create a simple function handler named hello, which simply prints the text "Hello world".

use salvo::prelude::*;

// Handler for English greeting
#[handler]
async fn hello() -> &'static str {
    "Hello World"
}

// Handler for Chinese greeting
#[handler]
async fn hello_zh() -> Result<&'static str, ()> {
    Ok("你好,世界!")
}

#[tokio::main]
async fn main() {
    // Initialize logging subsystem
    tracing_subscriber::fmt().init();

    // Bind server to port 8698
    let acceptor = TcpListener::new("0.0.0.0:8698").bind().await;

    // Create router with two endpoints:
    // - / (root path) returns English greeting
    // - /你好 returns Chinese greeting
    let router = Router::new()
        .get(hello)
        .push(Router::with_path("你好").get(hello_zh));

    // Print router structure for debugging
    println!("{router:?}");

    // Start serving requests
    Server::new(acceptor).serve(router).await;
}

Congratulations! Your first Salvo program is complete. Just run cargo run in the command line, then open http://127.0.0.1:5800 in your browser.

Detailed Explanation

Here, hello_world is a Handler used to process user requests. The #[handler] attribute allows a function to conveniently implement the Handler trait. Moreover, it enables us to write function parameters in various simplified ways.

  • Original form:

    #[handler]
    async fn hello(_req: &mut Request, _depot: &mut Depot, res: &mut Response, _ctrl: &mut FlowCtrl) {
        res.render("Hello world");
    }
  • You can omit unused parameters in the function. For example, _req, _depot, and _ctrl are not used here and can be omitted entirely:

    #[handler]
    async fn hello(res: &mut Response) {
        res.render("Hello world");
    }
  • Any type can be used as the function's return type as long as it implements the Writer trait. For instance, &str implements Writer, and when returned, it prints plain text:

    #[handler]
    async fn hello(res: &mut Response) -> &'static str {
        "Hello world"
    }
  • More commonly, we need to use Result<T, E> as the return type to handle errors during function execution. If both T and E implement Writer, then Result<T, E> can be used as a return value:

    #[handler]
    async fn hello(res: &mut Response) -> Result<&'static str, ()> {
        Ok("Hello world")
    }

Sleek HTTP3

They say HTTP3 is as agile as a swallow, a dream many programmers have longed for but couldn't achieve. This time, Salvo makes it happen, allowing everyone to effortlessly enjoy the wonderful services brought by HTTP3!

First, enable the HTTP3 feature in Cargo.toml, then modify main.rs as follows:

main.rs
Cargo.toml
use salvo::conn::rustls::{Keycert, RustlsConfig};
use salvo::prelude::*;

// Handler function responding with "Hello World" for HTTP/3 requests
#[handler]
async fn hello() -> &'static str {
    "Hello World"
}

#[tokio::main]
async fn main() {
    // Initialize logging system
    tracing_subscriber::fmt().init();

    // Load TLS certificate and private key from embedded PEM files
    let cert = include_bytes!("../certs/cert.pem").to_vec();
    let key = include_bytes!("../certs/key.pem").to_vec();

    // Create router with single endpoint
    let router = Router::new().get(hello);

    // Configure TLS settings using Rustls
    let config = RustlsConfig::new(Keycert::new().cert(cert.as_slice()).key(key.as_slice()));

    // Create TCP listener with TLS encryption on port 8698
    let listener = TcpListener::new(("0.0.0.0", 8698)).rustls(config.clone());

    // Create QUIC listener and combine with TCP listener
    let acceptor = QuinnListener::new(config.build_quinn_config().unwrap(), ("0.0.0.0", 8698))
        .join(listener)
        .bind()
        .await;

    // Start server supporting both HTTP/3 (QUIC) and HTTPS (TCP)
    Server::new(acceptor).serve(router).await;
}

Salvo CLI Tool 🛠️

Salvo CLI is a tool designed for the Salvo web framework. It helps create clean, readable code, saving your time for more interesting things in life.

If you have ideas for improving the CLI or notice issues that need fixing, don't hesitate! Submit an issue—we welcome your insights.

Step 1

Install the CLI tool:

cargo install salvo-cli

Step 2

Create a new Salvo project using the new command followed by your project name:

salvo new project_name

With this simple CLI tool, you can quickly set up a Salvo project, allowing you to focus on implementing your business logic rather than project structure setup. ✨

More Examples

It's recommended to clone the Salvo repository directly and run the examples in the examples directory. For instance, the following command runs the hello example:

git clone https://github.com/salvo-rs/salvo
cd salvo/examples
cargo run --bin example-hello

There are many examples in the examples directory. You can run them using commands like cargo run --bin example-<name>.