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

firebirdrest-js

v0.4.0

Published

JavaScript/TypeScript client for FirebirdREST with the supabase-js query API

Readme

firebirdrest-js

The supabase-js query API for FirebirdREST: from(), select(), filters, embedding, rpc(), inserts, upserts, counts, and the rest, against a Firebird database.

FirebirdREST speaks PostgREST's URL grammar, so this package is a thin layer over @supabase/postgrest-js, the query builder inside supabase-js, plus what Firebird adds: its own filter operators, raw file downloads and time zone handling. There is no auth, storage or realtime module: FirebirdREST authenticates with JWTs you issue, and Firebird has none of the rest.

npm install firebirdrest-js
import { createClient } from 'firebirdrest-js'

const client = createClient('http://localhost:3000', token) // token: a JWT, or omit for anonymous

const { data, error } = await client
  .from('books')
  .select('title, price, author:authors(name), tags(name)')
  .eq('in_stock', true)
  .order('price', { ascending: false })
  .limit(10)

Works in Node 18+, Deno, Bun and browsers (anything with fetch).

What works

Everything postgrest-js offers that FirebirdREST implements, checked by the compatibility suite in CI against the FirebirdREST example database:

| | | |---|---| | Reads | select with aliases, casts, embedding (to-one, to-many, many-to-many, !inner), aggregates (count(), sum()...), computed fields | | Filters | eq, neq, gt, gte, lt, lte, like, ilike, is, in, not, or, match, filter, filters on embedded resources | | Shaping | order (including by an embedded column), limit, range, single, maybeSingle, csv, explain, abortSignal | | Counts | { count: 'exact' \| 'planned' \| 'estimated', head: true } | | Writes | insert (one or many), upsert with onConflict, update, delete, .select() for the representation | | RPC | rpc(name, args, { get: true }) for functions, selectable procedures with .select().order()..., package routines ('catalog.book_count') | | Schemas | client.schema('inventory') on Firebird 6 with db-schemas | | Types | npx firebirdrest-js gen types writes the Database type for createClient<Database>() (typed queries) | | Events | client.channel('order_placed').on(...).subscribe() streams Firebird POST_EVENTs (FirebirdREST 0.5.0 with db-events) | | Errors | { code, message, details, hint } as in supabase-js; code is an FBRST... code or Firebird's SQLSTATE |

Not available, because Firebird has no such types: contains, containedBy, overlaps, textSearch, range and JSON operators. See Differences from PostgREST.

Firebird's operators

CONTAINING (case-insensitive substring), STARTING WITH and SIMILAR TO are available as functions, and as methods for JavaScript callers:

import { createClient, containing, starting, similar } from 'firebirdrest-js'

const q = client.from('books').select('title')
await containing(q, 'title', 'gods')        // title CONTAINING 'gods'
await starting(q, 'title', 'The')           // title STARTING WITH 'The'
await similar(q, 'title', '(A|The) %')      // title SIMILAR TO '(A|The) %'

await client.from('books').select('title').containing('title', 'gods') // JavaScript
await client.from('books').select('title').filter('title', 'containing', 'gods') // either

Typed queries

gen types writes the TypeScript types of your API, the way supabase gen types does, so from(), select(), insert() and rpc() are checked by the compiler and their results typed:

npx firebirdrest-js gen types --url http://localhost:3000 --token "$JWT" --output src/database.types.ts
import { createClient } from 'firebirdrest-js'
import type { Database, Tables } from './database.types'

const client = createClient<Database>('http://localhost:3000', token)

const { data } = await client.from('books').select('title, authors(name), tags(name)')
// data: { title: string; authors: { name: string } | null; tags: { name: string }[] }[] | null

await client.from('books').insert({ title: 'Mort' }) // a missing NOT NULL column is a compile error
await client.rpc('add_vat', { amount: 100 })          // data: number | null
type Author = Tables<'authors'>

What it reads from the server's OpenAPI document (FirebirdREST 0.7 or later for all of it):

