Chapter 3: Hello World Explained in Rust
In this chapter, we will take the Hello World example apart line by line. Understanding every piece now will make the rest of the tutorial much easier.
The Code
use kungfu::prelude::*;
fn main() {
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(
Kungfu::new()
.handle_get("/hello", |_req, res| res.text("world"))
.run("0.0.0.0:3000"),
).unwrap();
}
Line by Line Breakdown
1. Importing Kungfu
The first line imports the Kungfu.js library. This gives you access to the Kungfu class, which is the main entry point for creating a web application.
Think of this like opening a toolbox. Until you open it, you cannot use any of the tools inside.
2. Creating the Application
The next line creates a new Kungfu application instance. This object holds your routes, middleware, and configuration. Everything you do with Kungfu.js starts here.
Behind the scenes, this initializes:
- A trie-based router (for fast URL matching)
- A middleware pipeline (for request processing)
- A buffer pool (for memory-efficient request handling)
- An OpenAPI spec generator (for auto-documentation)
3. Registering a Route
The .get("/hello", handler) line registers a route. It tells the router: "When a GET request comes in for the path /hello, call this handler function."
The handler function receives a request object and returns a response. In Rust, the response is an object with status and body properties.
4. Starting the Server
The .listen(3000) line starts the HTTP server on port 3000. This is a blocking call: the server will run forever, listening for connections, until you press Ctrl+C to stop it.
When a request arrives, here is the exact sequence of events:
- The Rust core accepts the TCP connection
- It reads the raw HTTP bytes from the socket
- It parses the HTTP request (method, path, headers, body)
- It looks up the path in the trie router
- It runs the middleware chain (security headers, CORS, rate limiter, logger)
- It calls your handler function
- Your function returns a response
- The middleware chain runs again in reverse (adding response headers)
- The Rust core formats the response as HTTP bytes
- It sends the bytes back to the client
All of this happens in microseconds. The Rust core is doing the heavy lifting; your Rust code only runs for step 6.
The Request Object
Your handler function receives a request object. Here is what it contains:
{
"method": "GET",
"path": "/hello",
"query": {}, // parsed query string parameters
"params": {}, // route parameters (we will cover in chapter 6)
"headers": { // all HTTP headers (lowercase keys)
"host": "localhost:3000",
"user-agent": "Mozilla/5.0..."
},
"body": "", // request body (for POST/PUT)
"remote_addr": "127.0.0.1:54321"
}
The Response Object
Your handler returns a response. The simplest response has two fields:
status: The HTTP status code (200 for OK, 404 for Not Found, etc.)body: The response body as a string
You can also add custom headers:
return {
status: 200,
headers: { "x-custom-header": "hello" },
body: "world"
};
Why is the Server So Fast?
The Rust core uses several techniques to achieve high throughput:
- Zero-copy body handling: Request and response bodies use
bytes::Bytes, which avoids copying memory. Cloning a Bytes object is just incrementing a counter. - Trie router: URL routing is O(path depth), not O(number of routes). Whether you have 10 routes or 10,000, lookup takes the same time.
- Buffer pooling: Instead of allocating new memory for every request, the server reuses buffers from a pool. This reduces garbage collection pressure.
- Single-syscall writes: The entire response (status line, headers, body) is built into one buffer and sent with a single
write_allcall.
What is Next?
In chapter 4, we will look at how to structure a real Kungfu.js project with multiple files, configuration, and best practices.