@boltstore/client
v1.0.2
Published
Boltstore client SDK — JavaScript/TypeScript SDK for Boltstore
Readme
@boltstore/client
JavaScript/TypeScript SDK for Boltstore — the self-hostable SQLite-over-HTTP Database-as-a-Service.
HTTP-only. No realtime, no offline sync, no client-side cache. Boltstore is a database platform (DBaaS), not a BaaS — BaaS-style features (RLS, user auth, realtime, sync) are application-layer concerns or future plugin territory, not part of this SDK. The client talks to the Boltstore server REST API directly.
Installation
npm install @boltstore/clientQuick start
import { BoltstoreClient } from "@boltstore/client";
const client = new BoltstoreClient({
url: "http://localhost:8080",
database: "myapp",
key: "boltstore_...", // per-database API key or admin session token
});
// Tables
const tables = await client.tables.list();
await client.tables.create("posts", [
{ name: "id", type: "integer", primary_key: true, auto_increment: true },
{ name: "title", type: "text", nullable: false },
{ name: "views", type: "integer", default: "0" },
]);
// Typed record CRUD
const posts = client.table<{ id: number; title: string; views: number }>("posts");
const created = await posts.create({ title: "Hello World", views: 0 });
const fetched = await posts.get(created.id);
await posts.update(created.id, { views: 1 });
await posts.delete(created.id);
// Query builder
const list = await posts
.query()
.where("title", "like", "Hello%")
.orderBy("id", "desc")
.limit(10)
.get();
// Raw SQL
const rows = await client.sql<{ id: number; title: string }>(
"SELECT id, title FROM posts WHERE views > ? ORDER BY id",
[0],
);Configuration
const client = new BoltstoreClient({
url: string; // Boltstore server base URL, e.g. "http://localhost:8080"
database: string; // Database name (must match VALID_NAME: /^[a-z0-9][a-z0-9_-]*$/)
key?: string; // API key or admin session token. Set later via setKey().
});
client.setKey(key: string | undefined): void;The key is sent as Authorization: Bearer <key> on every request. Use a per-database API key for data access, or an admin session token (from POST /api/admin/login) for admin methods.
API
Database operations
// Requires API key or admin session
await client.info(): Promise<DatabaseInfo>; // GET /api/databases/:database
await client.delete(): Promise<void>; // DELETE /api/databases/:database
await client.export(): Promise<Blob>; // POST /api/databases/:database/export
await BoltstoreClient.import({ // static — POST /api/databases/import (admin only)
url: string;
file: Blob | File;
name?: string;
key?: string; // admin session token required for import
}): Promise<DatabaseInfo>;Config
// Requires API key or admin session
await client.config.get(): Promise<Record<string, unknown>>;
await client.config.update(data: Record<string, unknown>): Promise<Record<string, unknown>>;API keys
// Requires API key or admin session
await client.keys.list(): Promise<ApiKey[]>;
await client.keys.create(label: string): Promise<CreatedApiKey>; // key is returned once
await client.keys.rotate(keyId: string): Promise<{ id: string; key: string }>;
await client.keys.revoke(keyId: string): Promise<void>;Tables
// Accessible with API key or admin
await client.tables.list(): Promise<string[]>;
await client.tables.create(name: string, columns: ColumnDef[]): Promise<{ name: string; columns: ColumnDef[] }>;
await client.tables.get(name: string): Promise<TableSchema>;
await client.tables.update(name: string, changes: {
name?: string;
add_columns?: ColumnDef[];
drop_columns?: string[];
rename_column?: { from: string; to: string };
}): Promise<void>;
await client.tables.delete(name: string): Promise<void>;Records (typed)
const t = client.table<Fields>("table_name");
await t.create(data: Fields): Promise<Fields>;
await t.list(opts?: {
filter?: Record<string, unknown>; // compiled to server filter DSL
sort?: string; // e.g. "created_at" or "-views,created_at"
limit?: number; // server max is 1000, default 50
offset?: number;
fields?: string[]; // column projection
}): Promise<PaginatedResult<Fields>>;
await t.get(id: string | number): Promise<Fields | null>;
await t.update(id: string | number, data: Partial<Fields>): Promise<Fields>;
await t.delete(id: string | number): Promise<void>;
t.query(): QueryBuilder<Fields>;Query builder
const q = t.query();
q.where(field: string, op: string, value: unknown): this; // op: "eq" | "ne" | "gt" | "gte" | "lt" | "lte" | "in" | "like" | "glob"
q.orWhere(field: string, op: string, value: unknown): this;
q.orderBy(field: string, dir?: "asc" | "desc"): this;
q.limit(n: number): this;
q.offset(n: number): this;
q.select(...fields: (keyof Fields)[]): this;
await q.get(): Promise<Fields[]>; // returns matching rows
await q.first(): Promise<Fields | null>; // limit(1).get()[0] ?? null
await q.count(): Promise<number>; // ⚠️ see "Known issues" below
await q.paginate(page: number, perPage?: number): Promise<PaginatedResult<Fields>>;Raw SQL
await client.sql<T = Record<string, unknown>>(sql: string, params?: unknown[]): Promise<T[]>;Sends { sql, params } to POST /api/databases/:database/query. API keys can execute any SQL statement (DDL, DML, SELECT, PRAGMA, etc.). Databases in read-only mode reject writes for all keys. See the server README's "Raw SQL endpoint" section.
Health
await client.health(): Promise<HealthCheck>; // { status, version, databases }Types
interface DatabaseInfo { id: string; name: string; path: string; createdAt: string; tables?: string[]; }
interface ApiKey { id: string; label: string; created_at: string; last_used_at?: string; }
interface CreatedApiKey extends ApiKey { key: string; }
interface TableSchema { name: string; columns: TableColumn[]; }
interface TableColumn { cid: number; name: string; type: string; notnull: number; dflt_value?: string; pk: number; }
interface ColumnDef {
name: string;
type: "text" | "integer" | "real" | "blob" | "numeric" | "boolean" | "date" | "datetime";
nullable?: boolean;
primary_key?: boolean;
auto_increment?: boolean;
unique?: boolean;
default?: string;
references?: { table: string; column: string };
}
interface ApiResponse<T> { data?: T; meta?: Record<string, unknown>; error?: { code: string; message: string; details?: unknown }; }
interface PaginatedResult<T> { data: T[]; total: number; limit: number; offset: number; }
interface HealthCheck { status: string; version: string; databases: number; }Authentication model
The SDK holds a single key used for every request. Most methods (info, export, config.*, keys.*, tables.*, table(), sql()) accept either a per-database API key or an admin session token. Only import and delete require admin credentials.
Known issues
- Server-enforced limits apply. The server caps list results at 1000 records per request. Use
offsetwithlist()for pagination beyond this limit.
Development
bun install
bun run build # compile TypeScript to dist/
bun test # run tests
bun run dev # watch modePublishing
npm publishLicense
MIT
