Search
🦀

260524_1718_axum hello_world

Axum Hello World

cargo build

async@awaits-MacBook-Air 260412_1712_axum_helloworld % cargo build

cargo run

async@awaits-MacBook-Air 260412_1712_axum_helloworld % cargo run

Explain

A minimal HTTP web server built with Axum 0.8 and Tokio. The server exposes a single route that responds with plain text: Hello, World!.

Overview

This project demonstrates the smallest useful Axum application:
One GET / route
Plain-text response body
Async request handling on the Tokio runtime
No shared application state, middleware, or database
Axum sits on top of Hyper and Tower. You define routes with a Router, attach handlers, bind a TCP listener, and serve requests with axum::serve.

Requirements

Tool
Version used
Notes
Rust
1.84+
Edition 2021
Cargo
1.84+
Bundled with Rust
OS
Any
macOS, Linux, and Windows
No external services (database, Redis, etc.) are required.

Project Structure

260412_1712_axum_helloworld/ ├── Cargo.toml # Crate metadata and dependencies ├── README.md # This document └── src/ └── main.rs # Application entry point and route handlers
Plain Text
복사

Dependencies

Defined in Cargo.toml:
Crate
Version
Role
axum
0.8
Web framework: routing, handlers, HTTP serving
tokio
1.x
Async runtime (full features: I/O, macros, multi-thread scheduler)
Axum pulls in Hyper, Tower, and HTTP types transitively. You do not need to add them explicitly for this example.

How It Works

1. Route definition

let app = Router::new().route("/", get(hello_world));
Rust
복사
Router::new() creates an empty router.
.route("/", get(...)) registers GET / and maps it to the hello_world handler.
Handlers are async functions. Axum runs them on the Tokio runtime.

2. Handler

async fn hello_world() -> &'static str { "Hello, World!" }
Rust
복사
Returning &'static str tells Axum to send a 200 OK response with Content-Type: text/plain; charset=utf-8 and the string as the body. No manual response building is required for this simple case.

3. TCP listener

let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await?;
Rust
복사
The server listens on port 3000 on all network interfaces (0.0.0.0). Use 127.0.0.1:3000 if you only want local access.

4. HTTP server

axum::serve(listener, app).await?;
Rust
복사
axum::serve accepts connections on the listener and dispatches HTTP requests to the router. It supports HTTP/1.1 and HTTP/2 (when enabled via features).

Request flow

Client (browser/curl) │ ▼ TcpListener (port 3000) │ ▼ axum::serve │ ▼ Router ──► GET / ──► hello_world() ──► "Hello, World!"
Plain Text
복사

Build and Run

From the project root:
cd /Users/async/glory/Schedule/code/200518_0157_rs/axum/260412_1712_axum_helloworld cargo run
Bash
복사
First run downloads and compiles dependencies; later runs are faster.
Expected console output:
Server listening on <http://localhost:3000>
Plain Text
복사
Press Ctrl+C to stop the server.

Release build (optional)

For optimized binaries:
cargo run --release
Bash
복사

Verify the Server

With the server running, in another terminal:
curl <http://localhost:3000>
Bash
복사
Expected response:
Hello, World!
Plain Text
복사
You can also open http://localhost:3000 in a browser; the same text should appear.

HTTP details

Item
Value
Method
GET
Path
/
Status
200 OK
Body
Hello, World!
Content-Type
text/plain; charset=utf-8 (inferred by Axum)
Other paths (e.g. /foo) return 404 Not Found because no route is registered for them.

Configuration

Setting
Location
Default value
Listen address
src/main.rs
0.0.0.0:3000
Axum version
Cargo.toml
0.8
Tokio features
Cargo.toml
full
To change the port, edit the LISTEN_ADDR constant in src/main.rs and update the println! message if desired.

Common Commands

Command
Description
cargo run
Build and run in debug mode
cargo build
Compile without running
cargo build --release
Optimized build
cargo check
Type-check without full link
cargo clean
Remove target/ build artifacts

Troubleshooting

Address already in use

