{"_id":"@adaiasmagdiel/pdo-restify","name":"@adaiasmagdiel/pdo-restify","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@adaiasmagdiel/pdo-restify","version":"0.1.0","description":"Typed JS/TS client for pdo-restify — a framework-agnostic REST API layer on top of PDO.","license":"LGPL-3.0-or-later","author":{"name":"Adaias Magdiel"},"homepage":"https://github.com/adaiasmagdiel/pdo-restify/tree/main/clients/js","repository":{"type":"git","url":"git+https://github.com/adaiasmagdiel/pdo-restify.git","directory":"clients/js"},"keywords":["pdo-restify","rest","api","postgrest","sql","client"],"type":"module","sideEffects":false,"main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"engines":{"node":">=20"},"scripts":{"build":"tsup","prepublishOnly":"npm run typecheck && npm run build","test":"vitest run","test:watch":"vitest","typecheck":"tsc --noEmit"},"devDependencies":{"@types/node":"^26.4.0","@vitest/coverage-v8":"^4.1.11","tsup":"^8.5.1","typescript":"^5.7.2","vitest":"^4.1.11"},"gitHead":"b3694348297288c7ef5b235c5ca307e97906f9f9","_id":"@adaiasmagdiel/pdo-restify@0.1.0","bugs":{"url":"https://github.com/adaiasmagdiel/pdo-restify/issues"},"_nodeVersion":"24.11.1","_npmVersion":"11.12.1","dist":{"integrity":"sha512-fzYD3EL2R1gg6d6FkxIQZDCxUbT2eQNLVcwGHjJKEyApcK44jk925jy+nQ/OkNhxSE+jg5lBKRCTs+ACOHGYrw==","shasum":"2a58d159f87762d2086802b6bdc3edcb78e9b4a2","tarball":"https://registry.npmjs.org/@adaiasmagdiel/pdo-restify/-/pdo-restify-0.1.0.tgz","fileCount":10,"unpackedSize":76370,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCN6KgFoyoXAm6yPbo4CeQ9fDP/fYOCU4Lqj2COEqG4GgIgPdeOQafA98TFNLgAkWqLLaWON9Azus8AxzeLffJJwZg="}]},"_npmUser":{"name":"adaias_magdiel","email":"adaiasmagdiell@gmail.com"},"directories":{},"maintainers":[{"name":"adaias_magdiel","email":"adaiasmagdiell@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/pdo-restify_0.1.0_1787804444440_0.3241103476453733"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-27T04:20:44.254Z","0.1.0":"2026-08-27T04:20:44.581Z","modified":"2026-08-27T04:20:44.802Z"},"maintainers":[{"name":"adaias_magdiel","email":"adaiasmagdiell@gmail.com"}],"description":"Typed JS/TS client for pdo-restify — a framework-agnostic REST API layer on top of PDO.","homepage":"https://github.com/adaiasmagdiel/pdo-restify/tree/main/clients/js","keywords":["pdo-restify","rest","api","postgrest","sql","client"],"repository":{"type":"git","url":"git+https://github.com/adaiasmagdiel/pdo-restify.git","directory":"clients/js"},"author":{"name":"Adaias Magdiel"},"bugs":{"url":"https://github.com/adaiasmagdiel/pdo-restify/issues"},"license":"LGPL-3.0-or-later","readme":"# @adaiasmagdiel/pdo-restify\n\nA typed JS/TS client for [pdo-restify](https://github.com/adaiasmagdiel/pdo-restify) —\nthe PHP library this package lives alongside. It's a thin wrapper around\n`fetch`: it builds the `column=operator.value` query strings and paths\npdo-restify expects, and normalizes the response into a predictable\n`{ data, error, status }` shape. No dependencies, works in browsers, Node\n20+, and edge runtimes (anywhere with a global `fetch`).\n\n> Early, minimal first version, matching the PHP library's own scope — see\n> its [README](../../README.md#roadmap) for what's planned beyond this.\n\n## Install\n\n```bash\nnpm install @adaiasmagdiel/pdo-restify\n```\n\nDon't use npm/TypeScript/a bundler at all? Drop this in a plain `<script>`\ntag — no build step, no install, no toolchain:\n\n```html\n<script src=\"https://cdn.jsdelivr.net/npm/@adaiasmagdiel/pdo-restify/dist/index.global.js\"></script>\n<script>\n  const api = PdoRestify.createClient('https://api.example.com/');\n\n  api.from('posts').select().then(({ data, error }) => {\n    console.log(data);\n  });\n</script>\n```\n\nThat's the exact same client, just built as a plain global (`window.PdoRestify`)\ninstead of an ES module — served straight off the npm package via\n[jsDelivr](https://www.jsdelivr.com/) (unpkg.com works the same way, if you\nprefer it). Every method shown below works identically; only the `import`\nline differs.\n\n## Quick start\n\n```ts\nimport { createClient } from '@adaiasmagdiel/pdo-restify';\n\nconst api = createClient('https://api.example.com/', {\n  headers: { Authorization: `Bearer ${token}` },\n});\n\ninterface Post {\n  id: number;\n  title: string;\n  body: string;\n  user_id: number;\n}\n\nconst { data, error } = await api\n  .from<Post>('posts')\n  .select('id,title')\n  .eq('user_id', 42)\n  .order('id', 'desc')\n  .limit(10);\n\nif (error) {\n  console.error(error.message, error.status);\n} else {\n  console.log(data); // Post[] (only id/title, per select())\n}\n```\n\nEvery request builder is **thenable** — `await`ing it is what actually sends\nthe request. Nothing fires until then, and awaiting the same builder twice\nonly sends one request (the result is cached).\n\n## API\n\n### `createClient(baseUrl, options?)`\n\n`baseUrl` is where pdo-restify is mounted, e.g. `'https://api.example.com/'`\nor `'https://api.example.com/v1/'` if it's behind a prefix — a trailing\nslash is added if you leave it off.\n\n`options`:\n\n- `headers` — a plain object, or a function (sync or async) returning one,\n  called fresh before every request. Use the function form for a token that\n  can expire or rotate mid-session.\n- `fetch` — override the `fetch` implementation (mainly for tests, or a\n  runtime without a global one).\n\n### `client.from<T>(table)`\n\nReturns a `TableClient<T>` scoped to `table`. `T` is the row shape — it's\nyour responsibility to keep it in sync with the resource's `columns()` on\nthe PHP side; nothing here validates it against the server.\n\n### Reading\n\n```ts\n// GET /{table} — list, with the same operators pdo-restify's query string supports\nawait api.from<Post>('posts')\n  .select('id,title,comments(id,body)') // embeds work exactly like the PHP docs describe\n  .eq('status', 'published')\n  .neq('archived', true)\n  .gt('views', 100)\n  .gte('views', 100)\n  .lt('views', 10000)\n  .lte('views', 10000)\n  .like('title', '*hello*')   // '*' is the wildcard, not SQL's '%' — see pdo-restify's own docs\n  .in('id', [1, 2, 3])\n  .order('created_at', 'desc')\n  .limit(20)\n  .offset(40);\n\n// GET /{table}/{id}\nawait api.from<Post>('posts').find(1);\nawait api.from<Post>('posts').find(1, 'id,title,comments(id,body)'); // with select/embeds\n```\n\nCalling a filter method (`.eq()`, `.like()`, ...) twice for the same column\nkeeps only the last one — same as setting the same query-string key twice.\n\n### Writing\n\n```ts\n// POST /{table} — single insert\nawait api.from<Post>('posts').insert({ title: 'Hello', body: '...' });\n\n// POST /{table} — bulk insert (an array, not an object, is what makes it bulk)\nawait api.from<Post>('posts').insert([{ title: 'A', body: '...' }, { title: 'B', body: '...' }]);\n\n// PATCH /{table}/{id}\nawait api.from<Post>('posts').update(1, { title: 'Updated' });\n\n// PATCH /{table} — bulk update; each row needs the resource's primary key\nawait api.from<Post>('posts').updateMany([\n  { id: 1, title: 'A' },\n  { id: 2, title: 'B' },\n]);\n\n// DELETE /{table}/{id}\nawait api.from('posts').delete(1);\n\n// DELETE /{table} — bulk delete, a list of primary key values\nawait api.from('posts').deleteMany([1, 2, 3]);\n```\n\nBulk insert/update/delete run in a single transaction on the server — one\nbad row fails the whole batch. See the PHP library's\n[Bulk operations](../../docs/08-bulk-operations.md) doc.\n\n### Handling results\n\nEvery request resolves to:\n\n```ts\ntype PdoRestifyResult<T> =\n  | { data: T; error: null; status: number }\n  | { data: null; error: { message: string; status: number }; status: number };\n```\n\nThis client never throws for an API-level error (a 4xx from pdo-restify) —\ncheck `error` before using `data`. It *can* throw for a genuine network\nfailure (DNS, connection refused, ...), same as a raw `fetch()` would; wrap\nin `try`/`catch` if you need to handle that separately from an API error.\n\n```ts\nconst { data, error, status } = await api.from('posts').find(999);\n\nif (error) {\n  // status is also on the top-level result, so you don't need error.status\n  if (status === 404) {\n    // not found (or not yours, per policy — pdo-restify doesn't distinguish, on purpose)\n  }\n}\n```\n\n## What this client does *not* do\n\n- **No schema introspection or codegen.** `T` in `.from<T>('posts')` is a\n  type assertion, not a validated contract — this client doesn't know your\n  server's `Resource` definitions.\n- **No caching, retries, or request deduplication.** Every `await` is one\n  `fetch` call; build that on top if you need it.\n- **No realtime/subscriptions.** pdo-restify itself is a plain request/response\n  REST layer — there's nothing to subscribe to.\n\n## Development\n\n```bash\nnpm install\nnpm test        # mocked-fetch unit tests + a real end-to-end suite against\n                 # an actual `php -S` server running this repo's PHP library\n                 # (skipped automatically if `php` isn't on PATH)\nnpm run typecheck\nnpm run build    # emits dist/ (ESM + CJS + .d.ts + a plain-<script> global build) via tsup\n```\n\nThe end-to-end suite (`tests/e2e/`) is the important one: it spawns\n`php -S` running `tests/e2e/server.php` — a real `Api` instance from the PHP\npackage in this repo, backed by a real SQLite file — and drives it with this\nclient over real HTTP. It's what actually proves this client speaks the wire\nprotocol the PHP library implements, rather than the protocol the mocked\ntests assume it implements.\n\n## License\n\n[LGPL-3.0-or-later](../../LICENSE), same as the PHP library.\n","readmeFilename":"README.md","_rev":"1-6f3d6dc57865c64939af2d08c09e21fd"}