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

@nuxx-brandly/db-client

v0.5.3

Published

Nuxx BaaS Client SDK

Readme

@nuxx-brandly/db-client

Client SDK for Nuxx BaaS — a lightweight, Supabase-style backend-as-a-service. Every dynamic app gets its own set of tables (declared via a nuxx.json schema file) and talks to them through a single REST endpoint, wrapped by this SDK's fluent query builder.

Install

npm install @nuxx-brandly/db-client

Quick start

import { createClient } from '@nuxx-brandly/db-client';

// url = `${API_URL}/${APP_ID}` — API_URL is your Nuxx DB base (e.g. https://api.nuxx.app/api/nuxx-db),
// APP_ID is the Gitea repo name of your app. apiKey is the per-app secret generated
// from the Nuxx DB admin panel (never the APP_ID itself — that's public, not a secret).
export const db = createClient(`${API_URL}/${APP_ID}`, apiKey);

Querying data

Every table is queried the same way, regardless of what columns/relations it has:

// Select all rows
const { data, error } = await db.from('posts').select('*');

// Select specific columns, including a joined relation (declared as a foreignKey in nuxx.json)
const { data, error } = await db.from('posts').select('title, author(name)');

// Filter, sort, paginate
const { data, error } = await db
  .from('posts')
  .select('*')
  .eq('published', true)
  .gte('createdAt', '2026-01-01')
  .order('createdAt', { ascending: false })
  .limit(20)
  .offset(0);

// Insert — returns the raw Mongo insert result ({ acknowledged, insertedCount, insertedIds }),
// NOT the created row. Re-select if you need the inserted document back.
const { data, error } = await db.from('posts').insert({ title: 'Hello', authorId: userId });

// Update — always scope it with a filter, or it updates every row in the table
const { data, error } = await db.from('posts').update({ title: 'Edited' }).eq('_id', postId);

// Delete — same rule, always scope it
const { data, error } = await db.from('posts').delete().eq('_id', postId);

Every call resolves to { data, error }data is null and error is populated on failure. The builder is also directly await-able (it implements .then()), so you don't need to call .execute() explicitly — await db.from('x').select('*') works as shown above.

Filter operators: .eq(), .neq(), .gt(), .gte(), .lt(), .lte(), .in(), .contains() (case-insensitive substring match on strings).

Security: tables can require a logged-in end-user

Your api key is not a true secret — it's compiled into your app's public JS bundle, so anyone can extract it from devtools and call this SDK's endpoints directly. Any table declared with "requiresAuth": true in nuxx.json rejects every operation (select/insert/update/delete/bulkUpdate/bulkDelete) unless the caller also has a valid end-user session — you don't need to do anything extra in your own code for this: once db.auth.signIn()/signUp() succeeds, the SDK already attaches that session to every subsequent db.from(...) call automatically. If a query against a requiresAuth table fails with a 401 (error.message mentioning "Token de autorización"), the caller isn't logged in yet — mark any table holding data that shouldn't be world-readable (customers, orders, billing info) this way.

Pagination

.limit()/.offset() work as shown above. There's also an inclusive .range(from, to) convenience (Supabase-style), which is pure sugar over the same two fields — whichever of .range()/.limit()/.offset() you call last wins:

// Rows 0-9 (10 rows), same as .offset(0).limit(10)
const { data, error } = await db.from('posts').select('*').range(0, 9);

To get the total row count alongside a page of results, pass { count: 'exact' } to .select():

const { data, error, count } = await db.from('posts').select('*', { count: 'exact' }).range(0, 9);
// count = total rows matching your filters (NOT limited by range/limit) — use it to compute total pages.

count reflects rows matching your .eq()/.gt()/etc. filters on the base table — if your select includes a joined relation (author(name)), count is still the number of base rows, not the (potentially different) number of rows after the join. Omitting { count: 'exact' } costs nothing extra — the default select() behavior/performance is unchanged.

Bulk update and bulk delete

.update() always applies the same patch to every row matching your filter (updateMany under the hood) — useful for "set published: true on every row where authorId = X", not for "save 50 rows I edited individually with different values." For that, use .bulkUpdate():

const { data, error } = await db.from('posts').bulkUpdate([
  { id: post1Id, data: { title: 'New title 1' } },
  { id: post2Id, data: { title: 'New title 2', published: true } },
]);
// data: { matchedCount, modifiedCount, results: [{ id, success, error? }, ...] }

Partial failure is normal, not an exception: the top-level error stays null even if some records fail (bad id, record not found, invalid data) — always check data.results for per-record outcomes. Records with success: false carry an error string ('not found', 'invalid id format', or a validation/write error message). Valid records are still applied even when others in the same call fail. Max 500 records per call; duplicate ids in one call are not deduplicated (both run, order not guaranteed).

