Chapter 3: Hello World Explained in C#
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
using Kungfu;
var app = new KungfuApp();
app.Get("/hello", (req, res) => res.Text(200, "world"));
app.Listen(3000);
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 C#, 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 C# 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.