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

@allyourbase/js

v0.3.0

Published

JavaScript/TypeScript client SDK for Allyourbase

Readme

@allyourbase/js

JavaScript/TypeScript client SDK for Allyourbase — the PostgreSQL Backend-as-a-Service.

Install

npm install @allyourbase/js

The SDK release candidate is version 0.3.0.

Quick Start

import { AYBClient, type SearchHit } from "@allyourbase/js";

const ayb = new AYBClient("http://localhost:8090");

// Create a record
const post = await ayb.records.create("posts", {
  title: "Hello World",
  published: true,
});

// List records with filtering and sorting
const posts = await ayb.records.list("posts", {
  filter: "published=true",
  sort: "-created_at",
  perPage: 20,
});

// Search records with highlights and facet counts
const search = await ayb.records.list<SearchHit<{ id: string; title: string }>>(
  "posts",
  {
    search: "postgres",
    fuzzy: true,
    typoThreshold: 0.3,
    highlight: true,
    facets: ["published"],
  },
);
console.log(search.items[0]?._highlight, search.facets?.published);

// Auth
await ayb.auth.login("[email protected]", "password");
const me = await ayb.auth.me();

// Passkey sign-in and MFA
await ayb.auth.signInWithPasskey("[email protected]");
await ayb.auth.enrollPasskey("Primary passkey");

highlight is a boolean toggle that asks the backend to return _highlight snippets on matching items. typoThreshold is only accepted when fuzzy: true.

API Reference

new AYBClient(baseURL, options?)

Create a client instance.

const ayb = new AYBClient("http://localhost:8090");

// With custom fetch (e.g. for Node.js < 18)
const ayb = new AYBClient("http://localhost:8090", { fetch: myFetch });

PostgreSQL RPC

Call PostgreSQL functions with client.rpc<T>(functionName, args?, options?). Void and empty responses resolve to undefined.

const total = await ayb.rpc<number>("leaderboard_total", {
  club_id: "abc123",
});

Edge functions

client.functions.invoke(name, options?) returns the raw response as { status, headers, rawBody }.

const response = await ayb.functions.invoke("send-digest", {
  body: { club_id: "abc123" },
});
console.log(response.status, response.headers, response.rawBody);

GraphQL and admin/org typed clients are JS-only scope today and live on client.graphql and client.admin(...). The other SDKs intentionally do not define those typed clients yet.

Records

// List with filtering, sorting, pagination
const result = await ayb.records.list<Post>("posts", {
  filter: "status='active' AND views>100",
  sort: "-created_at,+title",
  page: 1,
  perPage: 50,
  fields: "id,title,status",
  expand: "author,category",
  skipTotal: true,
});
// result: { items: Post[], page, perPage, totalItems, totalPages }

// Search with typo tolerance, highlights, and facets
const search = await ayb.records.list<SearchHit<Post>>("posts", {
  search: "postgres database",
  fuzzy: true,
  typoThreshold: 0.3,
  highlight: true,
  facets: ["status", "category"],
});
// search.items[0]._highlight is present when the backend returns a highlight.
// search.facets is a FacetCounts envelope keyed by requested facet column.

// Semantic/vector search
await ayb.records.list<Post>("posts", {
  semantic: true,
  semanticQuery: "articles about hosted Postgres",
  nearest: [0.12, 0.34, 0.56],
  vectorColumn: "embedding",
  distance: "cosine",
});

// Get by ID
const post = await ayb.records.get<Post>("posts", "abc123", {
  expand: "author",
});

// Create
const post = await ayb.records.create<Post>("posts", {
  title: "New Post",
  body: "Content here",
});

// Update (partial)
const updated = await ayb.records.update<Post>("posts", "abc123", {
  title: "Updated Title",
});

// Delete
await ayb.records.delete("posts", "abc123");

highlight mirrors the REST API's boolean query param: pass true to request _highlight snippets, and omit it otherwise. typoThreshold requires fuzzy: true.

InstantSearch adapter

import { createInstantSearchClient } from "@allyourbase/js/instantsearch";

const searchClient = createInstantSearchClient({
  client: ayb,
  objectIDField: "id",
  defaultIndexName: "posts",
});

@allyourbase/js/instantsearch is a thin adapter over records.list; it does not add a second search transport. objectIDField is required because AYB rows are arbitrary PostgreSQL records, and the adapter fails closed when a returned row is missing that field or has a null value.

The adapter supports one-index search(requests) calls with query, zero-based page, hitsPerPage, concrete facets, disjunctiveFacets, facetFilters in attribute:value form, numericFilters range comparisons, and the documented filters comparison subset. Empty query strings are sent as browsable list calls with no search parameter so first-render facets and range stats remain available. Set highlight: false in the adapter options to omit the backend highlight=true request flag.

searchClient.searchForFacetValues(requests) is supported for searchable facet widgets. It delegates each request through client.records.searchFacetValues() (see below) and returns Algolia-shaped { facetHits, exhaustiveFacetsCount, processingTimeMS } per request; the backend's <mark> prefix wrappers are remapped onto the caller's highlightPreTag/highlightPostTag (default InstantSearch placeholders). maxFacetHits defaults to 10 and is capped at 100.

