{"id":50593552,"url":"https://github.com/kennedyowusu/koolbase-react-native","last_synced_at":"2026-06-05T12:03:43.503Z","repository":{"id":348015368,"uuid":"1193053084","full_name":"kennedyowusu/koolbase-react-native","owner":"kennedyowusu","description":"React Native SDK for Koolbase — auth, database, storage, realtime, feature flags, and functions for mobile apps.","archived":false,"fork":false,"pushed_at":"2026-05-28T02:19:31.000Z","size":380,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-28T03:09:44.668Z","etag":null,"topics":["baas","backend","firebase-alternative","open-source","react-native","typescript"],"latest_commit_sha":null,"homepage":"https://koolbase.com/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kennedyowusu.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-26T20:33:52.000Z","updated_at":"2026-05-28T02:19:36.000Z","dependencies_parsed_at":"2026-05-19T15:03:14.081Z","dependency_job_id":null,"html_url":"https://github.com/kennedyowusu/koolbase-react-native","commit_stats":null,"previous_names":["kennedyowusu/koolbase-react-native"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/kennedyowusu/koolbase-react-native","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kennedyowusu%2Fkoolbase-react-native","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kennedyowusu%2Fkoolbase-react-native/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kennedyowusu%2Fkoolbase-react-native/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kennedyowusu%2Fkoolbase-react-native/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kennedyowusu","download_url":"https://codeload.github.com/kennedyowusu/koolbase-react-native/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kennedyowusu%2Fkoolbase-react-native/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33939230,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-05T02:00:06.157Z","response_time":120,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["baas","backend","firebase-alternative","open-source","react-native","typescript"],"created_at":"2026-06-05T12:03:42.724Z","updated_at":"2026-06-05T12:03:43.485Z","avatar_url":"https://github.com/kennedyowusu.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# @techfinityedge/koolbase-react-native\n\n[![npm](https://img.shields.io/npm/v/@techfinityedge/koolbase-react-native.svg)](https://www.npmjs.com/package/@techfinityedge/koolbase-react-native)\n[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://opensource.org/licenses/MIT)\n\nReact Native SDK for [Koolbase](https://koolbase.com) — Backend as a Service built for mobile developers.\n\nAuth, database, storage, realtime, functions, feature flags, remote config, version enforcement, code push, logic engine, analytics, and cloud messaging — one SDK, one `initialize()` call.\n\n---\n\n## Get started in 2 minutes\n\n1. Create a free account at [app.koolbase.com](https://app.koolbase.com)\n2. Create a project and copy your public key from Environments\n3. Add the SDK:\n\n```bash\n   npm install @techfinityedge/koolbase-react-native\n   # or\n   yarn add @techfinityedge/koolbase-react-native\n   # or\n   pnpm add @techfinityedge/koolbase-react-native\n   # or\n   bun add @techfinityedge/koolbase-react-native\n```\n\n4. Initialize at app startup:\n\n```typescript\n   import { Koolbase } from '@techfinityedge/koolbase-react-native';\n\n   await Koolbase.initialize({\n     publicKey: 'pk_live_xxxx',\n     baseUrl: 'https://api.koolbase.com',\n   });\n```\n\nThat's it. Every feature below is now available via `Koolbase.*`.\n\n---\n\n\u003e **Auth is automatic (v3+).** Database, storage, and functions calls\n\u003e authenticate as the currently signed-in user — nothing to pass, no manual\n\u003e wiring. Log in (or restore a session) and every request carries that\n\u003e identity. `owner`/`authenticated` collections require an active session.\n\n---\n\n## Authentication\n\nEmail + password, Apple Sign-In, Google Sign-In, and phone + OTP — out of the box.\n\n```typescript\n// Register\nawait Koolbase.auth.register({ email: 'user@example.com', password: 'password' });\n\n// Login\nconst session = await Koolbase.auth.login({ email: 'user@example.com', password: 'password' });\n\n// Current user\nconst me = Koolbase.auth.currentUser;\n\n// Logout\nawait Koolbase.auth.logout();\n\n// Password reset\nawait Koolbase.auth.forgotPassword('user@example.com');\n\n// Listen to auth state changes (fires immediately with current state)\nconst unsubscribe = Koolbase.auth.onAuthStateChange((user) =\u003e {\n  console.log(user ? 'signed in' : 'signed out');\n});\n```\n\n---\n\n### OAuth — Apple\n\nApple Sign-In uses the native authentication flow via `@invertase/react-native-apple-authentication` as a peer dependency:\n\n```typescript\nimport appleAuth from '@invertase/react-native-apple-authentication';\nimport { Koolbase } from '@techfinityedge/koolbase-react-native';\n\nconst response = await appleAuth.performRequest({\n  requestedOperation: appleAuth.Operation.LOGIN,\n  requestedScopes: [appleAuth.Scope.EMAIL, appleAuth.Scope.FULL_NAME],\n});\n\nconst session = await Koolbase.auth.signInWithApple({\n  identityToken: response.identityToken!,\n  nonce: response.nonce,\n  fullName: response.fullName\n    ? {\n        givenName: response.fullName.givenName ?? undefined,\n        familyName: response.fullName.familyName ?? undefined,\n      }\n    : undefined,\n});\n```\n\nConfigure Apple Sign-In for your environment with your iOS app's Bundle ID. Full setup guide at [docs.koolbase.com/auth/oauth](https://docs.koolbase.com/auth/oauth).\n\n---\n\n### OAuth — Google\n\nGoogle Sign-In uses the native authentication flow via `@react-native-google-signin/google-signin` as a peer dependency:\n\n```typescript\nimport { GoogleSignin } from '@react-native-google-signin/google-signin';\nimport { Koolbase } from '@techfinityedge/koolbase-react-native';\n\nGoogleSignin.configure({\n  webClientId: '\u003cyour-web-client-id\u003e.apps.googleusercontent.com',\n});\n\nconst userInfo = await GoogleSignin.signIn();\n\nconst session = await Koolbase.auth.signInWithGoogle({\n  idToken: userInfo.idToken!,\n});\n```\n\nConfigure Google Sign-In for your environment with the OAuth client IDs from Google Cloud Console (typically one each for iOS, Android, and web). Full setup guide at [docs.koolbase.com/auth/oauth](https://docs.koolbase.com/auth/oauth).\n\n---\n\n### Phone + OTP\n\n```typescript\n// Send a one-time code\nawait Koolbase.auth.sendOtp({ phoneNumber: '+233200000000' });\n\n// Verify and sign in\nawait Koolbase.auth.verifyOtp({\n  phoneNumber: '+233200000000',\n  code: '123456',\n});\n\n// Or link a phone to an existing account\nawait Koolbase.auth.linkPhone({\n  phoneNumber: '+233200000000',\n  code: '123456',\n});\n```\n\nConfigure your SMS provider (Twilio, Africa's Talking, or Hubtel) in the dashboard under Phone Auth.\n\n---\n\n## Database\n\n```typescript\n// Insert\nawait Koolbase.db.insert('posts', { title: 'Hello', published: true });\n\n// Query\nconst { records } = await Koolbase.db.query('posts', {\n  filters: { published: true },\n  limit: 10,\n  orderBy: 'created_at',\n  orderDesc: true,\n});\n\n// Read fields off a record\nconst post = records[0];\nconsole.log(post.data.title);          // your fields live under .data\nconsole.log(post.id, post.collection); // metadata\n\n// Populate related records\nconst { records: postsWithAuthor } = await Koolbase.db.query('posts', {\n  populate: ['author_id:users'],\n});\n\n// Update / Delete\nawait Koolbase.db.update('record-id', { title: 'Updated' });\nawait Koolbase.db.delete('record-id');\n```\n\n---\n\n### Handling unique-constraint conflicts\n\nA write that would violate a unique constraint throws `KoolbaseConflictError`:\n\n```ts\ntry {\n  await Koolbase.db.upsert('users', { email }, { name });\n} catch (e) {\n  if (e instanceof KoolbaseConflictError) {\n    showError('That email is already registered.');\n  }\n}\n```\n\n---\n\n### Public bucket URLs\n\nFor files in public buckets, you can construct the stable CDN URL directly — no\nnetwork call, no expiry, embeddable anywhere a browser fetches a URL.\n\n```typescript\nimport { KoolbaseStorage } from '@techfinityedge/koolbase-react-native';\n\n// From a KoolbaseObject you already have (e.g. from upload() or another read)\nconst { object } = await Koolbase.storage.upload({\n  bucket: 'avatars',\n  path: `user-${userId}.jpg`,\n  file: { uri: imageUri, name: 'avatar.jpg', type: 'image/jpeg' },\n});\n\nconst url = KoolbaseStorage.publicUrlForObject(object, 'avatars');\n// url is null for private-bucket objects; the CDN URL for public-bucket ones.\n\nif (url) {\n  // Safe to use — file lives in the public R2 bucket\n  return \u003cImage source={{ uri: url }} /\u003e;\n}\n\n// For build-time URL construction (no Object on hand)\nconst url = KoolbaseStorage.publicUrl({\n  projectId: 'proj_abc',\n  bucket: 'avatars',\n  path: 'user-123.jpg',\n});\n// Always returns the URL pattern; caller is responsible for knowing\n// the file lives in a public bucket. For files in private buckets,\n// the resulting URL will 404.\n```\n\nURLs follow the pattern `https://cdn.koolbase.com/{project_id}/{bucket}/{path}` — long-lived, edge-cached, no authentication. For files in private buckets, use `getDownloadUrl` instead, which returns a 1-hour presigned URL.\n\n---\n\n### Image transforms\n\nPublic bucket URLs can be transformed at the edge — resize, reformat,\noptimize — without any preprocessing. Two ways:\n\n**Direct transforms** — pass a `transform` option to `publicUrl`:\n\n```ts\nconst url = KoolbaseStorage.publicUrl({\n  projectId: 'proj_abc',\n  bucket: 'avatars',\n  path: 'user-123.jpg',\n  transform: {\n    width: 200,\n    height: 200,\n    fit: 'cover',\n    format: 'auto',\n    quality: 85,\n  },\n});\n```\n\n**Named presets** — store an option set server-side (via the dashboard or\nREST API), reference it by name:\n\n```ts\nconst url = KoolbaseStorage.publicUrlWithPreset({\n  projectId: 'proj_abc',\n  presetName: 'thumbnail',\n  bucket: 'avatars',\n  path: 'user-123.jpg',\n});\n\n// Or from a KoolbaseObject instance:\nconst url = KoolbaseStorage.publicUrlForObjectWithPreset(object, 'avatars', 'thumbnail');\n```\n\nAvailable options: `width` and `height` (1–2000), `format`\n(`auto`/`webp`/`avif`/`jpeg`/`png`), `quality` (1–100), `fit`\n(`scale-down`/`contain`/`cover`/`crop`/`pad`), `dpr` (1–3), `gravity`\n(`auto`/`center`/`top`/`bottom`/`left`/`right`/`top-left`/`top-right`/\n`bottom-left`/`bottom-right`). Transformed responses are edge-cached for 4\nhours; Cloudflare includes 5,000 unique transformations/month free per\naccount.\n\nSee the [Image Transforms docs](https://docs.koolbase.com/storage/image-transforms)\nfor the full reference.\n\n---\n\n### Upsert\n\nInsert a record, or update the existing one matching a filter.\n\n```ts\nconst result = await Koolbase.db.upsert(\n  'profiles',\n  { user_id: userId },\n  { weightKg: 70 }\n);\n\nconsole.log(result.created); // true if inserted, false if updated\nconsole.log(result.record.id);\n```\n\n\u003e Online-only: needs the server's view to decide insert vs update, so unlike\n\u003e `insert` it isn't queued offline and throws on network failure.\n\n### Delete where\n\nBulk-delete every record matching a filter. Returns the number deleted.\n\n```ts\nconst deleted = await Koolbase.db.deleteWhere('sessions', {\n  user_id: userId,\n  status: 'expired',\n});\n```\n\n\u003e A non-empty filter is required. The collection's delete rule applies; for\n\u003e `owner`/`scoped` rules the delete is scoped to your own records. Online-only.\n\n---\n\n### Offline-first\n\n```typescript\nconst { records, isFromCache } = await Koolbase.db.query('posts', { limit: 20 });\nif (isFromCache) console.log('Served from local cache');\n\nawait Koolbase.db.syncPendingWrites();\n```\n\n---\n\n### Atomic batch writes\n\nRun multiple writes in a single server-side transaction. All operations commit together or none are applied — any failure rolls back the entire batch.\n\n```ts\nimport { Koolbase, BatchOp } from '@techfinityedge/koolbase-react-native';\n\nconst results = await Koolbase.db.batch([\n  BatchOp.insert('orders', { total: 50, customer_id: customerId }),\n  BatchOp.update(inventoryId, { stock: 9 }),\n  BatchOp.upsert('counters', {\n    match: { name: 'orders' },\n    data: { value: 1 },\n  }),\n  BatchOp.delete(cartItemId),\n]);\n\n// results[i] corresponds to operations[i]:\n//   - insert / update: { type, record }\n//   - upsert:          { type, record, created }   // created = true if inserted\n//   - delete:          { type, deleted: true }\n```\n\n**Online-only by design.** Atomicity needs the server's authoritative view, so `batch()` is never queued offline — it throws on network failure (like `upsert` and `deleteWhere`). A server-side rejection throws a `KoolbaseDataError` with the failing operation's details; nothing was persisted.\n\n---\n\n### Handling write conflicts\n\n`insert`, `update`, and `upsert` are online-first: when the server is reachable they throw a typed error on rejection. Catch `KoolbaseConflictError` to handle unique-constraint violations (e.g. a duplicate email):\n\n```ts\nimport { KoolbaseConflictError } from '@techfinityedge/koolbase-react-native';\n\ntry {\n  await Koolbase.db.insert('users', { email, name });\n} catch (e) {\n  if (e instanceof KoolbaseConflictError) {\n    showError(`That ${e.field ?? 'value'} is already in use.`);\n  } else {\n    throw e;\n  }\n}\n```\n\nWhen the device is offline, these writes are queued and synced automatically when connectivity returns.\n\n---\n\n## Storage\n\nUpload and serve files via presigned URLs to Cloudflare R2. Uploads are\n**safe-by-default** (v5+) — uploading to a path that's already taken throws\n`KoolbaseStorageConflictError` instead of silently replacing the existing\nfile. Pass `overwrite: true` for true upsert semantics.\n\n```typescript\n// Upload — rejects if `user-${userId}.jpg` already exists\nconst { object, downloadUrl } = await Koolbase.storage.upload({\n  bucket: 'avatars',\n  path: `user-${userId}.jpg`,\n  file: { uri: imageUri, name: 'avatar.jpg', type: 'image/jpeg' },\n});\n\n// Upload — silently replaces any existing object at this path\nawait Koolbase.storage.upload({\n  bucket: 'avatars',\n  path: `user-${userId}.jpg`,\n  file: { uri: imageUri, name: 'avatar.jpg', type: 'image/jpeg' },\n  overwrite: true,\n});\n\n// Get download URL\nconst url = await Koolbase.storage.getDownloadUrl('avatars', `user-${userId}.jpg`);\n\n// Delete\nawait Koolbase.storage.delete('avatars', `user-${userId}.jpg`);\n```\n\n---\n\n### Handling upload conflicts\n\nFor user-supplied filenames, prompt the user before overwriting:\n\n```typescript\nimport { KoolbaseStorageConflictError } from '@techfinityedge/koolbase-react-native';\n\ntry {\n  await Koolbase.storage.upload({\n    bucket: 'documents',\n    path: filename,\n    file: { uri, name: filename, type: mimeType },\n  });\n} catch (e) catch (e) {\nif (e instanceof KoolbaseStorageConflictError) {\nconst ok = await confirm(${e.path} already exists. Overwrite?);\nif (ok) {\nawait Koolbase.storage.upload({\nbucket: 'documents',\npath: filename,\nfile: { uri, name: filename, type: mimeType },\noverwrite: true,\n});\n}\n} else {\nthrow e;\n}\n}\n```\n\nSee [Error handling](#error-handling) for the full set of storage errors.\n\n---\n\n### Handling bucket limits\n\nBuckets can be configured at creation time with a total size cap\n(`max_size_bytes`), a per-file cap (`max_file_size_bytes`), and a\ncontent-type allowlist (`allowed_mime_types`, supports `image/*`-style\nwildcards). The server surfaces violations as typed errors:\n\n````typescript\nimport {\n  KoolbaseStorageQuotaError,\n  KoolbaseStorageFileTooLargeError,\n  KoolbaseStorageMimeTypeError,\n} from '@techfinityedge/koolbase-react-native';\n\ntry {\n  await Koolbase.storage.upload({\n    bucket: 'user-photos',\n    path: filename,\n    file: { uri, name: filename, type: mimeType },\n  });\n} catch (e) {\n  if (e instanceof KoolbaseStorageMimeTypeError) {\n    showError('That file type is not allowed in this bucket.');\n  } else if (e instanceof KoolbaseStorageFileTooLargeError) {\n    showError('That file is too big — pick a smaller one.');\n  } else if (e instanceof KoolbaseStorageQuotaError) {\n    showError('This bucket is full — delete some files and try again.');\n  } else {\n    throw e;\n  }\n}\n````\n\nMIME enforcement runs at presign time — no bytes are transferred before\nrejection. File-size and quota enforcement run at confirm time; the\nserver cleans up the underlying R2 object before returning the error,\nso nothing leaks.\n\n---\n\n## Realtime\n\nSubscribe to live changes on a collection. Uses the signed-in user's session, so\nsubscribe after login. Streams `created`, `updated`, and `deleted` events for\ncollections whose read rule is `public` or `authenticated`.\n\n```ts\nconst unsubscribe = Koolbase.realtime.subscribe('messages', (event) =\u003e {\n  // event.type -\u003e 'created' | 'updated' | 'deleted'\n  if (event.type === 'deleted') {\n    console.log('deleted', event.recordId);   // recordId on deletes\n  } else {\n    console.log(event.type, event.record!.data); // record on created/updated\n  }\n});\n\nunsubscribe();\n```\n\nThe socket opens lazily, is shared, and reconnects automatically. The project is\ntaken from the user's session.\n\n---\n\n## Functions\n\nInvoke deployed serverless functions. When a user is signed in via `Koolbase.auth`, their access token is automatically forwarded — the function receives the caller's identity via `ctx.auth`. No token handling on the client side.\n\n```typescript\n// Invoke a deployed function\nconst result = await Koolbase.functions.invoke('send-welcome-email', {\n  userId: '123',\n});\nif (result.success) console.log(result.data);\n```\n\nInside the function, read the caller:\n\n```typescript\nexport async function handler(ctx) {\n  const userId = ctx.auth?.user_id;\n  if (!userId) {\n    return { error: { code: 'AUTH_REQUIRED' }, status: 401 };\n  }\n  // Authenticated logic here\n  return { ok: true };\n}\n```\n\nToken refresh is transparent — the SDK reads the current token fresh on every invoke. Full docs at [docs.koolbase.com/functions/authentication](https://docs.koolbase.com/functions/authentication).\n\n---\n\n## Feature Flags \u0026 Remote Config\n\n```typescript\nif (Koolbase.isEnabled('new_checkout')) { /* ... */ }\n\nconst timeout = Koolbase.configNumber('timeout_seconds', 30);\nconst apiUrl = Koolbase.configString('api_url', 'https://api.myapp.com');\nconst dark = Koolbase.configBool('force_dark_mode', false);\n```\n\n---\n\n## Version Enforcement\n\n```typescript\nconst result = Koolbase.checkVersion('1.2.3');\nif (result.status === 'force_update') {\n  // block and show update screen\n}\n```\n\n---\n\n## Code Push\n\nPush config overrides, feature flag overrides, and directive-driven behaviour without a store release.\n\n```typescript\nawait Koolbase.initialize({\n  publicKey: 'pk_live_xxxx',\n  baseUrl: 'https://api.koolbase.com',\n  codePushChannel: 'stable',\n});\n\n// Bundle values override Remote Config + Feature Flags transparently\nconst timeout = Koolbase.configNumber('api_timeout_ms', 3000);\n\n// Directive handlers\nKoolbase.codePush.onDirective('force_logout_all', (value) =\u003e {\n  if (value) Koolbase.auth.logout();\n});\nKoolbase.codePush.applyDirectives();\n```\n\n---\n\n### Mandatory updates\n\nMark a bundle **mandatory** in the dashboard (or via `PATCH /mandatory`) when every device must apply it before continuing — surfaced as a push callback and a pollable flag:\n\n```typescript\nawait Koolbase.initialize({\n  publicKey: 'pk_live_xxxx',\n  baseUrl: 'https://api.koolbase.com',\n  // Fires the moment a mandatory bundle is staged for the next launch\n  onMandatoryUpdate: ({ version }) =\u003e {\n    showRestartRequiredDialog(version);\n  },\n});\n\n// Or poll it — e.g. on app resume — before letting the user proceed\nif (Koolbase.codePush.hasMandatoryUpdate) {\n  showRestartRequiredDialog();\n}\n```\n\nA mandatory bundle still activates on the next cold launch like any other; the callback and flag just let you prompt the user to restart now instead of waiting.\n\n---\n\n## Logic Engine\n\nDefine conditional app behavior as data in your Runtime Bundle — no code changes required.\n\n```typescript\nconst result = Koolbase.executeFlow('on_checkout_tap', {\n  plan: user.plan,\n  usage: user.usage,\n});\n\nif (result.hasEvent) {\n  switch (result.eventName) {\n    case 'show_upgrade': navigation.navigate('Upgrade'); break;\n    case 'go_checkout': navigation.navigate('Checkout'); break;\n  }\n}\n```\n\n**v2 operators:** `eq`, `neq`, `gt`, `gte`, `lt`, `lte`, `contains`, `starts_with`, `ends_with`, `in_list`, `not_in_list`, `between`, `is_true`, `is_false`, `exists`, `not_exists`, `and`, `or`\n\nFull docs at [docs.koolbase.com/sdk/logic-engine](https://docs.koolbase.com/sdk/logic-engine).\n\n---\n\n## Analytics\n\nTrack screen views, custom events, and user behaviour. View DAU, WAU, MAU, funnels, and retention in the Koolbase dashboard.\n\n```typescript\nawait Koolbase.initialize({\n  publicKey: 'pk_live_xxxx',\n  baseUrl: 'https://api.koolbase.com',\n  analyticsEnabled: true,\n  appVersion: '1.0.0',\n});\n\n// Custom events\nKoolbase.analytics.track('purchase', { value: 1200, currency: 'GHS' });\n\n// Screen views\nKoolbase.analytics.screenView('checkout');\n\n// User identity\nKoolbase.analytics.identify(user.id);\nKoolbase.analytics.setUserProperty('plan', 'pro');\n\n// On logout\nKoolbase.analytics.reset();\n```\n\n---\n\n## Cloud Messaging\n\n```typescript\nawait Koolbase.initialize({\n  publicKey: 'pk_live_xxxx',\n  baseUrl: 'https://api.koolbase.com',\n  messagingEnabled: true,\n});\n\n// Register FCM token (after obtaining from @react-native-firebase/messaging)\nconst fcmToken = await messaging().getToken();\nawait Koolbase.messaging.registerToken({\n  token: fcmToken,\n  platform: 'android', // or 'ios'\n});\n\n// Send to a specific device\nawait Koolbase.messaging.send({\n  to: deviceToken,\n  title: 'Your order is ready',\n  body: 'Pick up at counter 3',\n  data: { order_id: '123' },\n});\n```\n\n---\n\n## Error handling\n\nKoolbase throws typed errors selected from the server's stable error `code`, so\nhandling doesn't depend on message text.\n\n### Database errors\n\nAll data-layer failures extend `KoolbaseDataError` (which extends `Error`):\n\n| Error | When |\n| `KoolbaseStorageConflictError` | An upload targets a path that's already taken and `overwrite: false` (409, code `PATH_CONFLICT`). Exposes `.path` — the colliding path. |\n| `KoolbaseStorageNotFoundError` | The bucket or object doesn't exist (404). |\n| `KoolbaseStorageValidationError` | The request was rejected as invalid — bad path, missing field (400). |\n| `KoolbaseStoragePermissionError` | The caller is not allowed to perform the operation (403). |\n| `KoolbaseStorageQuotaError` | An upload would push the bucket past its `max_size_bytes` cap (409, code `QUOTA_EXCEEDED`). |\n| `KoolbaseStorageFileTooLargeError` | A single file exceeds the bucket's `max_file_size_bytes` cap (413, code `FILE_TOO_LARGE`). |\n| `KoolbaseStorageMimeTypeError` | The upload's content-type isn't in the bucket's `allowed_mime_types` allowlist (415, code `MIME_NOT_ALLOWED`). |\n\n```ts\nimport { KoolbaseConflictError, KoolbaseDataError } from '@techfinityedge/koolbase-react-native';\n\ntry {\n  await Koolbase.db.upsert('users', { email }, { name });\n} catch (e) {\n  if (e instanceof KoolbaseConflictError) {\n    showError(`That ${e.field ?? 'value'} is already taken.`);\n  } else if (e instanceof KoolbaseDataError) {\n    showError(e.message);\n  }\n}\n```\n\n\u003e `query`, `get`, `upsert`, and `deleteWhere` throw these typed errors. `insert`,\n\u003e `update`, and `delete` are optimistic/offline-first — they queue and sync in\n\u003e the background, so their conflicts surface via the sync engine, not as a\n\u003e thrown error.\n\n---\n\n### Storage errors\n\nAll storage failures extend `KoolbaseStorageError` (which extends `Error`):\n\n| Error | When |\n|---|---|\n| `KoolbaseStorageConflictError` | An upload targets a path that's already taken and `overwrite: false` (409, code `PATH_CONFLICT`). Exposes `.path` — the colliding path. |\n| `KoolbaseStorageNotFoundError` | The bucket or object doesn't exist (404). |\n| `KoolbaseStorageValidationError` | The request was rejected as invalid — bad path, missing field (400). |\n| `KoolbaseStoragePermissionError` | The caller is not allowed to perform the operation (403). |\n\n```ts\nimport {\n  KoolbaseStorageConflictError,\n  KoolbaseStorageError,\n  KoolbaseStoragePermissionError,\n} from '@techfinityedge/koolbase-react-native';\n\ntry {\n  await Koolbase.storage.upload({\n    bucket: 'avatars',\n    path: 'me.png',\n    file: { uri, name: 'me.png', type: 'image/png' },\n  });\n} catch (e) {\n  if (e instanceof KoolbaseStorageConflictError) {\n    // Already exists — prompt user to confirm overwrite\n    promptOverwrite(e.path);\n  } else if (e instanceof KoolbaseStoragePermissionError) {\n    showError('You do not have permission to upload here.');\n  } else if (e instanceof KoolbaseStorageError) {\n    // Catch-all for any other storage error\n    showError(e.message);\n  } else {\n    throw e;\n  }\n}\n```\n\n---\n\n## What's included\n\n- Authentication: email + password, Apple Sign-In, Google Sign-In, phone + OTP\n- Database with offline-first cache, realtime subscriptions, and populate\n- Storage with presigned uploads and downloads, safe-by-default conflict handling\n- Realtime subscriptions over WebSocket\n- Authenticated functions (`ctx.auth` exposes the caller automatically)\n- Feature flags and remote config\n- Version enforcement\n- Code push (config + flag overrides + directives, no store release)\n- Logic engine (conditional flows as data, updatable OTA)\n- Analytics (DAU/WAU/MAU, funnels, retention)\n- Cloud Messaging (FCM token registration, targeted send, broadcast)\n- TypeScript-native with full type definitions\n\n---\n\n## Documentation\n\nFull documentation at [docs.koolbase.com](https://docs.koolbase.com)\n\n## Dashboard\n\nManage your projects at [app.koolbase.com](https://app.koolbase.com)\n\n## Support\n\n- [GitHub Issues](https://github.com/kennedyowusu/koolbase-react-native/issues)\n- [docs.koolbase.com](https://docs.koolbase.com)\n- Email: \u003chello@koolbase.com\u003e\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkennedyowusu%2Fkoolbase-react-native","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkennedyowusu%2Fkoolbase-react-native","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkennedyowusu%2Fkoolbase-react-native/lists"}