libsql-stateless
Thin libSQL stateless HTTP driver for TypeScript and JavaScript.
- ✅ Supported runtime environments: Web API (browser, serverless), Bun, Node.js (>=18)
- ✅ Extremely thin: Has no dependency, only has a few functions that implement the
Hrana v3 HTTP
protocol from scratch, and has no classes (tend to hog memory). - ✅ Does no extra computation.
- ✅ Has no premature optimizations.
- ✅ Is built for: Quick stateless query execution. (Mainly for serverless and edge functions.)
- ⚠️ Supports everything in @libsql/client except
transactions
.
Installation
$ npm i libsql-stateless
$ bun add libsql-stateless
⚠️ WARNING: The Docs are out of date. Give me a few days.
The Result Type
Every function in this library returns a Result<T, R>
type.
Result<T, R>
is either:
{
isOk: true,
val: T
}
or:
{
isOk: false,
err: R
}
For example, the function await libsqlExecute(conf: libsql_conf, stmt: libsql_statement)
returns Result<libsql_statement_result, libsql_error>
, which is either:
{
isOk: true,
val: libsql_statement_result
}
or:
{
isOk: false,
err: libsql_error
}
Benefits of Result Type
- No need of ugly try { } catch { }.
- Elegant error handling:
const res = await libsqlExecute(conf, {sql: "SELECT * FROM mad_cats;"});
if (res.isOk) {
console.log(res.val.rows);
console.log(res.val.affected_row_count)
} else {
console.log(res.err.message);
doSomething(res.err.code)
}
The libsqlExecute
Function
This function executes exactly one (1) SQL query.
Type
async function libsqlExecute(conf: libsql_conf, stmt: libsql_statement): Promise<Result<libsql_statement_result, libsql_error>>;
Parameters
conf
of type libsql_conf
import { libsql_conf } from "libsql-stateless";
{
db_url: string,
authToken?: string
}
stmt
of type libsql_statement
import { libsql_statement, libsql_value } from "libsql-stateless";
{
"sql": string,
"args"?: Array<libsql_value>,
"named_args"?: Array<{
"name": string,
"value": libsql_value,
}>,
"want_rows"?: boolean,
}
{ "type": "null" } |
{ "type": "integer", "value": string } |
{ "type": "float", "value": number } |
{ "type": "text", "value": string } |
{ "type": "blob", "base64": string };
Returns
This function returns a Promise<Result<libsql_statement_result, libsql_error>>
therefore await
is used before it to get Result<libsql_statement_result, libsql_error>
.
Result<T, R>
types have heen discussed above.
import { libsql_statement_result, libsql_error, libsql_column, libsql_value } from "libsql-stateless";
{
"message": string;
"code"?: string | null;
}
{
"cols": Array<libsql_column>,
"rows": Array<Array<libsql_value>>,
"affected_row_count": number,
"last_insert_rowid": string | null,
}
{
"name": string | null,
"decltype": string | null,
}
{ "type": "null" } |
{ "type": "integer", "value": string } |
{ "type": "float", "value": number } |
{ "type": "text", "value": string } |
{ "type": "blob", "base64": string };
Example
import { libsqlExecute } from "libsql-stateless";
const { libsqlExecute } = require("libsql-stateless");
const res = await libsqlExecute(conf, {sql: "SELECT * FROM mad_cats;"});
const res = await libsqlExecute(conf, {
sql: "SELECT madness, power_level FROM mad_cats WHERE cat_id = ? AND owner_name = ?;",
args: [
{
type: "integer",
value: "89342"
},
{
type: "text",
value: "John Smith"
}
]
});
const res = await libsqlExecute(conf, {
sql: "INSERT INTO mad_cats VALUES (:cat_name, :power_level, :madness);",
named_args: [
{
name: "cat_name",
value: {
type: "text",
value: "Bgorthe, The Destroyer of Worlds"
}
},
{
name: "power_level",
value: {
type: "integer",
value: "9105"
}
},
{
name: "madness",
value: {
type: "float",
value: "32.5"
}
}
]
});
The libsqlBatch
Function
This function executes SQL queries in a batch.
Type
async function libsqlBatch(conf: libsql_conf, batch_steps: Array<libsql_batch_step>): Promise<Result<libsql_batch_statement_result, libsql_error>>;
Parameters
conf
of type libsql_conf
import { libsql_conf } from "libsql-stateless";
{
db_url: string,
authToken?: string
}
batch_steps
of type Array<libsql_batch_step>
import { libsql_batch_step, libsql_batch_execution_condition, libsql_statement } from "libsql-stateless";
{
"condition"?: libsql_batch_execution_condition | null;
"stmt": libsql_statement;
}
{ "type": "ok", "step": number } |
{ "type": "error", "step": number } |
{ "type": "not", "cond": libsql_batch_execution_condition } |
{ "type": "and", "conds": Array<libsql_batch_execution_condition> } |
{ "type": "or", "conds": Array<libsql_batch_execution_condition> } |
{ "type": "is_autocommit" };
{
"sql": string,
"args"?: Array<libsql_value>,
"named_args"?: Array<{
"name": string,
"value": libsql_value,
}>,
"want_rows"?: boolean,
}
Returns
This function returns a Promise<Result<libsql_batch_statement_result, libsql_error>>
therefore await
is used before it to get Result<libsql_batch_statement_result, libsql_error>
.
Result<T, R>
types have heen discussed above.
import { libsql_batch_statement_result, libsql_error, libsql_statement_result, libsql_column, libsql_value } from "libsql-stateless";
{
"message": string;
"code"?: string | null;
}
{
"step_results": Array<libsql_statement_result | null>;
"step_errors": Array<libsql_error | null>;
}
{
"cols": Array<libsql_column>,
"rows": Array<Array<libsql_value>>,
"affected_row_count": number,
"last_insert_rowid": string | null,
}
{
"name": string | null,
"decltype": string | null,
}
{ "type": "null" } |
{ "type": "integer", "value": string } |
{ "type": "float", "value": number } |
{ "type": "text", "value": string } |
{ "type": "blob", "base64": string };
Example
import { libsqlBatch } from "libsql-stateless";
const { libsqlBatch } = require("libsql-stateless");
const res = await libsqlBatch(conf, [
{stmt: {sql: "SELECT * FROM mad_cats;"}},
{stmt: {
sql: "SELECT madness, power_level FROM mad_cats WHERE cat_id = ? AND owner_name = ?;",
args: [
{
type: "integer",
value: "89342"
},
{
type: "text",
value: "John Smith"
}
]
}},
{stmt: {
sql: "INSERT INTO mad_cats VALUES (:cat_name, :power_level, :madness);",
named_args: [
{
name: "cat_name",
value: {
type: "text",
value: "Bgorthe, The Destroyer of Worlds"
}
},
{
name: "power_level",
value: {
type: "integer",
value: "9105"
}
},
{
name: "madness",
value: {
type: "float",
value: "32.5"
}
}
]
}}
]);
const res = await libsqlBatch(conf, [
{stmt: {sql: "SELECT * FROM mad_cats;"}, condition: { "type": "is_autocommit" }}
]);
The libsqlServerCompatCheck
Function
This function checks if the db_url
's server supports Hrana HTTP API v3
.
Type
async function libsqlServerCompatCheck(db_url: string): Promise<Result<null, null>>;
Parameters
db_url
of type string
Returns
This function returns a Promise<Result<null, null>>
therefore await
is used before it to get Result<null, null>
.
Result<T, R>
types have heen discussed above.
Example
import { libsqlServerCompatCheck } from "libsql-stateless";
const { libsqlServerCompatCheck } = require("libsql-stateless");
const res = await libsqlServerCompatCheck(conf.db_url);
TODO
- Add JS/TS Docs along with example snippets.
- Add Drizzle ORM support?
- Finding the purpose of my existence.
Special Thanks