Implementing a Hello World Application
Now that the project is created, let's implement an actual application. Uzumibi allows you to work with both Ruby code (lib/app.rb) and a static frontend (public/ directory).
Frontend Implementation
First, let's place a static HTML file in the public/ directory. Create public/index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Hello Uzumibi</title>
<style>
body {
font-family: sans-serif;
max-width: 600px;
margin: 50px auto;
padding: 0 20px;
}
#result {
margin-top: 20px;
padding: 15px;
background: #f0f0f0;
border-radius: 5px;
}
</style>
</head>
<body>
<h1>Hello Uzumibi!</h1>
<p>Building Cloudflare Workers with Ruby</p>
<button id="greetBtn">Call API</button>
<div id="result"></div>
<script>
document.getElementById('greetBtn').addEventListener('click', async () => {
const res = await fetch('/api/hello');
const text = await res.text();
document.getElementById('result').textContent = text;
});
</script>
</body>
</html>
Files placed in the public/ directory are automatically served by Cloudflare Workers' static asset feature.
API Implementation
Next, edit lib/app.rb to implement the API endpoints. Replace the default generated code with the following:
class App < Uzumibi::Router
# Root path delegates to static assets (public/index.html)
get "/" do |req, res|
fetch_assets
end
# Simple API endpoint
get "/api/hello" do |req, res|
res.status_code = 200
res.headers = {
"content-type" => "text/plain",
"x-powered-by" => "#{RUBY_ENGINE} #{RUBY_VERSION}"
}
res.body = "Hello from Uzumibi! Running on #{RUBY_ENGINE} #{RUBY_VERSION}\n"
res
end
# Greeting API with dynamic parameters
get "/api/greet/:name" do |req, res|
name = req.params[:name]
res.status_code = 200
res.headers = {
"content-type" => "application/json",
}
res.body = JSON.generate({ message: "Hello, #{name}!" })
res
end
# POST request handling
post "/api/echo" do |req, res|
res.status_code = 200
res.headers = {
"content-type" => "text/plain",
}
res.body = "Received: #{req.body.inspect}\n"
res
end
end
$APP = App.new
Code Walkthrough
Routing
Define routes using class methods like get and post within a class that inherits from Uzumibi::Router:
class App < Uzumibi::Router
get "/path" do |req, res|
# Handler logic
end
end
Each route handler block receives two arguments: req (request) and res (response).
Serving Static Assets
get "/" do |req, res|
fetch_assets
end
Calling the fetch_assets method delegates the request to the static assets in the public/ directory. This causes public/index.html to be returned for the / path.
Note that if you define a handler for "/file" and a request comes in for /file, public/file.html will be returned if it exists. Be aware that the file extension is automatically appended.
Building a Response
Set status_code, headers, and body on the response object, then return res:
get "/api/hello" do |req, res|
res.status_code = 200
res.headers = {
"content-type" => "text/plain",
}
res.body = "Hello!\n"
res # Always return res
end
Dynamic Parameters
Including :parameter_name in the URL path captures that path segment as a dynamic parameter. Access it through the req.params Hash:
get "/api/greet/:name" do |req, res|
name = req.params[:name]
# ...
end
Request Body
The body of POST requests and similar can be accessed synchronously via req.body. If the request's content-type is form data (application/x-www-form-urlencoded) or JSON data (application/json), it is automatically parsed, and you can access the data through req.params as well.
The $APP Global Variable
$APP = App.new
In Uzumibi 0.6.x, the convention is to assign the application instance to the global variable $APP at the end. This variable is referenced from the Rust code on the Wasm side, so it must always be set.