Build or update the BlueBubbles external channel plugin for Moltbot (extension package, REST...
npx skills add salvo-rs/salvo-skills --skill "salvo-basic-app"
Install specific skill from multi-skill repository
# Description
Create basic Salvo web applications with handlers, routers, and server setup. Use when starting a new Salvo project or adding basic HTTP endpoints.
# SKILL.md
name: salvo-basic-app
description: Create basic Salvo web applications with handlers, routers, and server setup. Use when starting a new Salvo project or adding basic HTTP endpoints.
Salvo Basic Application Setup
This skill helps create basic Salvo web applications with proper structure and best practices.
Core Concepts
Handler
Handlers process HTTP requests. Use the #[handler] macro on async functions:
use salvo::prelude::*;
#[handler]
async fn hello() -> &'static str {
"Hello World"
}
#[handler]
async fn greet(req: &mut Request) -> String {
let name = req.query::<String>("name").unwrap_or("World".to_string());
format!("Hello, {}!", name)
}
Handler parameters can be in any order and are all optional:
- req: &mut Request - HTTP request object
- res: &mut Response - HTTP response object
- depot: &mut Depot - Request-scoped data storage
- ctrl: &mut FlowCtrl - Flow control for middleware
Router
Routers define URL paths and attach handlers:
use salvo::prelude::*;
let router = Router::new()
.get(hello)
.push(Router::with_path("greet").get(greet));
Server Setup
Basic server configuration:
use salvo::prelude::*;
#[handler]
async fn hello() -> &'static str {
"Hello World"
}
#[tokio::main]
async fn main() {
let router = Router::new().get(hello);
let acceptor = TcpListener::new("0.0.0.0:8080").bind().await;
Server::new(acceptor).serve(router).await;
}
Response Types
Returning Different Types
Handlers can return any type implementing Writer or Scribe:
use salvo::prelude::*;
#[handler]
async fn json_response() -> Json<serde_json::Value> {
Json(serde_json::json!({"status": "ok"}))
}
#[handler]
async fn text_response() -> &'static str {
"Plain text response"
}
#[handler]
async fn html_response(res: &mut Response) {
res.render(salvo::writing::Html("<h1>Hello</h1>"));
}
#[handler]
async fn status_response() -> StatusCode {
StatusCode::NO_CONTENT
}
#[handler]
async fn redirect_response(res: &mut Response) {
res.render(salvo::writing::Redirect::found("https://example.com"));
}
Rendering JSON
use salvo::prelude::*;
use serde::Serialize;
#[derive(Serialize)]
struct User {
name: String,
age: u8,
}
#[handler]
async fn get_user() -> Json<User> {
Json(User {
name: "Alice".to_string(),
age: 30,
})
}
Error Handling
Return Result<T, E> where both implement Writer:
use salvo::prelude::*;
#[handler]
async fn may_fail() -> Result<Json<Data>, StatusError> {
let data = fetch_data().await.map_err(|_| StatusError::internal_server_error())?;
Ok(Json(data))
}
Request Object
Common Request Methods
#[handler]
async fn handle_request(req: &mut Request) -> String {
// Get request method
let method = req.method();
// Get request URI
let uri = req.uri();
// Get header value
if let Some(content_type) = req.header::<String>("Content-Type") {
println!("Content-Type: {}", content_type);
}
// Get query parameter
let name = req.query::<String>("name").unwrap_or_default();
// Get path parameter (requires route like /users/{id})
let id = req.param::<i64>("id").unwrap();
// Parse JSON body
let body: UserData = req.parse_json().await.unwrap();
format!("Processed request")
}
Response Object
Common Response Methods
use salvo::prelude::*;
#[handler]
async fn handle_response(res: &mut Response) {
// Set status code
res.status_code(StatusCode::CREATED);
// Set response header
res.headers_mut().insert("X-Custom-Header", "value".parse().unwrap());
// Render text response
res.render("Hello, World!");
}
Dependencies
Add to Cargo.toml:
[dependencies]
salvo = "1.88.1"
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
Best Practices
- Use
#[handler]macro for all handlers - Keep handlers focused on single responsibility
- Use appropriate return types (Json, Text, StatusCode)
- Handle errors with Result types
- Use
TcpListenerfor basic HTTP servers - Extract common logic into middleware using
hoop()
# Supported AI Coding Agents
This skill is compatible with the SKILL.md standard and works with all major AI coding agents:
Learn more about the SKILL.md standard and how to use these skills with your preferred AI coding agent.