backlex — Ruby SDK

Official Ruby client for the backlex API. A thin wrapper over the same REST + SSE surface the TypeScript SDK (@backlex/client) speaks — CRUD, a fluent query builder, auth, realtime, and storage. Zero runtime dependencies (stdlib net/http + json).

Part of backlex's multi-language SDK effort; follows the hybrid model: hand-written ergonomic layer here, optional OpenAPI-generated models underneath.

gem install backlex   # not yet published

Quickstart

require "backlex"
F = Backlex::Filter

client = Backlex::Client.new("https://api.example.com", api_key: "pak_...")

# CRUD
created = client.from("posts").create({ "title" => "Hello" })
client.from("posts").update(created["data"]["id"], { "title" => "Edited" })
client.from("posts").delete(created["data"]["id"])

# Fluent query builder → compiles to canonical JSON (same wire format as every other SDK)
rows = client.from("orders").query
             .where(F.and_(
                      F.eq("status", "active"),
                      F.gte("total", 100),
                      F.rel("customer", F.eq("tier", "gold")),   # -> "customer.tier"
                      F.gte("placed_at", F.now(sub: { "months" => 1 }))
                    ))
             .select("id", "total", "customer.name")
             .order_by("-placed_at", "id")
             .limit(50)
             .list

Ruby keywords are suffixed with _: and_, or_, not_, in_.

Auth

# Server-to-server: Backlex::Client.new(url, api_key: "pak_...") — bearer on every call.

# App mode — end-users of a workspace:
client = Backlex::Client.new(url, workspace: "myapp")
res = client.auth.("user@example.com", "secret")  # token auto-captured
token = client.auth.token                                 # persist this
# later: Backlex::Client.new(url, workspace: "myapp", token: token)
client.auth.sign_out

client.auth.providers returns the public auth surface. sign_in_social and sign_in_magic_link are also available.

Realtime (SSE)

sub = client.subscribe("items:posts", ->(ev) { puts "#{ev['event']}: #{ev['data']}" })
# ... runs on a background thread, auto-reconnects ...
sub.close

Storage

client.storage.put("avatars/me.png", bytes, content_type: "image/png")
data = client.storage.download("avatars/me.png")
client.storage.list("avatars/")
client.storage.delete("avatars/me.png")

Errors

Every non-2xx response (and transport failures) raise Backlex::Error with #status, #code, #details:

begin
  client.from("missing").list
rescue Backlex::Error => e
  raise unless e.status == 404
end

Hybrid codegen

For typed models, generate them from the OpenAPI spec the server ships — no Ruby-specific wire format is introduced:

openapi-generator generate \
  -i apps/web/src/server/lib/openapi-static.generated.json \
  -g ruby -o sdks/ruby/generated

Develop

cd sdks/ruby
ruby -Ilib test/query_test.rb     # offline: query-builder contract
ruby -Ilib test/client_test.rb    # offline: WEBrick HTTP-layer contract

Parity with the TS SDK

TS (@backlex/client) Ruby (Backlex)
createClient(opts) Backlex::Client.new(url, api_key:/workspace:/token:)
client.from(slug) client.from(slug)
.query().where(f => ...) .query.where(Filter.and_(...))
f.eq / and / rel / now Filter.eq / and_ / rel / now
.orderBy().withMeta() .order_by.with_meta
client.subscribe(ch, cb) client.subscribe(ch, cb).close
auth.signIn / getToken client.auth.sign_in / token
BacklexError Backlex::Error