cloudflare-ruby

Ruby SDK for the Cloudflare API. Hand-written, narrow on purpose — currently covers the RealtimeKit product surface; other surfaces (R2, Workers, DNS) added on demand.

Status: prototyping. API not yet stable. The 0.0.x line is exploratory; the first stable release will be 0.1.0. Repo is private until then.

Product surfaces

Each Cloudflare product is its own namespace under Cloudflare::, with its own README living next to the code:

Product Namespace README
RealtimeKit Cloudflare::RealtimeKit lib/cloudflare/realtime_kit/README.md

New product surfaces are added by writing the resource classes by hand (no codegen) and adding a slice entry under spec/slices/<product>.json for drift detection.

Why hand-rolled and not openapi-generator / Stainless

  • We only need a thin slice of Cloudflare's 2,000+ endpoint API.
  • We want code that reads like the rest of our Ruby — generated SDKs in any language carry a generic style we'd rather not adopt.
  • A dedicated drift checker, scoped to what we use, gives us automation without 17 KLOC of generated noise.

See the design doc for the full reasoning.

Install

# Gemfile
gem "cloudflare-ruby", git: "git@github.com:tokimonki/cloudflare-ruby.git"

Configuration

Stripe-style global config; every resource picks it up implicitly.

Cloudflare.configure do |c|
  c.api_token  = ENV.fetch("CLOUDFLARE_API_TOKEN")
  c. = ENV.fetch("CLOUDFLARE_ACCOUNT_ID")  # default for every call
end

Per-call account_id: overrides the global. api_token has no per-call override — set it once.

Errors

Every product surface raises a member of the same hierarchy:

begin
  Cloudflare::RealtimeKit::Meeting.find("missing", app_id: "app-1")
rescue Cloudflare::NotFoundError => e
  e.status  # => 404
  e.body    # => parsed response body
end

Hierarchy:

Cloudflare::Error
├── AuthenticationError   (401, 403)
├── NotFoundError         (404)
├── ValidationError       (422)
├── RateLimitError        (429)
└── ServerError           (5xx)

Connection retries 429/502/503/504 up to 2× with backoff before raising.

Drift detection

The SDK is hand-written against a saved slice of Cloudflare's OpenAPI spec (spec/slices/<product>.json). When upstream changes, you re-extract the slice and diff to surface what moved:

bundle exec exe/cloudflare-ruby check-drift realtime_kit
# → "No drift detected for realtime_kit." (exit 0)
# → or a structured report of added/removed/changed paths and components (exit 1)

The fetched upstream spec is cached at tmp/upstream-openapi.json (gitignored) for inspection or offline re-runs (--spec PATH).

To re-baseline after an intentional update:

bundle exec exe/cloudflare-ruby refresh-slice realtime_kit

The diff covers paths plus every components.* sub-section discovered in either slice's union — new sections upstream starts using (e.g., requestBodies, securitySchemes) get diffed without code changes.

Architecture

lib/
├── cloudflare.rb               # top-level module, autoload registry
└── cloudflare/
    ├── version.rb
    ├── configuration.rb        # global config (api_token, account_id, base_url)
    ├── connection.rb           # Faraday singleton — auth, retries, JSON
    ├── errors.rb               # status-keyed error classes
    ├── resource.rb             # AR-flavored base (attribute, has_many, has_one, ...)
    ├── relation.rb             # nested-collection proxy
    └── <product>/              # one folder per product surface
        ├── README.md           # product-specific DX guide
        └── *.rb                # resource classes

codegen/                        # drift detection, not codegen
├── cli.rb                      # Thor: refresh-slice + check-drift
├── slicer.rb                   # extract paths-by-prefix from upstream
├── drift_detector.rb           # structured diff
└── spec_version.rb             # OpenAPI 3.0.3 pin

spec/slices/<product>.json      # the saved slice — drift baseline
test/                           # foundation + per-resource tests

Development

bundle install
bundle exec rake test

Adding a new product surface:

  1. bundle exec exe/cloudflare-ruby refresh-slice <product> (after registering it in codegen/cli.rb PRODUCTS).
  2. Write the resource classes under lib/cloudflare/<product>/.
  3. Write a lib/cloudflare/<product>/README.md documenting the DX.
  4. Add a row to the product surfaces table above.

License

MIT.