Request-scoped
Wrappers resolve against the current Worker environment and are cached only for that Rack request.
PicoRuby × Cloudflare Workers
Examples and API boundaries for the Cloudflare bindings available from PicoRuby.
01 — THE SHAPE
Every Rack request receives a Cloudflare::Environment proxy at env["cloudflare.env"]. It resolves typed resource wrappers against the current Worker env; it is not a Ruby pointer to a JavaScript object.
Text variables and secrets use ENV. Resource bindings such as KV, D1, AI, or R2 do not.
Wrappers resolve against the current Worker environment and are cached only for that Rack request.
The generated registry reads declared binding types. The runtime never guesses a resource from its methods.
Promise-backed host operations cross the Wasm boundary through JSPI and resume the same Ruby stack.
Reach declared resources from the proxy. Use ENV for Worker text values and secrets only; writing to it creates a request-local Ruby overlay and does not mutate Cloudflare.
cloudflare = env["cloudflare.env"]
cache = cloudflare.CACHE_KV # Cloudflare::KV
database = cloudflare.DB # Cloudflare::D1
ai = cloudflare.AI # Cloudflare::AI
api_url = ENV["API_URL"] # variable or secret
same_cache = Cloudflare::KV.from_env(env, "CACHE_KV")
[]; property syntax raises NoMethodError. A missing or wrong typed explicit resource raises Cloudflare::BindingError.D1 exposes prepared statements. Build a statement with prepare, derive a bound one with bind, then choose a terminal operation such as run, rows, or first.
db = env["cloudflare.env"].DB
statement = db.prepare("SELECT id, name FROM users WHERE id = ?1")
user = statement.bind(1).first
created = db
.prepare("INSERT INTO users (name) VALUES (?1)")
.bind("Alice")
.run
Use a named namespace for binary-safe reads and writes. The optional ttl: maps to the Worker API’s relative expiration.
cache = env["cloudflare.env"].CACHE_KV
cache.put("greeting", "Hello from PicoRuby", ttl: 300)
value = cache.get("greeting")
[200, { "content-type" => "text/plain" },
[value || "missing"]]
get and put are exposed today. TTL must be at least 60 seconds; values are capped at 8 MiB at the Wasm boundary.Put Ruby Strings as raw bytes, inspect objects, list and delete keys, or return an original R2 body as a host-owned Worker stream.
bucket = env["cloudflare.env"].BUCKET
object = bucket.get("report.pdf")
return [404, { "content-type" => "text/plain" },
["not found"]] unless object
env["cloudflare.hijack"] = object.body
[200, { "content-type" => "application/pdf" }, []]
content-length or transfer-encoding; Workers controls framing.Keep run model-agnostic. Use generate or embed when their explicit response helpers fit. For streaming, hand the returned descriptor to the Rack extension.
ai = env["cloudflare.env"].AI
stream = ai.generate(
"@cf/meta/llama-3.1-8b-instruct",
{ "prompt" => "Tell a short Ruby story", "stream" => true }
)
env["cloudflare.hijack"] = stream
[200, { "content-type" => "text/event-stream" }, []]
Query by vector or ID, then insert, upsert, inspect, and delete vectors with JSON-compatible values.
index = env["cloudflare.env"].VECTOR_INDEX
matches = index.query(
[0.1, 0.2, 0.3],
top_k: 5,
return_metadata: :indexed,
namespace: "docs"
)
top_k is 1–100, or at most 50 when returning values or all metadata.A producer sends one UTF-8 text message. Consumers receive a batch environment and settle messages with ack or retry.
# request path: producer
env["cloudflare.env"].EVENTS_QUEUE.send("event-created")
# queue consumer
class EventConsumer < Cloudflare::QueueConsumer::Base
def call(_env)
current_batch.messages.each { |message| message.ack }
end
end
Cloudflare::Queues.run(EventConsumer)
The current adapter is a small JSON-backed object store. Select a named instance and read or write a POJO, Hash, Array, or to_pojo value.
store = Cloudflare::DurableObject.from_env(env, "OBJECTS")
profile = Cloudflare::DurableObject::POJO.new
profile["name"] = "Alice"
store.put("user-1", profile)
stored = store.get("user-1")
name = stored["name"]
PicoRubyDurableObject.Use the Rack middleware to turn the CF_Authorization cookie into an AccessIdentity at env["cloudflare.identity"].
app = Rack::Builder.new do
use Rack::Cloudflare::Access, team: "my-team"
run lambda { |env|
identity = env["cloudflare.identity"]
[200, { "content-type" => "text/plain" }, [identity.email]]
}
end
Rackup::Handler::CloudflareWorker.run(app)
Call HTTP(S) through the Worker host, and use the browser-compatible Web Crypto API for secure random bytes and AES-GCM.
response = Cloudflare.fetch("https://example.com/api",
method: "POST", body: "hello")
secret = SecureRandom.random_bytes(32)
iv, encrypted = Crypto.encrypt(:AES_GCM, secret, "private data")
plain = Crypto.decrypt(:AES_GCM, secret, iv, encrypted)