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

@cubs/rqlite-client

v1.1.0

Published

Typed rqlite client, declarative schemas and migrations

Readme

@cubs/rqlite-client

Typed client, declarative schemas and migrations for rqlite. Node.js 24+, ESM, TypeScript 7 consumers, isolated TypeScript 6.0.3 schema compiler. Validated against rqlite 10.3.3 / SQLite 3.53.4.

npm install @cubs/[email protected]

The installed executable is rqlite, usable as npx rqlite from that project. For a one-off invocation without installation, use npm exec --package @cubs/[email protected] -- rqlite --help.

Connect immediately

import {createClient} from '@cubs/rqlite-client';

const db = createClient({connection: {url: process.env.DATABASE_URL!}});
await db.waitForDatabase();
await db.assertReady();
const rows = await db.sql.query('SELECT 1 AS value');

This client offers SQL and health before you define models. createClient itself does not connect or migrate. Supply DATABASE_URL in your application's .env and load it with Node's --env-file or your own configuration code. Add schemas and migrations below to obtain typed db.models repositories.

Define a schema

// schemas/User.schema.ts
import {MappedSuperclass, Table, Column, PrimaryKey, Generated,
  CreatedAt, UpdatedAt, Default} from '@cubs/rqlite-client/schema';

@MappedSuperclass()
abstract class Base {
  @PrimaryKey() @Generated('ulid') id!: string;
  @CreatedAt() created_at!: string;
  @UpdatedAt() updated_at!: string;
}

@Table('users')
export class User extends Base {
  @Column() name!: string;
  @Column() @Default(null) biography!: string | null;
}

Types infer TEXT, REAL and INTEGER for strings, numbers and booleans respectively. Use @Integer() for integral numbers, @Json() for JSON, and T | null for SQL nullability. @CreatedAt() and @UpdatedAt() already declare a persisted clock with a database default: no additional @Column or @Default is necessary. Managed clocks are returned as strings and excluded from generated insert/update inputs. Field names are configurable; they need not be created_at/updated_at.

Decorators have no runtime side effects. Compilation resolves symbols, aliases, constants and inherited fields without instantiating classes or executing field initializers. Initializers are not SQL defaults. Optional ?: persisted fields, ambiguous types and implicit Date codecs are rejected. Use @Override() for an explicit inherited metadata override.

@Unique, @Check, @ForeignKey, @Index and composite @PrimaryKey([...]) describe constraints. Index options support unique, where and expressions. SQL expressions in constraints are trusted developer-authored fragments; runtime values use binds. The generator constructs CREATE TABLE from metadata; schemas do not contain full-table SQL overrides.

Generate and migrate

// rqlite.config.ts
import {defineConfig} from '@cubs/rqlite-client/config';
export default defineConfig({
  schemas: ['schemas'], output: 'generated/schema.ts', migrations: 'migrations',
  environments: {
    development: {urlEnv: 'DATABASE_URL', development: true},
    production: {urlEnv: 'DATABASE_URL', development: false},
  },
});
npx rqlite init
npx rqlite create migration "create users"
npx rqlite migrate status --environment development
npx rqlite migrate --environment development
npx rqlite check --database --environment development

create migration generates the client as well. Use rqlite generate for metadata/type-only regeneration. rqlite check without --database checks only files. Configuration is .ts or .js; paths resolve relative to it. Use --config path and optional --env-file path. Database operations require an explicit configured environment. An environment needs url, urlEnv, or host/port configuration; environment-variable references are preserved in compiled builds.

Use the client

import {createClient} from '@cubs/rqlite-client';
import {schema} from './generated/schema.js';

const client = createClient({
  schema,
  connection: {url: process.env.DATABASE_URL!},
  migrations: {directory: 'migrations'},
});

await client.assertReady();
const user = await client.models.users.create({name: "O'Brien"});
await client.models.users.update({name: 'Updated'}, {id: user.id});
const confirmed = await client.models.users.find({id: user.id});

Model and input types are inferred without per-table generics. createClient does no I/O. Call client.migrations.status(), .up() and client.assertReady() explicitly. client.sql.query() and .batch() expose parameterized SQL and declarative request transactions. There is no interactive transaction callback. connection also accepts an injected fetch and request timeout.

Authentication (1.1.0)

