Database
Databases: SQL for an app or an agent, SQLite by default and Postgres when you outgrow it.
db = noclick.Database.from_name("orders", create_if_missing=True)
db.migrate("migrations/")
db.execute("insert into orders (id, item, qty) values (?, ?, ?)", ["o1", "bun", 3])
for row in db.query("select item, qty from orders where qty > ?", [2]):
print(row["item"], row["qty"])The same object serves an app (bound to a project) and an agent keeping its own
state. Placeholders are ? with a list, or :name with a dict, on either engine.
class DatabaseA SQL database: SQLite (the default) or Postgres. query reads, execute and batch write,
migrate applies your schema. Database.fromName("orders", { createIfMissing: true }) is lazy.
In an agent's or a toolkit's tools it is three tools, {name}__query, {name}__execute and
{name}__schema; only(...) picks some of them.
- Like every live object, it also has hydrate(), refresh() and raw.
| id | string | |
| name | string | |
| engine | DatabaseEngine |
|
| status | DatabaseStatus |
|
| sizeBytes | number | null | |
| tableCount | number | null | |
| parentId | string | null | The database a branch was made from (null if it isn't one). |
| usedBy | UsedBy[] | Who uses it: apps that bind it ( |
| region | RegionName | null | The region it keeps its data in, fixed when it was created. |
| userId | string | null | |
| workflow | { id: string; name: string | null } | null | The automation whose tables it holds (NoClick makes one for each automation that keeps tables), or null. |
| createdAt | Date | null | |
| consoleUrl | string | null | This database in the developer console (null off the hosted API). |
only
only(...operations: DatabaseTool[]): DatabaseOperationsA tool ref to some of its operations, for tools: "query" (a read-only statement),
"execute" (a statement that writes) and "schema" (its tables and columns). The database
itself in tools gives all three. "upsert" writes one row of one table by its primary key,
its parameters that table's columns; bind its table with .bind({ table }).
A ref to just those operations, to list in tools=; bind(...) fixes arguments.
createstatic
static create(name: string, options?: DatabaseCreateOptions): Promise<Database>Create a database, unique by name per user (a taken name throws Conflict with existingId).
"sqlite" (the default, cheap while idle) or "postgres".
"sqlite")Where it runs or keeps its data (regions()); omitted, your account's default region.
undefined = the ambient user (asUser), null = the account itself.
The client to send with; defaults to the module client built from NOCLICK_API_KEY.
The new database, ready and empty.
fromNamestatic
static fromName(
name: string,
options?: DatabaseCreateOptions & {
createIfMissing?: boolean;
branchData?: BranchData;
},
): DatabaseA lazy reference to the database called name for the current (or given) user. With
createIfMissing it's created on first use with engine; one that exists is used as it is.
In a preview branch of an app whose preview_resources is branch, the branch gets its own copy
at its first write and reads production's as it is until then. A Postgres copy is a copy-on-write
branch of production's data; a SQLite copy holds the schema and migrations only (migrate's seed
fills it), unless branchData: "copy" asks for production's rows too, read at one moment while
production keeps serving (up to 200,000 rows and 16 MB). It changes nothing outside a preview.
"sqlite" (the default, cheap while idle) or "postgres".
"sqlite")Where it runs or keeps its data (regions()); omitted, your account's default region.
undefined = the ambient user (asUser), null = the account itself.
The client to send with; defaults to the module client built from NOCLICK_API_KEY.
A database that loads (or is created) the first time it's used. Nothing is sent yet.
fromIdstatic
static fromId(databaseId: string, options?: CallOptions): DatabaseA lazy reference to a database by id.
A database that loads the first time you read a field. Nothing is sent yet.
getstatic
static get(databaseId: string, options?: CallOptions): Promise<Database>Load a database by id now; an unknown id raises noclick.NotFound.
The database, loaded: its engine, size, table count and status.
liststatic
static list(
options?: ReadOptions & { engine?: DatabaseEngine; limit?: number },
): Promise<Database[]>The current user's databases, branches included (or those userFilter covers).
Return at most this many.
Which users the read covers (user_id query), instead of acting as one (user).
undefined = the ambient user (asUser), null = the account itself.
The client to send with; defaults to the module client built from NOCLICK_API_KEY.
Every matching database, newest first, fetched page by page.
listPagestatic
static listPage(
options?: ReadOptions & PageOptions & { engine?: DatabaseEngine },
): Promise<Page<Database>>One page of databases; pass nextCursor back as cursor.
The next_cursor of the page before; omit it for the first page.
Return at most this many.
Which users the read covers (user_id query), instead of acting as one (user).
undefined = the ambient user (asUser), null = the account itself.
The client to send with; defaults to the module client built from NOCLICK_API_KEY.
A page whose data holds the databases and whose next_cursor is None on the last page.
query
query(sql: string, params?: SqlParams): Promise<QueryResult>Run a read: one select (or with ... select). It runs read-only: a statement that writes
throws InvalidRequest; use execute for those. At most 10 MB of rows; 30 seconds. On
Postgres, noclick.embed(:q, model) turns a parameter or a quoted text into a halfvec
before the statement runs (up to 100 distinct texts a request; not over connectionUrl), so a
read (an agent's query tool too) can search by meaning (billed as AI to the database's user).
await kb.query("select id from articles order by embedding <=> noclick.embed(:q, :model) limit 5", { q, model });The rows as dicts, in rows or by iterating the result, with columns, rows_read
and duration_ms.
execute
execute(sql: string, params?: SqlParams): Promise<QueryResult>Run one statement that writes (several go in a batch). A SQLite database that would pass
10 GB throws QuotaExceeded, whose next step is branching into Postgres.
What it did: rows_written, last_row_id for an insert on SQLite, and the rows
a returning clause gave back.
batch
batch(statements: readonly SqlStatement[]): Promise<QueryResult[]>Run up to 100 statements in one transaction: all apply or none do.
One QueryResult per statement, in order.
migrate
migrate(
migrations: Migrations,
options?: { dryRun?: boolean; seed?: string },
): Promise<MigrationResult>Apply the migrations the database hasn't run, in name order, in one transaction (a failing
one applies none). In Node, read your migrations/*.sql files into an object first.
dryRun reports what would run and changes nothing. seed is SQL run once, after them and in
their transaction, on a preview branch's database that holds none of production's data (a SQLite
copy made without its rows, or one only the branch has); production never runs it, and it shows
in applied as (seed).
The migrations applied now, and those still pending (all the new ones after a dry run).
migrations
migrations(): Promise<Migration[]>The migrations this database has applied, oldest first.
Each applied migration with when it ran, oldest first.
schema
schema(): Promise<Table[]>The database's tables, their columns and row counts.
Every table by name, each with its columns in order and its row_count.
restore
restore(options: { at: Date | string }): Promise<Operation<Database>>Put the database back as it was at at (within its history: 30 days on SQLite, 7 on
Postgres), in place. wait() resolves to this database, restored. To keep the present,
branch at that time instead.
An operation whose wait() gives this database, restored.
branch
branch(
name: string,
options?: { at?: Date | string; engine?: DatabaseEngine },
): Promise<Operation<Database>>Copy the database into a new one called name, as it is now or as it was at (within its
history). engine: "postgres" branches SQLite into Postgres. While a SQLite database is
copied its status is branching. wait() resolves to the new Database.
An operation whose wait() gives the new Database.
branches
branches(): Promise<Database[]>The databases branched from this one.
Each branch as a Database, newest first.
connectionUrl
connectionUrl(options?: { ttlS?: number; readOnly?: boolean }): Promise<string>A postgresql:// URL with a password that stops working after ttlS seconds (default
3600), when any session still open on it ends. SQLite throws NotAvailable. Treat it as a
secret.
3600A postgresql:// URL with its short-lived credentials inside; treat it as a secret.
export
export(): Promise<Operation<ExportBundle>>Export as SQL that recreates its schema and rows in any database of its engine:
sqlite3 booking.db < databases/booking-db.sql, or a pg_dump script for psql (NoClick's own
noclick schema, owners and grants left out). Exporting a Postgres database wakes it, and its
compute is billed as any use is. wait() resolves to an ExportBundle: a zip at url holding the dump.
An operation whose wait() gives an ExportBundle: a zip at url holding the dump.
delete
delete(): Promise<TrashItem>Delete the database and its data: it goes to the trash, restorable for 30 days, and its
connection URLs stop working. One with branches, an app's binding or an agent's tool throws
Conflict. Returns its TrashItem.
Its TrashItem: restore() puts it back, tables and rows with it.
DatabaseOperations
class DatabaseOperationsSome of a database's operations as tools (db.only(...)), with the arguments bind(...) fixed.
| database | Database | |
| operations | DatabaseTool[] | |
| bindings | JsonObject | The arguments NoClick fills on every call, out of the model's reach. |
bind
bind(args: JsonObject): DatabaseOperationsFix arguments of these operations, as ConnectionOperations.bind does. upsert needs table
bound; bind a column too and every row it writes carries that value.
leads.only("upsert").bind({ table: "enriched" })A new ref with these arguments bound on top of any bound before.
QueryResult
class QueryResultWhat one statement returned: rows (an object per row, keyed by columns), and what it
touched (on SQLite the rows read and written, what it's billed on; on Postgres the rows
returned and changed, not billed). Iterating it yields the rows. lastRowId is the rowid an
insert made on SQLite (null on Postgres, where returning gives it).
| columns | string[] | |
| rows | JsonObject[] | |
| rowsRead | number | |
| rowsWritten | number | |
| lastRowId | number | null | |
| durationMs | number | null | |
| raw | JsonObject | The response JSON exactly as the API sent it. |
lengthTypeScript only
length: numberMigration
interface MigrationA migration the database has applied, and the checksum of the SQL that ran.
| name | string | |
| appliedAt | Date | null | |
| checksum | string | null | |
| raw | JsonObject | The response JSON exactly as the API sent it. |
MigrationResult
interface MigrationResultWhat migrate did: what it ran now, and what's still to run (everything new after a dry run).
| applied | string[] | |
| pending | string[] | |
| raw | JsonObject | The response JSON exactly as the API sent it. |
Table
interface TableA table: its columns in order and its row count (the planner's estimate on Postgres).
| name | string | |
| columns | Column[] | |
| rowCount | number | null | |
| raw | JsonObject | The response JSON exactly as the API sent it. |
Column
interface ColumnOne column of a table. default is its default expression, or null.
| name | string | |
| type | string | |
| nullable | boolean | |
| primaryKey | boolean | |
| default | string | null |