Saving Memos with Durable Object
In this chapter, we'll create a memo application that stores data persistently using Uzumibi::KV (based on Cloudflare Durable Objects).
Architecture Overview
Cloudflare Durable Objects is a service for managing stateful objects on Cloudflare Workers. While regular Workers are stateless (they don't retain state between requests), Durable Objects enable persistent data read/write operations.
In Uzumibi version 0.6.x, the Uzumibi::KV API wraps Durable Objects as a simple Key-Value store.
Client → Cloudflare Worker (Uzumibi)
↓ Uzumibi::KV.get/set
UzumibiKVObject (Durable Object)
↓
SQLite storage (persisted)
UzumibiKVObject is a Durable Object class defined in the JavaScript glue code. Internally, it uses the Durable Object's ctx.storage API (SQLite-based) to store data.
Creating the Project
Create a project with external service integration enabled:
uzumibi new \
--template cloudflare \
--features enable-external memo-app
cd memo-app
pnpm install
Check the generated wrangler.jsonc to see the automatically configured Durable Object bindings:
{
"durable_objects": {
"bindings": [
{
"name": "UZUMIBI_KV_DATA",
"class_name": "UzumibiKVObject"
}
]
},
"migrations": [
{
"tag": "v1",
"new_sqlite_classes": [
"UzumibiKVObject"
]
}
]
}
Implementation
Edit lib/app.rb to create a simple memo save/retrieve API:
class App < Uzumibi::Router
get "/" do |req, res|
fetch_assets
end
# Get a memo
get "/api/memo/:key" do |req, res|
key = req.params[:key]
value = Uzumibi::KV.get(key)
if value
res.status_code = 200
res.headers = {
"content-type" => "application/json",
}
res.body = JSON.generate({
key: key,
value: value
})
else
res.status_code = 404
res.headers = {
"content-type" => "application/json",
}
res.body = JSON.generate({
error: "not found",
key: key
})
end
res
end
# Save a memo
post "/api/memo/:key" do |req, res|
key = req.params[:key]
value = req.body
debug_console("Saving memo: #{key} = #{value}")
Uzumibi::KV.set(key, value)
res.status_code = 201
res.headers = {
"content-type" => "application/json",
}
res.body = JSON.generate({
key: key,
value: value,
status: "saved"
})
res
end
end
$APP = App.new
Code Walkthrough
Getting a Value
value = Uzumibi::KV.get(key)
Uzumibi::KV.get returns the value for the specified key as a string. If the key doesn't exist, it returns nil.
Saving a Value
Uzumibi::KV.set(key, value)
Uzumibi::KV.set saves a key-value pair. Calling set again with the same key overwrites the value. Both keys and values are string types.
Verifying Operation
Start the development server:
pnpm run dev
Test saving and retrieving memos with curl:
# Save a memo
$ curl -X POST -d "Shopping list: milk, bread, eggs" \
http://localhost:8787/api/memo/shopping
{"key": "shopping", "value": "Shopping list: milk, bread, eggs", "status": "saved"}
# Get a memo
$ curl http://localhost:8787/api/memo/shopping
{"key": "shopping", "value": "Shopping list: milk, bread, eggs"}
# Non-existent key
$ curl http://localhost:8787/api/memo/unknown
{"error": "not found", "key": "unknown"}
# Update a memo
$ curl -X POST -d "Shopping list: milk, bread, eggs, butter" \
http://localhost:8787/api/memo/shopping
{"key": "shopping", "value": "Shopping list: milk, bread, eggs, butter", "status": "saved"}
Data stored in the Durable Object persists across Worker restarts and deployments.
Notes
- Both keys and values in
Uzumibi::KVare string types. If you want to store numbers or objects, convert them to JSON strings or similar before saving. - During local development (
wrangler dev), Durable Object data is saved in local storage within the project's.wrangler/directory. - When deployed to production, data is stored on Cloudflare's global network.