Unsupported cases throw before AYB is called: mixed index requests, wildcard facets, vector/search tuning params, skipTotal, negative facetFilters, nested attributes, tag filters, malformed numeric filters, arrays, NOT, and unlisted Algolia request parameters.

Facet value search

// Searchable facet values: bucket-level search on a single facet column
const facet = await ayb.records.searchFacetValues("products", "category", {
  q: "st",
  maxFacetHits: 10,
});
// facet.facetHits[0]: { value: "Stationery", highlighted: "<mark>St</mark>ationery", count: 3 }
console.log(facet.exhaustiveFacetsCount);

records.searchFacetValues(collection, column, params) calls GET /api/collections/{table}/facets/{column}/search. column must be a text facet column. params accepts an optional q prefix, maxFacetHits, filter, and search (the same scoping predicates the list endpoint accepts). maxFacetHits defaults to 10 and is capped at 100. The InstantSearch adapter's searchForFacetValues(requests) uses this method as its transport.

Auth

// Register
const { token, refreshToken, user } = await ayb.auth.register(
  "[email protected]",
  "password123",
);

// Login
await ayb.auth.login("[email protected]", "password123");

// Current user
const me = await ayb.auth.me();

// Refresh token
await ayb.auth.refresh();

// Logout
await ayb.auth.logout();

// Password reset
await ayb.auth.requestPasswordReset("[email protected]");
await ayb.auth.confirmPasswordReset(token, "newpassword");

// Email verification
await ayb.auth.verifyEmail(token);
await ayb.auth.resendVerification();

// First-factor WebAuthn login
const challenge = await ayb.auth.beginWebAuthnLogin("[email protected]");
await ayb.auth.finishWebAuthnLogin(challenge.challengeId, assertionResponse);

// Browser passkey convenience flow
await ayb.auth.signInWithPasskey("[email protected]");

// WebAuthn MFA enrollment and verification
await ayb.auth.enrollPasskey("Work laptop");
await ayb.auth.verifyPasskey(mfaToken);

// Restore tokens from storage
ayb.setTokens(savedToken, savedRefreshToken);

Storage

// Upload a file to a bucket
const file = document.querySelector("input[type=file]").files[0];
const result = await ayb.storage.upload("avatars", file);
// result: { id, bucket, name, size, contentType, createdAt, updatedAt }

// Upload with a custom filename
await ayb.storage.upload("documents", blob, "report.pdf");

// Get download URL
const url = ayb.storage.downloadURL("avatars", "photo.jpg");
// → "http://localhost:8090/api/storage/avatars/photo.jpg"

// List files in a bucket
const files = await ayb.storage.list("avatars", { prefix: "user_", limit: 20 });

// Get a signed URL (time-limited access, default 1 hour)
const { url: signedUrl } = await ayb.storage.getSignedURL("avatars", "photo.jpg", 3600);

// Delete
await ayb.storage.delete("avatars", "photo.jpg");

Realtime

// Subscribe to table changes (Server-Sent Events)
const unsubscribe = ayb.realtime.subscribe(
  ["posts", "comments"],
  (event) => {
    console.log(event.action, event.table, event.record);
    // action: "create" | "update" | "delete"
  },
);

// Stop listening
unsubscribe();

TypeScript

All methods accept generic type parameters for full type safety:

interface Post {
  id: string;
  title: string;
  published: boolean;
  created_at: string;
}

const posts = await ayb.records.list<Post>("posts");
// posts.items is Post[]

Exported types include:

AdminAPIKey, AdminAPIKeyListResponse, App, AppListResponse, AuthPersistence, AuthResponse, AuthStateEvent, AuthStateListener, BatchOperation, BatchResult, ClientOptions, CreateAdminAPIKeyRequest, CreateAdminAPIKeyResponse, CreateOAuthClientRequest, CreateOAuthClientResponse, FacetCounts, FacetValueCount, FacetValueSearchHit, FacetValueSearchParams, FacetValueSearchResponse, GetParams, GraphQLErrorItem, GraphQLResponse, HealthResponse, ListParams, ListResponse, MagicLinkConfirmResponse, MagicLinkRequestResponse, MFAPendingAuthResponse, OAuthClient, OAuthClientListResponse, OAuthOptions, OAuthProvider, OAuthTokenResponse, PersistedAuthSession, PublicKeyCredentialCreationOptionsJSON, PublicKeyCredentialDescriptorJSON, PublicKeyCredentialParametersJSON, PublicKeyCredentialRequestOptionsJSON, PublicKeyCredentialRpEntityJSON, PublicKeyCredentialUserEntityJSON, RealtimeEvent, RotateOAuthClientSecretResponse, RpcNotifyOption, RpcOptions, SearchHit, StorageObject, UpdateOAuthClientRequest, User, WebAuthnEnrollBeginResponse, WebAuthnEnrollConfirmRequest, WebAuthnLoginBeginResponse, WebAuthnLoginFinishRequest, WebAuthnMFAChallengeResponse, and WebAuthnMFAVerifyRequest.

Error Handling

All API errors throw AYBError with the HTTP status code:

import { AYBClient, AYBError } from "@allyourbase/js";

try {
  await ayb.records.get("posts", "nonexistent");
} catch (err) {
  if (err instanceof AYBError) {
    console.log(err.status);  // 404
    console.log(err.message); // "record not found"
  }
}

License

MIT