npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@ikeboy003/cloudrest-client

v0.6.1

Published

Typed query builder + realtime client for cloudrest. PostgREST-grammar compatible.

Readme

@ikeboy003/cloudrest-client

Typed query builder for cloudrest / postgrest-rs. PostgREST-grammar compatible, zero deps, works anywhere fetch exists.

import { createClient } from "@ikeboy003/cloudrest-client";
import type { paths } from "./schema"; // openapi-typescript output (optional)

const db = createClient<paths>({
  baseUrl: "https://api.example.com",
  getToken: () => localStorage.getItem("jwt"),
});

Reads

const books = await db.from("books")
  .select("id,title,author(*)")
  .eq("published", true)
  .gte("year", 2000)
  .order("year", { ascending: false })
  .limit(20);

const one = await db.from("books").select("*").eq("id", 1).single();

// Paged: rows + total via Content-Range.
const { rows, count } = await db.from("books")
  .select("*").range(0, 19).count("estimated").withCount();

Every PostgREST operator is here: eq neq gt gte lt lte like ilike match imatch in is isDistinct fts plfts phfts wfts cs cd ov sl sr nxr nxl adj not notIs filter, plus nestable and()/or() via the cond.* helpers.

viaQuery() — filters in the body

When a big in.(...) list would blow past the ~8 KB URL ceiling, send the read as an RFC-10008 QUERY: identical result, filters travel in the body.

await db.from("events").select("*").in("id", tenThousandIds).viaQuery();

Writes

await db.insert("books", { title: "x" }).returning();
await db.insert("books", rows).onConflict(["isbn"]).returning();       // upsert
await db.insert("books", rows).onConflict("isbn", { ignoreDuplicates: true });
await db.update("books", { sold: true }).eq("id", 1).returning();
await db.delete("books").lt("year", 1900);

// Full-row upsert by PK (PUT /table).
await db.put("settings", { key: "theme", value: "dark" }).eq("key", "theme");

Write tuning (chainable on any mutation): returning() minimal() dryRun() maxAffected(n) minAffected(n) timezone(tz) buffered(), plus columns([...]) and missing("null"|"default") on inserts.

Media

await db.insert("merch", { slug: "tee" }).attach("image", file).returning();      // 1:1
await db.insert("merch", { slug: "tee" }).attachMany("images", files).returning(); // 1:N

RPC

const r = await db.rpc("update_mastery", { p_id: 1, p_correct: true });   // POST
const s = await db.rpc("search", { q: "cat" }).get();                     // cacheable GET
const t = await db.rpc("search", { q: "cat" }).viaQuery();                // QUERY verb

Raw SQL (read-only)

POST /_sql — an arbitrary SELECT under the caller's role in a BEGIN READ ONLY transaction (RLS + grants enforced, writes rejected). For joins/CTEs/windows the structured grammar can't express. Capped at 1000 rows.

const { rows, truncated } = await db.sql("select count(*) n from books");
const { plan } = await db.sql("select * from big_view").explain();  // dry-run

Batch

Bundle several ops into one round-trip (POST /_batch). Each runs through the normal handler pipeline (same auth/RLS/Prefer) and commits independently.

const results = await db.batch()
  .add("book", db.from("books").select("*").eq("id", 1))
  .add("newTag", db.insert("tags", { name: "sci-fi" }))
  .op("health", "GET", "/health")           // raw escape hatch
  .run();
// → [{ id, status, body }, ...]

Bulk COPY

await db.copy("books", "isbn,title\n123,x\n", { columns: ["isbn", "title"] });

Realtime

Postgres LISTEN/NOTIFY-backed change feed.

// One-shot catch-up (RLS-gated), page forward with the returned cursor:
const { changes, cursor } = await db.realtime.changes({ table: "books", since });

// Live streams — hand the URL to the platform primitive:
const es = new EventSource(db.realtime.sseUrl({ table: "books", accessToken: jwt }));
const ws = new WebSocket(db.realtime.wsUrl({ table: "books" }));

Live shapes + channels (connect())

connect() opens one multiplexed WebSocket and speaks the protocol for you — no manual framing. A shape is a filtered table subscription that MATERIALIZES rows (insert/move-in adds, update mutates, delete/move-out removes) with an initial snapshot; a channel is an ephemeral broadcast/presence topic with no database round-trip (ideal for a driver's live GPS). Shapes reconnect and re-snapshot automatically.

const rt = db.realtime.connect(); // uses global WebSocket, or pass { WebSocket }

// Customer watches ONE delivery: snapshot of current state, then live —
// bind the UI straight to the materialized rows.
const order = rt.shape(
  { table: "deliveries", where: "id.eq.42", snapshot: true, key: "id" },
  {
    onState: (rows) => renderOrder(rows[0]),
    onUpToDate: ({ truncated }) => { if (truncated) pageRemainderViaRest(); },
    onLagged: () => resyncFromScratch(),
  },
);

// Driver's live position: ephemeral broadcast on a topic (no DB writes).
const chan = rt.channel("order:42", {
  onBroadcast: (_event, pos) => moveMarker(pos),   // customer receives
});
chan.broadcast("loc", { lat, lng });               // driver publishes ~1/s

// cleanup
rt.unsubscribe(order);
rt.close();

Notes: hard deletes need deletes: true on the shape (the table must be REPLICA IDENTITY FULL); channels need the server built with the realtime-presence feature.

Advisory locks

const lock = await db.advisory.acquire(42, { wait: true });
if (lock.held) {
  try { /* critical section */ } finally { await db.advisory.release(lock); }
}

API keys

Per-caller key lifecycle. The raw secret is returned exactly once.

const { key } = await db.keys.create({ name: "ci" });  // stash `key` now
const { keys } = await db.keys.list();
await db.keys.rotate(id);   // old dies, new raw returned once
await db.keys.revoke(id);

Persisted queries

Register a request shape once, replay it by id (and lock the server down to only allow-listed queries if you want).

const { id } = await db.persisted.register(db.from("books").select("*").eq("author", "x"));
const rows = await db.persisted.run(id, { params: { limit: 10 } });

Server introspection

await db.quota();          // GET /_quota — per-subject metering status
await db.openapi();        // GET /openapi.json
await db.codegen("ts");    // GET /_codegen/ts — typed client source ("ts"|"go"|"rs"|"py")

Auth & headers

getToken is called before every request; return null when unauthenticated. Pass static headers (e.g. Accept-Profile for a non-public schema) via headers on createClient. Errors throw CloudRestError with .status and the parsed PostgREST .body.