libSQL JavaScript Client (@libsql/client)
The libSQL JavaScript Client (`@libsql/client`) is a comprehensive TypeScript/JavaScript driver for interacting with libSQL databases, a fork of SQLite. It aims for API compatibility with `better-sqlite3`, offering both synchronous and opt-in promise-based APIs. This client supports Node.js, Bun, Deno, and web browsers, facilitating connections to in-memory, local file-based, and remote libSQL instances (including Turso databases). Key differentiators include support for embedded replicas (local SQLite files that sync with remote Turso databases for offline capabilities), remote access over HTTP/WebSockets, and advanced features like encryption at rest and AI/Vector Search integration when used with Turso. The current stable version is `0.5.29`, with active development towards a `0.6.x` release, which has introduced some breaking changes and new features. The npm package `libsql` is deprecated; users should install and import from `@libsql/client` instead.
Common errors
-
Error [LibsqlError]: URL_INVALID: The URL is not in a valid format
cause The connection URL provided to `createClient` is malformed or invalid, or environment variables are not correctly loaded/passed.fixVerify that `process.env.TURSO_DATABASE_URL` or the directly provided URL string is in the correct format (e.g., `libsql://your-db-slug-your-org.turso.io` or `file:./local.db`). Ensure environment variables are loaded (e.g., with `dotenv`). -
Error: Unexpected end of JSON input
cause This error was observed in `@libsql/client` version `0.6.1` due to an internal parsing issue, particularly with database push operations.fixUpgrade `@libsql/client` to version `0.6.2` or newer. This issue was a specific bug fixed in that release. -
Cannot read properties of undefined (reading 'rows') or similar for query results
cause Attempting to access properties like `.rows` on a `ResultSet` that might be empty or undefined, or incorrectly assuming an `execute` call will always return rows when it's an `INSERT` or `UPDATE`.fixAlways check if `selectResult.rows` exists and has elements before accessing `selectResult.rows[0]`. Operations like `client.execute('INSERT ...')` return `ResultSet` but `rows` array might be empty. Use `ResultSet.rowsAffected` for mutation operations. Consider `client.transaction` for complex read/write operations. -
Error: SQLite error: UNIQUE constraint failed: users.email
cause Attempting to insert or update a record with a value in a `UNIQUE` column (e.g., `email`) that already exists in the database.fixImplement logic to prevent duplicate insertions, such as checking for existing records before inserting, or handling the error gracefully by providing user feedback. Ensure your application logic respects database constraints.
Warnings
- breaking The `libsql` npm package is deprecated. Users should migrate to `@libsql/client` for the official JavaScript/TypeScript client.
- breaking Versions of `@libsql/client` greater than `0.6.0` had issues pushing to Turso schema databases, particularly when integrated with ORMs like Drizzle. A bug fix was released in `0.6.2` addressing an `Unexpected end of JSON input` error.
- gotcha While `@libsql/client` provides a `better-sqlite3` compatible API, using `Database` directly from `libsql/node` or `libsql/promise` might retain synchronous blocking behavior in some contexts or have subtle differences in error handling or advanced features compared to the native `better-sqlite3` library.
- gotcha Connecting to remote libSQL instances (e.g., Turso) requires a valid URL and an authentication token. Incorrect or missing credentials, or an invalid URL format, will lead to connection failures.
- gotcha The client automatically selects an implementation based on the URL scheme (e.g., `file:` for local SQLite, `http(s):` or `ws(s):` for remote). `file:` URLs are only supported in Node.js and will not work in browser or serverless environments without local filesystem access.
Install
-
npm install libsql -
yarn add libsql -
pnpm add libsql
Imports
- createClient
const createClient = require('@libsql/client').createClient;import { createClient } from '@libsql/client'; - Client
import { Client } from '@libsql/client';import type { Client } from '@libsql/client'; - Config
import type { Config } from '@libsql/client'; - Database (legacy/sync API)
import Database from '@libsql/client';
import Database from '@libsql/client/node'; // for Node.js sync API import Database from '@libsql/client/promise'; // for Node.js async API
Quickstart
import { createClient } from '@libsql/client';
import 'dotenv/config';
const tursoUrl = process.env.TURSO_DATABASE_URL ?? '';
const tursoAuthToken = process.env.TURSO_AUTH_TOKEN ?? '';
if (!tursoUrl) {
console.error('TURSO_DATABASE_URL environment variable is not set.');
process.exit(1);
}
// Create a client for a remote Turso database
const client = createClient({
url: tursoUrl,
authToken: tursoAuthToken,
});
async function runExample() {
try {
// Create a table if it doesn't exist
await client.execute(`
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT UNIQUE NOT NULL
);
`);
console.log('Table "users" ensured to exist.');
// Insert data (batch operation for efficiency)
const insertResult = await client.batch(
[
{ sql: 'INSERT INTO users (name, email) VALUES (?, ?)', args: ['Alice', 'alice@example.com'] },
{ sql: 'INSERT INTO users (name, email) VALUES (?, ?)', args: ['Bob', 'bob@example.com'] }
],
'write'
);
console.log('Inserted two users.', insertResult);
// Query data
const selectResult = await client.execute('SELECT * FROM users WHERE name = ?', ['Alice']);
console.log('Query result for Alice:', selectResult.rows);
// Update data within a transaction
await client.transaction(async tx => {
await tx.execute('UPDATE users SET name = ? WHERE email = ?', ['Alicia', 'alice@example.com']);
// console.log('Updated Alice to Alicia within transaction.');
throw new Error("Simulating a rollback for demonstration purposes"); // Transaction will roll back
}).catch(e => {
if (e.message !== "Simulating a rollback for demonstration purposes") {
throw e; // Re-throw if it's not our simulated error
}
console.log("Transaction rolled back as intended.");
});
// Verify if update was rolled back
const verifyResult = await client.execute('SELECT name FROM users WHERE email = ?', ['alice@example.com']);
console.log('Name after attempted update (should be Alice):', verifyResult.rows[0].name);
} catch (e) {
console.error('An error occurred:', e);
}
}
runExample();