For deleting a list of ids in one call, .bulkDelete() is sugar for .delete().in('_id', ids) — there's no new backend behavior here, since deleting by a filter already deletes every matching row:

const { data, error } = await db.from('posts').bulkDelete([post1Id, post2Id]);

All of the above is additive — every existing method, filter, and response shape keeps working exactly as before if you don't use these.

Authenticating end-users

Nuxx DB has its own built-in end-user auth, separate from the app's API key and separate from any table you declare — use db.auth, never hand-roll requests to the REST endpoints:

// Register (roleId is optional — the _id of a row in your own `roles` table, if you declared one)
const { data, error } = await db.auth.signUp({ name: 'Jane', email: '[email protected]', password: 'secret123' });

// Log in
const { data, error } = await db.auth.signIn({ email: '[email protected]', password: 'secret123' });

// Both resolve to { data: { user, token } | null, error }. From this point on, db.from(...) queries
// are automatically authenticated — no need to touch setAuthToken or localStorage yourself, the SDK
// persists the session for you (survives a page reload).

// On app boot, check synchronously (no await) whether someone's already logged in:
const session = db.auth.getSession(); // { user, token } | null

// Re-fetch the current user from the server:
const { data, error } = await db.auth.getUser(); // { data: { user } | null, error }

// Log out:
await db.auth.signOut();

The returned user always has the shape { _id, name, email, status, roles, roleId, role }role is { name, permissions }, resolved automatically when the user has a roleId. Check a permission with user.role?.permissions?.someKey.

users is a reserved table name. Never declare a users table in nuxx.json — end-user accounts already exist via db.auth. Any objectId column that references "the user who owns this row" just declares a normal foreignKey with "foreignTable": "users"; relation population (.select('*, author(name)')) resolves it against the real accounts automatically, excluding password even when selecting *.

Uploading files & sending emails

Use db.files/db.email — never hand-roll requests to the REST endpoints. Under the hood these live on a sibling path (nuxx-api/:appId/... instead of nuxx-db/:appId/...), which the client derives automatically from the URL you passed to createClient.

// Upload a file — store ONLY the returned URL in your table, never the file itself.
const { data, error } = await db.files.upload(fileObject); // data: { url: "https://..." }

// Send an email — recipient/subject/from are bound to the brand server-side, you only send content.
const { data, error } = await db.email.send({ html: '<p>Hello</p>' }); // data: { id: "..." }

Uploads are capped at 10MB and restricted to common image/PDF/document mimetypes; email.send's html is sanitized server-side before delivery. Both are rate-limited per app.

Using the AI Assistant

The SDK provides a useAI React hook to interact with an integrated intelligent assistant (powered by Gemini). It handles loading states, error handling, and rate-limiting (20 seconds per request per IP).

import { useAI } from '@nuxx-brandly/db-client';
import { db } from './lib/nuxx-constants'; // Your NuxxDbClient instance

function AIFeature() {
  // The second parameter is an optional systemPrompt
  const { generate, loading, error, data } = useAI(db);

  return (
    <div>
      <button onClick={() => generate('Write a short poem about Nuxx', 'You are an expert poet.')}>
        Generate Poem
      </button>
      {loading && <p>Loading...</p>}
      {error && <p>Error: {error.message}</p>}
      {data && <p>{data}</p>}
    </div>
  );
}

API reference

createClient(url: string, apiKey: string, options?: { auth?: { token?: string } })NuxxDbClient

  • db.from(table){ select, insert, update, delete, bulkUpdate, bulkDelete }
  • db.auth{ signUp, signIn, signOut, getUser, getSession } — see "Authenticating end-users" above.
  • db.files{ upload } — see "Uploading files & sending emails" above.
  • db.email{ send } — see "Uploading files & sending emails" above.
  • db.setAuthToken(token: string | null) — internal; db.auth.signUp/signIn/signOut already call this for you.

db.from(table).select(columns?) / .insert(data) / .update(data) / .delete() all return a NuxxDbFilterBuilder, chainable with:

  • .eq(column, value) / .neq(column, value)
  • .gt(column, value) / .gte(column, value) / .lt(column, value) / .lte(column, value)
  • .in(column, values[])
  • .contains(column, value)
  • .order(column, { ascending })
  • .limit(count) / .offset(count) / .range(from, to)

db.from(table).bulkUpdate(updates: { id: string; data: any }[]) → resolves to { data: { matchedCount, modifiedCount, results: { id, success, error? }[] }, error }.

db.from(table).bulkDelete(ids: string[]) → sugar for .delete().in('_id', ids), same NuxxDbFilterBuilder chain.