RafikiDB JS / TypeScript SDK#

sdk-js

Use when writing JavaScript or TypeScript code with the RafikiDB JS SDK (@rafikidb/sdk on npm). Covers createClient, data builder, joins, auth sessions, realtime (Centrifugo/SSE), storage, env, secrets, webhooks, functions, payments, and React hooks (useAuth).

You are working with @rafikidb/sdk (npm, TypeScript, ESM + CJS). Install: npm install @rafikidb/sdk.

Setup#

import { createClient } from "@rafikidb/sdk";

export const db = createClient({
  projectId: "5c73dab0-...",
  apiKey: "raf_live_...",
  baseUrl: "https://api.rafikidb.com/api/v1", // optional
  persistSession: true, // localStorage key: rafikidb_session.{projectId}
});
TypeScript
  • baseUrl defaults to http://localhost:8080/api/v1.
  • All calls return the API ApiEnvelope<T>: { success, message, data?, errors? }.
  • Failures throw RafikiDBError with status, code (unauthorized, forbidden, not_found, conflict, invalid_input, quota_exceeded, internal_error) and errors: string[].

Data#

// Read with filters
const { data } = await db
  .from<Profile>("profiles")
  .select("id, nationality")
  .eq("nationality", "Tanzania")
  .gt("age", 18)
  .in("region", ["dar", "mba"])
  .order("created_at", { ascending: false })
  .limit(25)
  .execute();

// Filters: eq, neq, gt, gte, lt, lte, like, ilike, isNull, isNotNull, in
// Pagination: limit, offset, cursor(id) (keyset, for large tables)
// Single row: .get(id), first row: .single(), count: .head()
TypeScript

execute() is thenable, so await db.from("t").select() also works.

Joins (no SQL, via query engine)#

const { data } = await db
  .from("messages")
  .select("id, message, users.full_name")
  .join({
    type: "LEFT JOIN", // INNER | LEFT | RIGHT | FULL
    table: "users",
    from_column: "user_id",
    to_column: "id",
  })
  .limit(20)
  .execute();
TypeScript

Joined columns appear as {table}.{column}. Joins route through POST /projects/{id}/query/run.

Writes#

await db.from("profiles").insert({ full_name: "Asha", nationality: "Tanzania" }); // single or array
await db.from("profiles").eq("id", id).update({ nationality: "Kenya" });
await db.from("profiles").eq("id", id).delete();
TypeScript

update() / delete() require an eq("id", ...) filter, else they throw.

Auth (project users, not dashboard)#

// Sign up / sign in (session auto-stored, sent with every request -> RLS)
const res = await db.auth.signup({ email, password, full_name, phone, metadata });
const session = await db.auth.login({ email, password }); // alias: signin

// OTP / reset
await db.auth.otpRequest("+255712345678");
await db.auth.otpVerify({ phone, code, full_name });
await db.auth.resetPassword(email);
await db.auth.confirmResetPassword({ email, code, new_password });

// Session
db.getSession();                 // Session { user, tokens } | null
db.setSession(session);          // manual restore/persist
db.subscribeSession((s) => {});  // unsubscribe fn returned
db.auth.signOut();               // clears + unpersists
db.auth.refresh(refreshToken);   // rotates tokens
db.auth.logout(refreshToken);
TypeScript
  • persistSession: true auto-saves to localStorage under rafikidb_session.{projectId} and restores on boot.
  • Dashboard login is db.auth.platformLogin({ email, password }) - use only for RafikiDB account holders.

Realtime#

const sub = db.realtime.subscribe(
  "messages",
  (event) => {
    // event: { id, type: "INSERT"|"UPDATE"|"DELETE", table, project_id, record, old_record?, created_at }
    console.log(event.type, event.record);
  },
  { events: ["INSERT", "DELETE"], transport: "auto" } // auto | centrifugo | sse
);
sub.unsubscribe();
TypeScript
  • auto prefers Centrifugo WebSocket (POST /realtime/connect -> scoped JWT, v6 wire: send { id: 1, connect: { token } }, frames carry push.channel + push.pub.data), falls back to SSE. Auto-reconnects with exponential backoff (1s..30s).

Modules#

// Storage
await db.storage.createBucket({ name, slug, is_public, file_size_limit, allowed_mime_types });
await db.storage.listBuckets();
await db.storage.signedUploadUrl({ bucket_id, object_name, content_length });
await db.storage.signedDownloadUrl(objectId);
db.storage.publicUrl(bucketId, objectId);

// Env vars
await db.env.set("STRIPE_KEY", "sk_test_123", "production");
await db.env.bulkSet("development", { DEBUG: "true" });

// Secrets (write-only)
await db.secrets.set({ key: "API_SECRET", value: "s3cr3t", description });
await db.secrets.reveal(secretId);

// Webhooks
await db.webhooks.create({ name, url, events, secret });
await db.webhooks.listDeliveries(webhookId);

// Edge functions
await db.functions.create({ name, runtime: "javascript", code });
await db.functions.deploy(fnId);
await db.functions.invoke(fnId, { method: "POST", body: "{}" });

// Payments (Snippe + M-Pesa)
await db.payments.stkPush({ phone: "+255712345678", amount: 5000, description });
await db.payments.snippeSession({ amount, redirect_url, order_id });
await db.payments.snippeStatus(reference);
TypeScript

React hooks#

import { useAuth } from "@rafikidb/sdk/react";

const { user, session, signOut } = useAuth(); // reacts to db session changes
TypeScript

Gotchas#

  • Never call platformLogin for app users; app auth is project-scoped (/projects/{id}/auth/*).
  • update/delete throw unless .eq("id", ...) is set first.
  • persistSession key includes the project id, so multiple projects do not collide.
  • If you pass a custom fetch, it is invoked with the global receiver to avoid detached-fetch "Illegal invocation" errors.