import {createClient, basicAuthFromEnv} from '@cubs/rqlite-client';
const client = createClient({connection: {
  url: process.env.DATABASE_URL!,
  auth: basicAuthFromEnv('DATABASE_USERNAME', 'DATABASE_PASSWORD'),
}});

Configure CLI targets with environment variable names, never literal passwords:

production: {
  urlEnv: 'DATABASE_URL', development: false,
  auth: {usernameEnv: 'DATABASE_USERNAME', passwordEnv: 'DATABASE_PASSWORD'},
}

Both credentials are required for a target declaring auth. prepare preserves only these environment references. Existing unauthenticated targets still work. Basic Auth is sent to readiness and SQL endpoints; authenticated redirects are refused. Use TLS or a private encrypted tunnel outside a trusted local network. URLs with embedded credentials remain unsupported. SQL requests return AUTH for HTTP 401 and FORBIDDEN for HTTP 403 without retrying writes or exposing response bodies. isReady() returns false on denied readiness.

A read-only account needs query, status, and ready; use client.sql.query() or batch(statements, {endpoint: 'query'}). The default mixed /db/request endpoint requires both query and execute, even for SELECT statements. Permissions are enforced by rqlite for the entire database, not by the client.

Repositories support create, find/findAll, update, updateAndFind and delete. @Table('users', {softDelete: 'deleted_at'}) enables soft deletion. Empty mutation lookups, unknown columns, invalid scalar values and caller-owned managed clocks are rejected at runtime. JSON codecs apply only to marked fields. Predicates eq, like and isNull are explicit; % in equality remains literal.

The client retains response positions including DDL and zero-row writes. A lost write response raises COMMIT_UNKNOWN; it is never retried blindly. Migration application reconciles its committed checksum with a strong read.

Development and manual changes

npx rqlite watch
npx rqlite watch --apply --environment development --run server.ts
npx rqlite create migration "backfill" --manual
# Edit the last draft's up statements, then:
npx rqlite finalize migration --data-only
npx rqlite migrate --environment development --allow-manual

Watch follows schema imports and complete journal entries, coalesces editor rename saves and serializes work. The optional server stops on pending schema/draft/error and restarts only after migrations and descriptors are ready. Metadata-only changes also restart it without producing a physical migration. Autoapply is restricted to development and does not apply manual/destructive migrations. Restart the watcher after an externally applied manual migration. Unrelated application files are not hot-reloaded by this schema watcher.

The diff uses the latest versioned snapshot, including pending migrations, never the developer's live database. An unchanged schema produces no migration. Renames require --renames map.json with {tables:{old:new},columns:{table:{old:new}}}. Checksums, predecessor/catalog guards and history writes are transactional. Applied migrations are immutable; incomplete publication, forks and drift fail closed.

Type/constraint changes, removals, rebuilds and opaque objects require reviewed manual SQL. For structural drafts, rehearse the canonical resulting DDL on a disposable database and use finalize migration --verified-url URL; the verified catalog becomes a transactional postcondition. Destructive application requires --allow-destructive. There is no automatic rollback/data recovery command. New clock defaults use CURRENT_TIMESTAMP; the client emits datetime('now') in mutations so rqlite can rewrite the timestamp before replication. The generator rejects strftime(now) defaults that rqlite would freeze during CREATE TABLE.

Programmatic tools and compiled deployment

import {loadProject} from '@cubs/rqlite-client/project';
const project = await loadProject('./rqlite.config.ts');
await project.createMigration('add field');
await project.migrate('development');
await project.check('development');

createProject(config, {root}) also accepts configuration directly. Init, generate, create/finalize migration, status, migrate, check, wait, prepare and watch use the same services as the CLI. Runtime clients do not load the schema compiler.

For deployment, add these explicit compiler-output paths to configuration:

build: {
  config: 'dist/rqlite.config.js',
  client: 'dist/generated/schema.js',
  migrations: 'dist/migrations',
}

Compile your generated client and migrations with TypeScript, then run npx rqlite prepare. It checks compiled migrations, copies metadata and writes a runtime config. Deploy the compiled files and installed package. Startup can run rqlite wait, rqlite migrate, and rqlite check --database with --config dist/rqlite.config.js --environment production before starting the app. Compiled startup imports neither TypeScript compiler nor tsx.

Advanced exports include /compiler, /migrations and /compat. Compatibility adapters reuse package SQL generation and CRUD while allowing an application to supply its existing transactional guards and response conventions. They are not required for a new application.