| | | |---|---| | tables and views | Row, Insert (columns without a default required), Update; views typed apart | | foreign keys | embedding in both directions, one-to-one, many-to-many through junction tables, composite keys | | computed fields | typed when selected by name, left out of select('*') as the server does | | functions and procedures | arguments (required unless they have a default) and results: a value, rows, or an object | | Firebird 6 schemas | one entry per exposed schema, for client.schema('inventory') |

  • The document lists what the token's role may use. Generate with a role that sees everything the application uses, or run the server with openapi-mode = "ignore-privileges" in development.
  • A foreign key into another schema is embeddable but not typed: postgrest-js types relationships within one schema. gen types lists such keys on standard error.
  • Computed relationships (procedures embedded as resources) are not typed.
  • Against a server before 0.7, views, composite keys and argument defaults are not known: views are typed as tables and every argument is optional.
  • --schema public,inventory limits the schemas; FIREBIRDREST_URL and FIREBIRDREST_TOKEN can replace --url and --token. The generator is also a function: import { generateTypes } from 'firebirdrest-js/gen'.

Regenerate after changing the database.

Realtime events

FirebirdREST streams Firebird events (POST_EVENT 'order_placed' in a trigger or procedure) to clients as Server-Sent Events, for the names listed in its db-events setting. A channel listens for one or more of them:

const channel = client
  .channel('order_placed', 'stock_changed')
  .on('order_placed', (e) => refreshOrders()) // e.name, e.count (posts merged into this notification)
  .on('*', (e) => console.log('event', e.name))
  .onReset((reason) => reloadOrders()) // notifications may have been missed: read again
  .subscribe((status, error) => console.log(status, error)) // 'open' | 'error' | 'closed'

channel.unsubscribe()

An event says that something happened, not what: read the data through the API as usual. The channel reconnects by itself while the server is unreachable (1 s to 30 s between attempts), sending the last notification's id so FirebirdREST 0.8+ replays what was missed; when the server can't know (it restarted, or the client was away too long) onReset runs instead. It stops when the server refuses the subscription (an event that isn't exposed, a role that may not listen). It uses fetch with a streaming body, so it works in browsers and in Node 18+ without EventSource, and sends the client's Authorization header.

Files and single values

file() sends a query with the Accept type you choose and returns the one value as it is: a Blob for binary types, a string for text/*. The query must select one column and match one row.

const { data } = await client.file(
  client.from('covers').select('image').eq('book_id', 1), 'image/png')
// data: Blob with the PNG bytes

const { data: title } = await client.file(
  client.from('books').select('title').eq('id', 1), 'text/plain')

In JSON responses, binary columns are base64 strings, as FirebirdREST sends them.

Options

const client = createClient(url, token, {
  headers: { 'X-Request-Id': '...' },  // extra headers for every request
  schema: 'inventory',                  // Firebird 6 SQL schema (Accept-Profile / Content-Profile)
  timezone: 'Europe/Bucharest',         // Prefer: timezone= for WITH TIME ZONE values (Firebird 4+)
  fetch: myFetch,                       // custom fetch
})
client.auth(newToken) // switch the JWT later

Tests

The suite runs against a FirebirdREST server with the bookstore example:

FIREBIRDREST_URL=http://localhost:3000 \
FIREBIRDREST_JWT_SECRET=reallyreallyreallyreallyverysafe1234 npm test

Without FIREBIRDREST_JWT_SECRET the write tests are skipped. CI starts Firebird 3, 5 and 6 (with db-schemas and the Firebird 6 schema example) and FirebirdREST in Docker, both the released image and a build of its main branch (see .github/workflows/ci.yml).

Releasing

Publishing is done by GitHub Actions when a version tag is pushed, after the compatibility suite passes:

npm version 0.1.0        # updates package.json and creates the v0.1.0 tag
git push --follow-tags

One-time setup, after the first manual npm publish: on npmjs.com open the package → Settings → Trusted publisher → GitHub Actions, with repository mariuz/firebirdrest-js and workflow release.yml. The workflow then publishes with a short-lived OIDC token, no secret needed, and with provenance, so npm shows the source commit and workflow.

License

MIT.