Reference

Client SDKs

One-file SDKs for the web, iOS and Android. Sign users in, read and write tables, upload files, and listen for live changes and messages.

The three SDKs

Each SDK is one file with no dependencies, served by the API so it always matches it. They cover sign-in (with automatic token refresh), tables, functions you allowed users to call, files, live row changes, and broadcast and presence channels.

PlatformFileHow to add it
Web, Deno, Bun, Node 18+berth.jsimport it from the URL, or save it next to your code
iOS 15+, macOS 12+Berth.swiftdrag the file into your Xcode project
Android (minSdk 24)Berth.ktcopy it into your app module (package com.atberth)

Ship only the publishable key in a client. Your row policies decide what each signed-in user can read and write.

JavaScript

import { createClient } from "https://api.atberth.com/v1/sdk/berth.js";
const berth = createClient("my-app", "bpk_...");
await berth.auth.signUp({ email, password });           // or signIn, sendCode + verifyCode, signInWithIdToken("google", token)
berth.auth.onChange((event, user) => render(user));     // signed_in, token_refreshed, signed_out
const { rows, next_cursor } = await berth.from("notes")
  .select("id,title,done").eq("done", false).order("created_at", "desc").limit(20);
await berth.from("notes").insert({ title: "Buy milk" });
await berth.from("notes").update({ done: true }, rowId);
await berth.from("notes").delete(rowId);
const count = await berth.rpc("my_org_count");          // a function you allowed with PUT /rpc/{fn}/access
const file = await berth.storage.from("avatars").upload(`${berth.auth.user.id}/me.png`, blob);
const url = await berth.storage.from("avatars").signedUrl(`${berth.auth.user.id}/me.png`, 3600);
const live = berth.from("notes").on("*", (e) => console.log(e.type, e.record));   // live.close() to stop
const room = berth.channel("room:42", { presence: { name: "Ana" } });
room.on("broadcast", (m) => console.log(m.event, m.payload))
    .on("presence", (members) => showOnline(members));
await room.send("typing", { on: true });

Errors throw a BerthError with status, code (like policy_violation) and requestId. The session is kept in localStorage in a browser and in memory elsewhere; pass { storage } to change that. In Node, realtime needs { EventSource } from the eventsource package.

Swift

let berth = Berth(app: "my-app", key: "bpk_...")
try await berth.auth.signIn(email: email, password: password)
try await berth.auth.signInWithApple(idToken: token, nonce: nonce)
struct Note: Codable { var id: String?; var title: String; var done: Bool? }
let notes: [Note] = try await berth.from("notes").eq("done", "false").order("created_at", .desc).limit(20).rows()
try await berth.from("notes").insert(Note(id: nil, title: "Buy milk", done: false))
for try await change in berth.from("notes").changes() { print(change.type, change.record ?? [:]) }
let room = berth.channel("room:42", presence: ["name": "Ana"])
Task { for try await e in room.messages() { print(e.kind, e.data) } }
try await room.send("typing", ["on": true])

The session is kept in UserDefaults under berth.session.<app>.

Kotlin (Android)

val berth = Berth(context, "my-app", "bpk_...")
// The calls block: run them on Dispatchers.IO.
berth.auth.signIn("a@b.com", "password 1")
berth.auth.signInWithGoogle(idToken)
val rows = berth.from("notes").eq("done", "false").order("created_at", desc = true).limit(20).rows()
berth.from("notes").insert(JSONObject().put("title", "Buy milk"))
val sub = berth.from("notes").changes { type, event -> Log.d("notes", "$type $event") }   // sub.close()
val room = berth.channel("room:42", JSONObject().put("name", "Ana")) { kind, data -> /* broadcast, presence_* */ }
room.send("typing", JSONObject().put("on", true))

Broadcast and presence

Channels pass messages between clients without touching the database. See Realtime: channels for the rules on who may join which channel.