{"_id":"@airostack/client","_rev":"8-d662106bf2f58eba86ba7918d8836d31","name":"@airostack/client","dist-tags":{"latest":"0.6.0"},"versions":{"0.1.0":{"name":"@airostack/client","version":"0.1.0","keywords":["airostack","postgrest","database","auth","baas","react-native"],"license":"MIT","_id":"@airostack/client@0.1.0","maintainers":[{"name":"airoxlab","email":"airoxlab@gmail.com"}],"homepage":"https://github.com/airoxlab/baseway#readme","bugs":{"url":"https://github.com/airoxlab/baseway/issues"},"dist":{"shasum":"cdcd012cd95451f550d35f9ef5b526dae5dec505","tarball":"https://registry.npmjs.org/@airostack/client/-/client-0.1.0.tgz","fileCount":18,"integrity":"sha512-I1wPulCUWOgxLyq2whsr5nM4xyNZtuQveS6xe8tryomTZlyAzLVfe1KeO5E3vtsUmhPzcTbcpPM0B8k6NtUoUg==","signatures":[{"sig":"MEUCIEviOr6ohY6C1Fdwc0CF5srKnrmXf8K+6IaUsrMfytjfAiEAjCM+aRiFHiIfKwKwbVi4tIGmFH009VoRbzv3HDvynhQ=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":43108},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","default":"./dist/index.js"}},"gitHead":"6a989e13c93e75190e4e920911bbdb2f6e18b9ee","scripts":{"build":"tsc -p tsconfig.build.json","clean":"rimraf dist .turbo","typecheck":"tsc --noEmit","prepublishOnly":"npm run build"},"_npmUser":{"name":"airoxlab","email":"airoxlab@gmail.com"},"repository":{"url":"git+https://github.com/airoxlab/baseway.git","type":"git","directory":"packages/client"},"_npmVersion":"11.9.0","description":"Isomorphic client for AiroStack projects — Supabase-style Data API queries and Auth. Works in Node.js, browsers, and React Native.","directories":{},"sideEffects":false,"_nodeVersion":"24.14.0","_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.7.2"},"_npmOperationalInternal":{"tmp":"tmp/client_0.1.0_1783623353612_0.7357933230607321","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@airostack/client","version":"0.2.0","keywords":["airostack","postgrest","database","auth","baas","react-native"],"license":"MIT","_id":"@airostack/client@0.2.0","maintainers":[{"name":"airoxlab","email":"airoxlab@gmail.com"}],"homepage":"https://github.com/airoxlab/baseway#readme","bugs":{"url":"https://github.com/airoxlab/baseway/issues"},"dist":{"shasum":"856699b2f80386211cc2ff1efda716ee9470d09a","tarball":"https://registry.npmjs.org/@airostack/client/-/client-0.2.0.tgz","fileCount":26,"integrity":"sha512-lhG+CvyNeuF83e8aHLOBOnTfg0WvA6Rwv8ygRX8HzQFoBItp6Pc91NUjX6Wp+ENdPLz3k7TBb9fc4RQHWCu2tQ==","signatures":[{"sig":"MEUCIQDmst4A8fVNnLCBCRl0xO4PX5ZWjjXVy0GME8enZwelxwIgC1dMRx9iVO3utmQzeSRdonJcyZoyLDryIKsjR2CnWZ0=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":82268},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","default":"./dist/index.js"}},"gitHead":"7f2a73c8f01d679e2fd2469bc811e6293bf0e957","scripts":{"build":"tsc -p tsconfig.build.json","clean":"rimraf dist .turbo","typecheck":"tsc --noEmit","prepublishOnly":"npm run build"},"_npmUser":{"name":"airoxlab","email":"airoxlab@gmail.com"},"repository":{"url":"git+https://github.com/airoxlab/baseway.git","type":"git","directory":"packages/client"},"_npmVersion":"11.9.0","description":"Isomorphic client for AiroStack projects — Supabase-style database queries, auth, RPC, storage, and realtime. Works in Node.js, browsers, and React Native.","directories":{},"sideEffects":false,"_nodeVersion":"24.14.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.7.2"},"_npmOperationalInternal":{"tmp":"tmp/client_0.2.0_1783673736891_0.8117557477991038","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"@airostack/client","version":"0.3.0","keywords":["airostack","postgrest","database","auth","baas","react-native"],"license":"MIT","_id":"@airostack/client@0.3.0","maintainers":[{"name":"airoxlab","email":"airoxlab@gmail.com"}],"homepage":"https://github.com/airoxlab/baseway#readme","bugs":{"url":"https://github.com/airoxlab/baseway/issues"},"dist":{"shasum":"cb711f759314fd68b0dcca15ce04aecf91e38768","tarball":"https://registry.npmjs.org/@airostack/client/-/client-0.3.0.tgz","fileCount":26,"integrity":"sha512-MhmtMkGDGcL5kkMturV+VD63g8bKhlNXQE6Ot89nu2jfYxOVrOaQRxWJ6W0QVz2o/1foi2o5uhbqZjtvpLKiyQ==","signatures":[{"sig":"MEYCIQDnKPAttOu/pmLXcyYuzrU6stqFikuK7fCEKlxHsHGh/gIhAMOfTKKduhub2hE39pY2JGSrjVRdTH1HFeEic3jn+526","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":85351},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","default":"./dist/index.js"}},"gitHead":"0410e01cdee95caf7062b7909150c9322bd15371","scripts":{"build":"tsc -p tsconfig.build.json","clean":"rimraf dist .turbo","typecheck":"tsc --noEmit","prepublishOnly":"npm run build"},"_npmUser":{"name":"airoxlab","email":"airoxlab@gmail.com"},"repository":{"url":"git+https://github.com/airoxlab/baseway.git","type":"git","directory":"packages/client"},"_npmVersion":"11.9.0","description":"Isomorphic client for AiroStack projects — Supabase-style database queries, auth, RPC, storage, and realtime. Works in Node.js, browsers, and React Native.","directories":{},"sideEffects":false,"_nodeVersion":"24.14.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.7.2"},"_npmOperationalInternal":{"tmp":"tmp/client_0.3.0_1783777340301_0.2772219184036293","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"name":"@airostack/client","version":"0.4.0","keywords":["airostack","postgrest","database","auth","baas","react-native"],"license":"MIT","_id":"@airostack/client@0.4.0","maintainers":[{"name":"airoxlab","email":"airoxlab@gmail.com"}],"homepage":"https://github.com/airoxlab/baseway#readme","bugs":{"url":"https://github.com/airoxlab/baseway/issues"},"dist":{"shasum":"7d2c010e6463f17f7dc270b370b71e61cc37771c","tarball":"https://registry.npmjs.org/@airostack/client/-/client-0.4.0.tgz","fileCount":26,"integrity":"sha512-Swwo19x3LUz7j+Ptn+59YVp0YUiiQQuEYeG45D97HNSFWie75MH/YQPMh9Q571XQBUH3vmd+jHpoFT5GJ67x2A==","signatures":[{"sig":"MEYCIQDT8zvkIIgtT3cIfbKLmQblXCOcLOklY22coXs8SAqveAIhAO3yD7IpitAOI0jtDR3AyBgVC2DMJ/uwFKNvUwtQljKg","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":96720},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","default":"./dist/index.js"}},"gitHead":"03086ef0efad3f15f9e850fde94a19c9967fffe5","scripts":{"build":"tsc -p tsconfig.build.json","clean":"rimraf dist .turbo","typecheck":"tsc --noEmit","prepublishOnly":"npm run build"},"_npmUser":{"name":"airoxlab","email":"airoxlab@gmail.com"},"repository":{"url":"git+https://github.com/airoxlab/baseway.git","type":"git","directory":"packages/client"},"_npmVersion":"11.9.0","description":"Isomorphic client for AiroStack projects — Supabase-style database queries, auth, RPC, storage, and realtime. Works in Node.js, browsers, and React Native.","directories":{},"sideEffects":false,"_nodeVersion":"24.14.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.7.2"},"_npmOperationalInternal":{"tmp":"tmp/client_0.4.0_1783779333069_0.849140115005597","host":"s3://npm-registry-packages-npm-production"}},"0.4.3":{"name":"@airostack/client","version":"0.4.3","keywords":["airostack","postgrest","database","auth","baas","react-native"],"license":"MIT","_id":"@airostack/client@0.4.3","maintainers":[{"name":"airoxlab","email":"airoxlab@gmail.com"}],"homepage":"https://github.com/airoxlab/baseway#readme","bugs":{"url":"https://github.com/airoxlab/baseway/issues"},"dist":{"shasum":"cad77f7fa1a9a5da02ee2f99e334cc4a730d9ca7","tarball":"https://registry.npmjs.org/@airostack/client/-/client-0.4.3.tgz","fileCount":26,"integrity":"sha512-qYxXl/wtzzrhxHEM6HdvXx8/516R0koNtvt9gXZgnCbLXnBaI8Ji6lE5qX4ki/Lv4M77kKZe6Vq6t5VeKJuE7Q==","signatures":[{"sig":"MEUCIFYWrrKVqhcDsZJOezaygtuQM8mqKlmj2vUIAus3BF9OAiEA05kO6pjLHtVkSaNrYiREPMi4a5PjJx5QWB7ImW0HU6s=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":101010},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","default":"./dist/index.js"}},"gitHead":"c8c9f6ad1e631ee4431b3202034ce96ef4027f09","scripts":{"build":"tsc -p tsconfig.build.json","clean":"rimraf dist .turbo","typecheck":"tsc --noEmit","prepublishOnly":"npm run build"},"_npmUser":{"name":"airoxlab","email":"airoxlab@gmail.com"},"repository":{"url":"git+https://github.com/airoxlab/baseway.git","type":"git","directory":"packages/client"},"_npmVersion":"11.9.0","description":"Isomorphic client for AiroStack projects — Supabase-style database queries, auth, RPC, storage, and realtime. Works in Node.js, browsers, and React Native.","directories":{},"sideEffects":false,"_nodeVersion":"24.14.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.7.2"},"_npmOperationalInternal":{"tmp":"tmp/client_0.4.3_1784187484745_0.639432271737405","host":"s3://npm-registry-packages-npm-production"}},"0.5.0":{"name":"@airostack/client","version":"0.5.0","keywords":["airostack","postgrest","database","auth","baas","react-native"],"license":"MIT","_id":"@airostack/client@0.5.0","maintainers":[{"name":"airoxlab","email":"airoxlab@gmail.com"}],"homepage":"https://github.com/airoxlab/baseway#readme","bugs":{"url":"https://github.com/airoxlab/baseway/issues"},"dist":{"shasum":"330e231586b8a0501d61ad8998007c0ba1ad6963","tarball":"https://registry.npmjs.org/@airostack/client/-/client-0.5.0.tgz","fileCount":26,"integrity":"sha512-QpvXNmq4Q06ZCgUhNd+WteiPCLBT1V9JBrqYg4Y2Mo8TPKk5JQ0vfyLiWEO+gr59mZLC+qEpDGMexskuHgW8rg==","signatures":[{"sig":"MEQCIH2cTqon6pr89GIZ5LSK73Z/rVK4eEBrM14uYSUFVoooAiAFaQA9elavKqK8u9SPF7xJLNnHJOIXbMJ2XcT48QuWDw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":110639},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","default":"./dist/index.js"}},"gitHead":"d3435d1587b438c48c72fc3f9911a66a6954c76c","scripts":{"build":"tsc -p tsconfig.build.json","clean":"rimraf dist .turbo","typecheck":"tsc --noEmit","prepublishOnly":"npm run build"},"_npmUser":{"name":"airoxlab","email":"airoxlab@gmail.com"},"repository":{"url":"git+https://github.com/airoxlab/baseway.git","type":"git","directory":"packages/client"},"_npmVersion":"11.9.0","description":"Isomorphic client for AiroStack projects — Supabase-style database queries, auth, RPC, storage, and realtime. Works in Node.js, browsers, and React Native.","directories":{},"sideEffects":false,"_nodeVersion":"24.14.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.7.2"},"_npmOperationalInternal":{"tmp":"tmp/client_0.5.0_1784384161372_0.9457035043679913","host":"s3://npm-registry-packages-npm-production"}},"0.5.1":{"name":"@airostack/client","version":"0.5.1","keywords":["airostack","postgrest","database","auth","baas","react-native"],"license":"MIT","_id":"@airostack/client@0.5.1","maintainers":[{"name":"airoxlab","email":"airoxlab@gmail.com"}],"homepage":"https://github.com/airoxlab/baseway#readme","bugs":{"url":"https://github.com/airoxlab/baseway/issues"},"dist":{"shasum":"afff7bf6ada135ed820e6aff4515e3ba44ce5d1e","tarball":"https://registry.npmjs.org/@airostack/client/-/client-0.5.1.tgz","fileCount":39,"integrity":"sha512-pr3oH2eJPxr1cW/jexcMzvekBHUqFWUd32rDHZtslB9dLiB5UwsOOjAooL8bbBpBzUzs0AMBf1VhzYsF55tO5w==","signatures":[{"sig":"MEUCIHTog4+Ks9n7GcHOVvEtdZoW6CjfLOQ6DLq/4J3ftzo3AiEA3+AfCtGUxsg6LckB008HtZrWVr1Jka0lkgk1gN4Yw5I=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":180201},"main":"./dist/cjs/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","default":"./dist/index.js","require":"./dist/cjs/index.js"}},"gitHead":"865204c9b424def290c1617047fa52138dafcfaf","scripts":{"build":"tsc -p tsconfig.build.json && tsc -p tsconfig.cjs.json && node -e \"require('fs').writeFileSync('dist/cjs/package.json', JSON.stringify({ type: 'commonjs' }))\"","clean":"rimraf dist .turbo","typecheck":"tsc --noEmit","prepublishOnly":"npm run build"},"_npmUser":{"name":"airoxlab","email":"airoxlab@gmail.com"},"repository":{"url":"git+https://github.com/airoxlab/baseway.git","type":"git","directory":"packages/client"},"_npmVersion":"11.9.0","description":"Isomorphic client for AiroStack projects — Supabase-style database queries, auth, RPC, storage, and realtime. Works in Node.js, browsers, and React Native.","directories":{},"sideEffects":false,"_nodeVersion":"24.14.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.7.2"},"_npmOperationalInternal":{"tmp":"tmp/client_0.5.1_1784386580669_0.2576499633761342","host":"s3://npm-registry-packages-npm-production"}},"0.6.0":{"name":"@airostack/client","version":"0.6.0","description":"Isomorphic client for AiroStack projects — Supabase-style database queries, auth, RPC, storage, and realtime. Works in Node.js, browsers, and React Native.","license":"MIT","repository":{"type":"git","url":"git+https://github.com/airoxlab/baseway.git","directory":"packages/client"},"publishConfig":{"access":"public"},"keywords":["airostack","postgrest","database","auth","baas","react-native"],"type":"module","main":"./dist/cjs/index.js","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/cjs/index.js","default":"./dist/index.js"}},"sideEffects":false,"scripts":{"build":"tsc -p tsconfig.build.json && tsc -p tsconfig.cjs.json && node -e \"require('fs').writeFileSync('dist/cjs/package.json', JSON.stringify({ type: 'commonjs' }))\"","typecheck":"tsc --noEmit","clean":"rimraf dist .turbo","prepublishOnly":"npm run build"},"devDependencies":{"typescript":"^5.7.2"},"gitHead":"f7931a70ffb113c91757fd8ca9312dcecf82b985","_id":"@airostack/client@0.6.0","bugs":{"url":"https://github.com/airoxlab/baseway/issues"},"homepage":"https://github.com/airoxlab/baseway#readme","_nodeVersion":"24.14.0","_npmVersion":"11.9.0","dist":{"integrity":"sha512-6kbIGtlTvS2ukpjJA0HTFD2t5VjirOR7Q5QaHzYyP6auFz/S/07dbokFEDFRq8y47zKyuoJBjZgJriB3LKDLMw==","shasum":"1c8e681bbbb32584bd228c95b3de0578c7d04196","tarball":"https://registry.npmjs.org/@airostack/client/-/client-0.6.0.tgz","fileCount":39,"unpackedSize":187444,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDnoIWjCRp8D/CnYCJVxHAz6QM5+iogg5dhrE3vb81vkAIhAJPZ9bGn9tQkzpYIPHFGyfgPAhETdjxX97ub2MGtiXEL"}]},"_npmUser":{"name":"airoxlab","email":"airoxlab@gmail.com"},"directories":{},"maintainers":[{"name":"airoxlab","email":"airoxlab@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/client_0.6.0_1784459079320_0.362043458235243"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-09T18:55:52.805Z","modified":"2026-07-19T11:04:39.644Z","0.1.0":"2026-07-09T18:55:53.872Z","0.2.0":"2026-07-10T08:55:37.041Z","0.3.0":"2026-07-11T13:42:20.476Z","0.4.0":"2026-07-11T14:15:33.217Z","0.4.3":"2026-07-16T07:38:04.887Z","0.5.0":"2026-07-18T14:16:01.526Z","0.5.1":"2026-07-18T14:56:20.802Z","0.6.0":"2026-07-19T11:04:39.463Z"},"bugs":{"url":"https://github.com/airoxlab/baseway/issues"},"license":"MIT","homepage":"https://github.com/airoxlab/baseway#readme","keywords":["airostack","postgrest","database","auth","baas","react-native"],"repository":{"type":"git","url":"git+https://github.com/airoxlab/baseway.git","directory":"packages/client"},"description":"Isomorphic client for AiroStack projects — Supabase-style database queries, auth, RPC, storage, and realtime. Works in Node.js, browsers, and React Native.","maintainers":[{"name":"airoxlab","email":"airoxlab@gmail.com"}],"readme":"# @airostack/client\n\nOfficial client for [AiroStack](https://console.airoxlab.com) projects — Supabase-style database queries and end-user authentication over your project's Data API.\n\n- **Zero dependencies** — plain `fetch` under the hood\n- **Isomorphic** — Node.js ≥ 18, all browsers, React Native / Expo, edge runtimes\n- **Never throws** — every call resolves to `{ data, error }`\n- **RLS-aware** — signed-in users' JWTs ride along automatically, so your Row Level Security policies apply per user\n\n```bash\nnpm install @airostack/client\n```\n\n---\n\n## 1. Creating a client\n\n```ts\nimport { createClient } from '@airostack/client';\n\nconst client = createClient(\n  'https://<project-ref>.db.airoxlab.com', // Project URL  → dashboard → project → Connect\n  '<publishable-key>',                     // Publishable key → project → API settings\n);\n```\n\n### All options\n\n```ts\nconst client = createClient(url, key, {\n  fetch: customFetch,          // bring your own fetch (rarely needed)\n  auth: {\n    persistSession: true,      // keep users signed in across reloads (default true)\n    storage: AsyncStorage,     // WHERE sessions persist — see React Native below\n    storageKey: 'my.app.auth', // storage key (default 'airostack.auth.token')\n  },\n});\n```\n\n### The two keys\n\n| Key | Where to use | Powers |\n|---|---|---|\n| **Publishable** | browsers, mobile apps — safe to ship | anonymous reads + RLS-scoped access; upgrades to the signed-in user when they log in |\n| **Secret** | servers ONLY — never ship to a client | full read/write, bypasses RLS |\n\n```ts\n// server-side admin client (API routes, cron jobs, workers)\nconst admin = createClient(url, process.env.AIROSTACK_SECRET_KEY!, {\n  auth: { persistSession: false },\n});\n```\n\n### React Native / Expo\n\nSessions persist to `localStorage` by default, which doesn't exist in React Native — pass AsyncStorage:\n\n```ts\nimport AsyncStorage from '@react-native-async-storage/async-storage';\n\nconst client = createClient(url, key, { auth: { storage: AsyncStorage } });\n```\n\nEverything else is identical across platforms.\n\n---\n\n## 2. Database queries — `client.from(table)`\n\nEvery chain is *thenable*: `await` it whenever you're done building. The result is always:\n\n```ts\n{\n  data:   T[] | T | null,  // rows (or a single row after .single())\n  error:  ApiError | null, // never thrown — always check this\n  count:  number | null,   // total rows when { count: 'exact' } was requested\n  status: number,          // HTTP status\n}\n```\n\n### Reading\n\n```ts\n// all columns\nconst { data, error } = await client.from('tasks').select();\n\n// specific columns\nawait client.from('tasks').select('id, title, done');\n\n// filters (chainable, ANDed together)\nawait client.from('tasks').select().eq('done', false).gte('priority', 3);\n\n// sorting + paging\nawait client.from('tasks')\n  .select()\n  .order('created_at', { ascending: false })\n  .limit(20);\n\n// page 2 (rows 20–39)\nawait client.from('tasks').select().range(20, 39);\n\n// exactly one row — error if none\nconst { data: task } = await client.from('tasks').select().eq('id', id).single();\n\n// one row or null — no error if none\nconst { data: maybe } = await client.from('tasks').select().eq('id', id).maybeSingle();\n\n// total count (for pagination UIs)\nconst { data: rows, count } = await client\n  .from('tasks')\n  .select('*', { count: 'exact' })\n  .limit(10);\n// count = total matching rows, rows = the first 10\n```\n\n### Embedded (nested) selects\n\nJoin related tables straight in the select string — the foreign keys decide the shape. A table your row **points at** embeds as an object; a table **pointing at** your row embeds as an array. Nesting and `alias:table(cols)` renames work, and RLS applies to the embedded tables too.\n\n```ts\n// items with their assignees' profiles (two levels)\nconst { data } = await client\n  .from('items')\n  .select('id, name, item_assignees(profiles(full_name, avatar_url))');\n// → [{ id, name, item_assignees: [{ profiles: { full_name, avatar_url } }] }]\n\n// many-to-one → object; alias the key\nawait client.from('orders').select('*, customer:customers(name, email)');\n// → [{ ..., customer: { name, email } | null }]\n\n// embed via the FK COLUMN (also how you disambiguate multiple FKs to one table)\nawait client.from('notifications').select('id, actor:actor_id(full_name), item:item_id(name)');\n\n// ...or with an FK constraint-name hint\nawait client.from('notifications').select('id, actor:profiles!notifications_actor_id_fkey(full_name)');\n\n// !inner + filtering on an embedded column — constrains the BASE rows too\nawait client.from('activities')\n  .select('id, action, items!inner(id, name, board_id)')\n  .eq('items.board_id', boardId);\n```\n\n### All filters\n\n| Method | SQL equivalent | Example |\n|---|---|---|\n| `.eq(col, v)` | `col = v` | `.eq('status', 'open')` |\n| `.neq(col, v)` | `col <> v` | `.neq('status', 'done')` |\n| `.gt(col, v)` | `col > v` | `.gt('qty', 10)` |\n| `.gte(col, v)` | `col >= v` | `.gte('price', 100)` |\n| `.lt(col, v)` | `col < v` | `.lt('age', 18)` |\n| `.lte(col, v)` | `col <= v` | `.lte('stock', 5)` |\n| `.like(col, p)` | `col LIKE p` | `.like('name', 'A%')` |\n| `.ilike(col, p)` | `col ILIKE p` (case-insensitive) | `.ilike('email', '%@gmail.com')` |\n| `.filter(col, op, v)` | any operator | `.filter('qty', 'gte', 10)` |\n\n### Inserting\n\n```ts\n// single row — returns the created row(s) in data\nconst { data, error } = await client.from('tasks').insert({ title: 'Ship v1', done: false });\n\n// bulk\nawait client.from('tasks').insert([\n  { title: 'One' },\n  { title: 'Two' },\n]);\n```\n\n### Upserting\n\n```ts\n// insert-or-update on the primary key\nawait client.from('profiles').upsert({ id: userId, full_name: 'Ada' });\n\n// custom conflict target (unique columns)\nawait client.from('user_preferences').upsert(\n  { user_id: userId, timezone: 'Asia/Karachi' },\n  { onConflict: 'user_id' },\n);\n\n// join tables (follow/like/read-receipt patterns): skip existing rows\nawait client.from('item_subscribers').upsert(\n  { item_id: itemId, user_id: userId },\n  { onConflict: 'item_id,user_id', ignoreDuplicates: true },\n);\n```\n\n### Updating\n\n```ts\n// ALWAYS pair .update() with filters — they choose which rows change\nconst { data } = await client\n  .from('tasks')\n  .update({ done: true })\n  .eq('id', taskId);\n// data = the updated rows\n```\n\n### Deleting\n\n```ts\nawait client.from('tasks').delete().eq('id', taskId);\n```\n\n### Typed rows (TypeScript)\n\n```ts\ninterface Task { id: string; title: string; done: boolean; created_at: string }\n\nconst { data } = await client.from<Task>('tasks').select().eq('done', false);\n// data: Task[] | null — fully typed\n```\n\n### Error handling\n\nNothing throws. Check `error` on every result:\n\n```ts\nconst { data, error } = await client.from('tasks').select();\nif (error) {\n  console.error(error.message, error.status); // ApiError: message + HTTP status\n  return;\n}\n// data is safe to use here\n```\n\n---\n\n## 3. Authentication — `client.auth`\n\nEnd-user accounts for **your app's users** (not dashboard accounts). Sessions persist automatically and access tokens refresh themselves.\n\n### Sign up\n\n```ts\nconst { data, error } = await client.auth.signUp({\n  email: 'ada@example.com',\n  password: 'secret123',\n  options: { data: { name: 'Ada Lovelace' } },  // optional user metadata\n});\n// data.user, data.session — signed in immediately\n```\n\n### Sign in / out\n\n```ts\nconst { data, error } = await client.auth.signInWithPassword({\n  email: 'ada@example.com',\n  password: 'secret123',\n});\n\nawait client.auth.signOut(); // drops the stored session\n```\n\n### Current session & user\n\n```ts\n// fast — local session (auto-refreshes the token if it expired)\nconst { data: { session } } = await client.auth.getSession();\nif (session) console.log(session.user.email, session.access_token);\n\n// verified — validates the token against the server\nconst { data: { user }, error } = await client.auth.getUser();\n```\n\n### Reacting to auth changes\n\n```ts\nconst sub = client.auth.onAuthStateChange((event, session) => {\n  // event: 'SIGNED_IN' | 'SIGNED_OUT' | 'TOKEN_REFRESHED'\n  setUser(session?.user ?? null);\n});\n\nsub.unsubscribe(); // e.g. in a useEffect cleanup\n```\n\n### Auth + RLS: how they connect\n\nWhile a user is signed in, **every `client.from()` query automatically carries their JWT**. On the server, your Row Level Security policies see them via `auth.uid()`, `auth.email()`, `auth.role()`:\n\n```sql\n-- example policy: users only see their own tasks\ncreate policy \"own tasks\" on tasks\n  for select using (auth.uid() = user_id);\n```\n\nNo extra client code — sign in, and the same `.from('tasks').select()` now returns only that user's rows.\n\n---\n\n## 4. Recipes\n\n**Next.js — one shared client**\n\n```ts\n// src/lib/airostack.ts\nimport { createClient } from '@airostack/client';\n\nexport const client = createClient(\n  process.env.NEXT_PUBLIC_AIROSTACK_URL!,\n  process.env.NEXT_PUBLIC_AIROSTACK_KEY!,\n);\n```\n\n**React login form**\n\n```tsx\nasync function onSubmit(email: string, password: string) {\n  const { error } = await client.auth.signInWithPassword({ email, password });\n  if (error) return setMessage(error.message);\n  router.push('/dashboard');\n}\n```\n\n**Server route with full access**\n\n```ts\n// app/api/report/route.ts — secret key, server only\nimport { createClient } from '@airostack/client';\n\nconst admin = createClient(process.env.AIROSTACK_URL!, process.env.AIROSTACK_SECRET_KEY!, {\n  auth: { persistSession: false },\n});\n\nexport async function GET() {\n  const { data } = await admin.from('orders').select('*', { count: 'exact' }).limit(1000);\n  return Response.json(data);\n}\n```\n\n**Pagination UI**\n\n```ts\nconst PAGE = 25;\nconst { data, count } = await client\n  .from('orders')\n  .select('*', { count: 'exact' })\n  .order('created_at', { ascending: false })\n  .range(page * PAGE, page * PAGE + PAGE - 1);\nconst totalPages = Math.ceil((count ?? 0) / PAGE);\n```\n\n---\n\n## 5. Exported types\n\n```ts\nimport type {\n  ApiResult,      // { data, error, count, status }\n  ApiError,       // Error subclass with .status\n  User,           // { id, email, user_metadata?, created_at? }\n  Session,        // { access_token, refresh_token, expires_in, expires_at?, user }\n  AuthResult,     // { data: { user, session }, error }\n  StorageAdapter, // getItem/setItem/removeItem — for custom session storage\n  ClientOptions,  // createClient's third argument\n} from '@airostack/client';\n```\n\n## 6. RPC — calling Postgres functions\n\nDefine a function in the SQL editor, then call it by name with named arguments:\n\n```sql\ncreate or replace function total_revenue(since date)\nreturns numeric language sql stable as $$\n  select coalesce(sum(amount), 0) from orders where created_at >= since;\n$$;\n```\n\n```ts\nconst { data, error } = await client.rpc('total_revenue', { since: '2026-01-01' });\n// scalar functions return the value directly; set-returning functions return rows\n\nconst { data: rows } = await client.rpc<Order[]>('orders_by_region', { region: 'EU' });\n```\n\nRPC runs under the caller's role — RLS and `security definer/invoker` behave exactly as in SQL.\n\n## 7. Storage — `client.storage.from(bucket)`\n\nCreate buckets in the dashboard (project → Storage). Access rules: **reads on\npublic buckets** work with the publishable key; **private buckets and all\nwrites** need a signed-in user or the secret key.\n\n```ts\nconst avatars = client.storage.from('avatars');\n\n// upload (Blob, File, ArrayBuffer, Uint8Array, or string)\nconst { error } = await avatars.upload('u1/profile.png', file, { contentType: 'image/png' });\n\n// download → Blob\nconst { data: blob } = await avatars.download('u1/profile.png');\n\n// permanent public URL (public buckets) — great for <img src>\nconst { data: { publicUrl } } = avatars.getPublicUrl('u1/profile.png');\n\n// time-limited URL for a PRIVATE bucket (the link carries the grant)\nconst { data: signed } = await avatars.createSignedUrl('u1/profile.png', 3600);\n\n// list + delete\nconst { data: files } = await avatars.list('u1/', { limit: 50 });\nawait avatars.remove(['u1/profile.png']);\n```\n\n## 8. Realtime — `client.channel(name)`\n\nFirst enable realtime for the table in the dashboard (project → Realtime).\nThen subscribe to live changes:\n\n```ts\nconst channel = client\n  .channel('orders-feed')\n  .on('postgres_changes', { event: '*', schema: 'public', table: 'orders' }, (payload) => {\n    // payload: { eventType: 'INSERT'|'UPDATE'|'DELETE', schema, table, new, old }\n    console.log(payload.eventType, payload.new);\n  })\n  .subscribe((status) => console.log(status)); // 'SUBSCRIBED' | 'CLOSED' | 'CHANNEL_ERROR'\n\n// later\nclient.removeChannel(channel);   // or channel.unsubscribe()\nclient.removeAllChannels();      // app teardown\n```\n\n- Filter per event: `{ event: 'INSERT', table: 'orders' }` — or `'*'` for all.\n- Reconnects automatically with backoff.\n- **Node < 22** has no global WebSocket — pass one:\n\n```ts\nimport ws from 'ws';\nconst client = createClient(url, key, { realtime: { WebSocket: ws as never } });\n```\n\nBrowsers, React Native, and Node ≥ 22 need nothing extra.\n\n## 9. Direct SQL (alternative)\n\nFor complex server-side queries you can always skip the API and connect straight\nto your project's Postgres (`db.airoxlab.com:5432`, connection string in the\ndashboard's Connect menu) with any SQL client — the Data API and direct SQL are\nthe same database.\n\n## License\n\nMIT\n","readmeFilename":"README.md"}