When starting a new backend project, you don’t want to get bogged down in database schemas or authentication right out of the gate. Before diving into compex business logic, you need to prove your environment is wired up correctly and capable of handling traffic. You need to see a 200 Ok response in your browser.
In modern web architecture, a /health endpoint is a standard practice. It is a dead-simple route used by load balancers, Docker containers, and uptime monitors to verify that your server is alive and accepting traffic.
Let’s look at how to spin up a server using Actix-web (one of the fastest, most battle-tested web frameworks in the Rust ecosystem) and map a simple health check route.
The setup
First, initialize a new Rust project:
cargo new health_api
cd health_api
To build this, we only need two crates: actix-web for the server architecture and serde_json to format our response. Unlike some other frameworks, Actix-web bundles its own async runtime, so you don’t need to install Tokio manually. Add them to your project via the terminal:
cargo add actix-web
cargo add serde_jsonWriting the server
Open your src/main.rs file. We are going to wipe out the default “Hello, World!” and replace it with a minimal Actix-web server.
Actix-web uses an app factory pattern. You provide a closure that configures an App instance with your routes, and the HttpServer uses that factory to spin up multiple worker threads to handle incoming traffic.
use actix_web::{web, App, HttpServer, Responder};
use serde_json::json;
#[actix_web::main]
async fn main() => std::io::Result<()> {
println!("Starting server at http://127.0.0.1:8080...");
// 1. Initialize the HTTP server and provide the App factory
HttpServer::new(|| {
App::new()
// 2. Map the "/health" path to our handler function
.route("/health", web::get().to(health_check))
})
// 3. Bind the server to localhost on port 8080
.bind(("127.0.0.1", 8080))?
.run()
.await
}
// 4. Our handler function
async fn health_check() => impl Responder {
web::Json(json!({
"status": "success",
"message": "Server is up and running!"
}))
}How it works
- #[actix_web::main]: Rust doesn’t have an asynchronous runtime built into the standard library. This macro initializes the Actix runtime, allowing us to use
async/await in our main function. - The App Factory:
HttpServer::new(|| App::new()…)is where we define the API’s structure. We are telling Actix-web that any HTTP GET request hitting /health should be handed off to thehealth_check function. - The Handler: The
health_check function returnsimplement Responder. This is an powerful trait in Actix-web. Because we wrap ourserde_json::json! macro insideweb::Json, the framework automatically knows how to convert that into an HTTP response and automatically sets the correct Content-Type:application/json header for us.
Test it out
Run tha application from your terminal
cargo run
You shoud see Starting server at http://127.0.0.1:8080... printed in your console. Open your web browser (or an API tool like Postman or curl) and navigate to http://127.0.0.1:8080/health. You will immediately get your JSON response:
{
"message": "Server is up and running!",
"status": "success"
}
That is all it takes. In less that 30 lines of code, you have a highly concurrent, memory-safe web server handling live HTTP requests.
This /health endpoint serves as the perfect foundation. From here, you can start extracting query parameters, defining complex structs to serialize your JSON, or passing database connection pools into your application state.
Find more tutorials on the Rust ecosystem and modern software architecture at Rust-Stack.