Quick Start

Install Rust

If you haven't installed Rust yet, you can install it using the officially provided rustup.

Official Rust TutorialDetailed instructions on how to install Rust
TIP

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

Writing Your First Salvo Program

Create a new project:

cargo new hello --bin

Add dependencies to Cargo.toml:

hello/Cargo.toml
[package]
name = "example-hello"
version = "0.1.0"
edition = "2024"

[dependencies]
salvo = { version = "0.77.1" }
tokio = { version = "1", features = ["macros"] }
tracing = "0.1"
tracing-subscriber = "0.3"

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

hello/src/main.rs
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 5800
    let acceptor = TcpListener::new("0.0.0.0:5800").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 your terminal, 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] macro allows a function to easily implement the Handler trait. Furthermore, it allows us to abbreviate the function parameters in different 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 parameters that are not used in the function, like _req, _depot, and _ctrl here, which can be simply left out:

    #[handler]
    async fn hello(res: &mut Response) {
        res.render("Hello world");
    }
  • Any type can be used as the return type of the function, as long as it implements the Writer trait. For example, &str implements Writer. When used as a return value, it renders 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 the return value:

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

Awesome HTTP3

HTTP3 is renowned for its speed and efficiency, a feature many developers have eagerly anticipated. Now, Salvo makes it easy to harness the power of HTTP3 and enjoy its benefits!

First, enable the HTTP3 feature in Cargo.toml, then modify main.rs like this:

main.rs
Cargo.toml
hello-h3/src/main.rs
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 5800
  let listener = TcpListener::new(("0.0.0.0", 5800)).rustls(config.clone());

  // Create QUIC listener and combine with TCP listener
  let acceptor = QuinnListener::new(config.build_quinn_config().unwrap(), ("0.0.0.0", 5800))
      .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 you time for more interesting things in life.

If you have ideas for improving the CLI or find any issues that need fixing, please 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

You can quickly start your Salvo project with this simple CLI tool, allowing you to focus on implementing your business logic instead of setting up the project structure. ✨

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>.