If port 3000 is taken:
Error: failed to bind TCP listener
Plain Text
복사
Fix: Stop the other process using port 3000, or change LISTEN_ADDR to another port (e.g. 0.0.0.0:8080).
On macOS/Linux, find the process:
lsof -i :3000
Bash
복사

Cannot connect from another machine

Binding to 127.0.0.1 only accepts local connections. This project uses 0.0.0.0, which accepts remote connections if your firewall allows it.

Compile errors after upgrading Axum

Axum 0.8 changed path parameter syntax from :name to {name}. This project has no path parameters, so upgrades are straightforward. See the Axum 0.8 announcement for breaking changes when extending the app.

Next Steps

Ideas for extending this server:
1.
More routes — Add GET /health for health checks.
2.
JSON responses — Return Json(...) with serde for structured APIs.
3.
Path parameters — Use /hello/{name} (Axum 0.8 syntax).
4.
Middleware — Logging, CORS, or compression via Tower layers.
5.
Shared stateRouter::with_state for database pools or config.
6.
Testsaxum::Router can be tested with tower::ServiceExt without binding a real port.

References

License

This example is provided as-is for learning purposes. Add a license file if you plan to distribute or publish the crate.

axum/260412_1712_axum_helloworld/src/main.rs

// `use` brings names from external crates into the current scope. // The curly braces `{ ... }` list multiple items from the same path (a "use group"). // - `Router`: Axum's central type for mapping HTTP paths to handler functions. // - `routing::get`: A helper that registers a handler for HTTP GET requests only. use axum::{routing::get, Router}; // `const` defines a compile-time constant. // Syntax: `NAME: Type = value;` // - `&str` is a borrowed string slice (pointer + length), not an owned `String`. // - `"0.0.0.0:3000"` binds on all network interfaces; clients can reach port 3000. const LISTEN_ADDR: &str = "0.0.0.0:3000"; // Handler function for the root path `/`. // // `async fn` declares an asynchronous function. The caller must `.await` it. // Axum accepts async handlers because it runs on the Tokio async runtime. // // Return type `-> &'static str`: // - `&'static str` is a string slice that lives for the entire program lifetime. // - Axum converts this into an HTTP 200 response with `Content-Type: text/plain`. // - No explicit `Response` type is needed for simple text responses (via `IntoResponse`). async fn hello_world() -> &'static str { // The last expression in a block (without a semicolon) is the return value. "Hello, World!" } // `#[tokio::main]` is a procedural macro attribute. // It rewrites `async fn main()` into a normal `fn main()` that: // 1. Creates a Tokio multi-thread runtime. // 2. Runs the async body on that runtime until it completes. // Without this attribute, Rust's standard `main` cannot be `async` directly. #[tokio::main] async fn main() { // `Router::new()` constructs an empty router (no routes yet). // `.route(path, method_router)` chains route registration: // - `"/"` matches the root URL path. // - `get(hello_world)` wires the GET method to our handler function. // Axum infers the handler's argument/return types at compile time. let app = Router::new().route("/", get(hello_world)); // `tokio::net::TcpListener::bind` is async and returns `Result<TcpListener, Error>`. // `.await` suspends until the OS completes the bind operation. // `.expect("...")` unwraps `Ok` or panics on `Err` (acceptable for a tiny demo app). let listener = tokio::net::TcpListener::bind(LISTEN_ADDR) .await .expect("failed to bind TCP listener"); println!("Server listening on http://localhost:3000"); // `axum::serve(listener, app)` accepts connections and dispatches HTTP requests // to matching routes in `app`. It runs until the process receives a shutdown signal // (e.g. Ctrl+C) or encounters a fatal error. axum::serve(listener, app).await.expect("server error"); }
Rust
복사

Cargo.toml

[package] name = "axum_helloworld" version = "0.1.0" edition = "2021" description = "Minimal Axum Hello World web server" [dependencies] axum = "0.8" tokio = { version = "1", features = ["full"] }
Rust
복사

안녕하세요

관련 기술 문의와 R&D 공동 연구 사업 관련 문의는 “glory@keti.re.kr”로 연락 부탁드립니다.

Hello

For technical and business inquiries, please contact me at “glory@keti.re.kr”