A tiny (1034b) runtime-agnostic Hrana HTTP client
This package is compatible with all JavaScript runtimes and is available on multiple registries:
-
npm — available as
hrana
-
JSR — available as
@lukeed/hrana
import * as hrana from 'hrana';
let credentials: hrana.Config = {
url: 'https://<database>.turso.io',
token: 'eyJ...',
};
let rows = await hrana.execute(credentials, {
sql: 'select * from users where orgid=? limit 2',
args: [
hrana.encode('acme'),
],
});
//-> {
//-> cols: [
//-> { name: "id", decltype: "INTEGER" },
//-> { name: "name", decltype: "TEXT" },
//-> // ...
//-> ],
//-> rows: [
//-> [
//-> { type: "integer", value: "1" },
//-> { type: "text", value: "lukeed" }
//-> // ...
//-> ],
//-> [
//-> { type: "integer", value: "12" },
//-> { type: "text", value: "foobar" }
//-> // ...
//-> ],
//-> ],
//-> affected_row_count: 0,
//-> last_insert_rowid: null,
//-> replication_index: "...",
//-> rows_read: 2,
//-> rows_written: 0,
//-> query_duration_ms: 0.09,
//-> }
type User = {
id: number;
name: string;
// ...
};
let users = hrana.parse<User>(rows);
//-> [
//-> { id: 1, name: 'lukeed' },
//-> { id: 12, name: 'foobar' },
//-> ]
When converting a Hrana.StmtResult
into your custom T
type, you may provide parse()
with column-specific transformers.
For example, to parse an apps.configs
(TEXT) column into JSON, you can do this:
type App = {
id: number;
name: string;
config: {
plan: string;
seats: number;
};
};
let apps = hrana.parse<App>(rows, {
config: (v) => JSON.parse(v as string),
});
//-> [
//-> {
//-> id: 123,
//-> name: 'Example',
//-> config: {
//-> plan: 'Free',
//-> seats: 1,
//-> }
//-> }
//-> ]
You can execute multiple statements in one roundtrip. These may also be executed conditionally based on the success or failure of previous statements.
See the Hrana 3 Spec for more information.
let r = await hrana.batch(credentials, [{
stmt: {
sql: `select 1`,
},
}, {
stmt: {
sql: `select * from users limit 1`,
},
condition: {
type: 'and',
conds: [{
type: 'ok',
step: 0, // only run if Step#0 is ok
}],
},
}]);
A transaction is like a batch statement except that the group of statements fails/succeeds collectively.
The transaction()
helper manages the condition
value for each step, and wraps the group with the proper BEGIN
, COMMIT
, and ROLLBACK
statements.
let tx = await hrana.transaction(credentials, 'deferred', {
sql: 'select * from apps limit 1',
}, {
sql: 'select * from apps limit 1 offset 2',
});
console.log(
tx.step_results.forEach((r) =>
r && m.parse<App>(r, {
config: (v) => JSON.parse(v as string),
})
),
);
Please refer to the generated API documentation, as it's always kept up-to-date.
Note: The API is the same across all JavaScript runtimes, regardless of registry — only the package name changes.
MIT © Luke Edwards