# libsql-client-ts: a TypeScript client for libSQL and Turso

> The libSQL TypeScript client gives JavaScript and TypeScript applications one API for a local SQLite file, a remote Turso database, or both with embedded replication between them. It is published on npm as @libsql/client, it adds vector search and encryption at rest, and it comes with a sibling package, @tursodatabase/serverless, for deployments that want no native dependencies at all.

**tursodatabase/libsql-client-ts** — TypeScript/JavaScript client API for libSQL

- Repository: https://github.com/tursodatabase/libsql-client-ts
- Website: https://docs.turso.tech/sdk/ts/quickstart
- Stars: 577 · Forks: 70
- Language: TypeScript
- License: MIT
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/tursodatabase-libsql-client-ts

## Two drivers, and the README says which to pick

The most useful paragraph in the README is the one about choosing a package, because it is a recommendation rather than a feature list.

If you are looking for the Turso serverless package, the README points you at @tursodatabase/serverless, described as the lightest option with zero native dependencies, and says it will be the driver to later support concurrent writes. The instruction is to use @libsql/client if you need a battle-tested driver today with ORM integration.

So the two packages are positioned differently. @libsql/client is the established driver, the one with ORM integration behind it, and it is the package the README's own quickstart uses. @tursodatabase/serverless is the lighter option for environments where a native module is a problem, and it comes with an explicit limitation about concurrent writes that have not landed yet.

That is the kind of comparison a README usually hides, and it is worth reading closely, because choosing the wrong one means either shipping a native dependency you cannot, or shipping a driver that cannot handle the write pattern your application has.

The package is published on npm as @libsql/client, the licence is MIT, and the primary language of the repository is TypeScript. The homepage is the quickstart page in the Turso documentation at docs.turso.tech/sdk/ts/quickstart, with a full SDK reference alongside it.

## Embedded replicas put a local file next to a remote database

The feature list is short, and the first item is the one that changes the shape of an application: it works offline with Embedded Replicas.

The quickstart shows what that means in code. The client is created with a local file URL, a remote sync URL, an auth token, and a sync interval:

```ts
import { createClient } from "@libsql/client";

export const turso = createClient({
    url: "file:local.db",
    syncUrl: process.env.TURSO_DATABASE_URL,
    authToken: process.env.TURSO_AUTH_TOKEN,
    syncInterval: 60000,
});
```

The example uses Embedded Replicas and syncs every minute from Turso, so the local SQLite file is the thing the application reads while the remote database is the thing that holds the canonical copy. The application code does not change when the connection target changes, which is the point of a client that speaks to both.

The remaining features are remote Turso databases, AI and vector search, and encryption at rest. The first of those is the plain case, the local file URL replaced by a remote URL and a token.

The sync interval is worth thinking about, because it is the point at which the tool decides on your behalf how stale a local replica is allowed to be. Sixty seconds is the example, and it is a reasonable default for an interactive application, and it is a poor default for anything that has to be correct immediately after a write on another device. The repository includes a read-your-writes example directory, which suggests that the case is handled somewhere, and the way to find out how is to read that example.

## Batch and execute, with parameters bound rather than interpolated

The query API is two calls, and both of them bind parameters.

```ts
await turso.batch(
    [
        "CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT)",
        {
            sql: "INSERT INTO users(name) VALUES (?)",
            args: ["Iku"],
        },
    ],
    "write",
);

await turso.execute({
    sql: "SELECT * FROM users WHERE id = ?",
    args: [1],
});
```

The batch call takes a list where each entry is either a raw SQL string or an object with sql and args, and the second argument names the transaction mode, here write. The execute call takes the same object shape, so a statement and its parameters travel together and the placeholder is bound rather than pasted into the string.

That parameter binding is the part of this API that matters most for anything beyond a demo, because it is the difference between a client and a query string builder. A caller that concatenates a value into SQL gets an injection bug, and a client whose shape makes binding the obvious thing removes that whole class of mistake from the application.

The mode argument on batch is the other thing to notice. Passing write tells the engine the batch changes data, which is what lets the driver route it to the primary rather than treating it as a read that could be served from a replica.

## The examples directory is the feature list

The README keeps a table of examples, and it is the most efficient way to understand the scope of the client, because each row is a working program rather than a sentence of prose.

The local example uses libSQL with a local SQLite file, creating a database, inserting data and querying. The remote example connects to a remote database and needs environment variables for the URL and the auth token. The sync example demonstrates synchronisation between local and remote databases. The batch example executes multiple SQL statements in one batch operation. The transactions example shows starting a transaction, performing operations and committing or rolling back. The memory example uses an in-memory SQLite database for temporary storage or fast access. The vector example works with vector embeddings, storing and querying them for similarity search. The encryption example creates and uses an encrypted SQLite database. And the ollama example does similarity search with Ollama and Mistral.

The repository listing holds two more directories that the README table does not mention, examples/cloud-encryption and examples/read-your-writes, and both names point at real questions. A cloud encryption example suggests encryption on a hosted database rather than a local file, and a read your writes example exists because the hardest problem in a replicated setup is that a read immediately after a write may be served from a stale local copy.

For a developer evaluating this, running the two examples that match your architecture, local, remote or sync, and reading the two that match your risk, is a faster route to a decision than the API reference.

## Vector search and AI, and what Ollama has to do with it

The AI and vector search feature is the one that takes the client beyond a SQL driver, and the examples directory is where you can see what it actually means.

The vector example works with vector embeddings, storing and querying them for similarity search. That is a different query shape from the rest of the client, because similarity search is not expressed as a WHERE clause over columns. The README links the Turso AI and Vector Search documentation as a separate feature area, which suggests the embeddings side of the product is documented as its own thing rather than as an extension of the SQL API.

The ollama example is the one to look at if you run models locally. It does similarity search with Ollama and Mistral, so instead of sending an embedding request to a hosted service, the example drives a local model. That matters for two reasons. It keeps the embedding step inside your own machine, and it means the client is usable without a hosted AI dependency at all.

Note what is not claimed. The README does not say how embeddings are generated, whether the client calls out to a model for you, or what the cost of that call is. From the example names alone, the sensible assumption is that the application supplies or fetches the embedding and the client stores and queries it, but the visible documentation does not settle that, and the example directory is where the answer is.

The encryption at rest feature sits alongside this one in the list, and it has its own pair of examples, one for a local encrypted database and one for a cloud setup, which is the same distinction as everywhere else in this client: the local file case and the hosted case are different code paths with different options.

## A three package monorepo with a shell driven test suite

The repository is an npm workspace with three members: packages/libsql-core, packages/libsql-client and packages/libsql-client-wasm. The core package holds the shared pieces, the client package is the Node and browser driver, and the wasm package is the WebAssembly build, which is what makes the client usable in a browser without a native module.

The root scripts show how the project is worked on. The build runs across all workspaces, the typecheck runs across all workspaces, and the format check does the same. The test script is different: it calls ./testing/test.sh rather than a JavaScript test runner, which suggests the suite needs a real environment, most likely a running database and the native bits, and a shell script is the simplest way to coordinate that.

Commit hygiene is handled with husky and lint-staged, with the prepare script installing the hooks, and the repository carries .prettierrc, .prettierignore, .lintstagedrc.json and an .npmrc.

The release picture is worth noting, because it is the one place where the metadata is thin. The only visible release is v0.14.0 from 2024-12-09, while the last push to the repository was on 2026-09-02, and the README carries a weekly downloads badge for @libsql/client on npm. So the package on npm is being published and used, and the repository tags do not tell you how far that has moved.

Support is handled through a Discord server, security issues go to a dedicated email address, and there is a contributing guide plus an issue search filtered to the good first issue label.

## Conclusion

libsql-client-ts suits a TypeScript team that wants one database API across a local SQLite file, a hosted Turso database, or an offline first application that replicates to the server, and it suits anyone who needs vector search or encryption at rest without assembling those pieces separately. It does not suit a deployment that cannot run native modules, because the mainstream client depends on them, and for that case the README points you at the @tursodatabase/serverless package instead. Check three things before you choose: which of the two drivers you want, since the serverless one has no native dependencies but does not yet support concurrent writes, whether your use case needs the vector search and encryption features or would carry the dependency for nothing, and how you will run the test suite, which the repository drives through a shell script rather than a single command. The last push to the repository was on 2026-09-02 and the visible release is v0.14.0 from 2024-12-09.

## FAQ

### What is the difference between @libsql/client and @tursodatabase/serverless?

The README describes @tursodatabase/serverless as the lightest option with zero native dependencies, which will be the driver to later support concurrent writes, and says to use @libsql/client if you need a battle-tested driver today with ORM integration.

### How do I install the libSQL TypeScript client?

With npm install @libsql/client. The client is MIT licensed and published on npm, and the quickstart in the README creates a client with createClient, giving a local file URL, a remote sync URL, an auth token and a sync interval.

### What can a libSQL client do besides running SQL?

It works offline with Embedded Replicas, works with remote Turso databases, supports AI and vector search, and supports encryption at rest. The examples directory includes a vector example for similarity search and an encryption example for an encrypted SQLite database.

### How are parameters passed in the libSQL TypeScript client?

Statements are given an object with sql and args, and the values are bound to placeholders such as the question mark. The batch call takes a list of either raw SQL strings or those objects, and a second argument naming the mode, for example write.

### How is the libSQL client repository tested?

The root test script calls ./testing/test.sh rather than a JavaScript test runner, while the build, typecheck and format check scripts all run across the npm workspaces, which are packages/libsql-core, packages/libsql-client and packages/libsql-client-wasm.

## Sources

- [License: MIT](https://github.com/tursodatabase/libsql-client-ts/blob/main/LICENSE)
- [Project website](https://docs.turso.tech/sdk/ts/quickstart)
- [README](https://github.com/tursodatabase/libsql-client-ts/blob/main/README.md)
- [Releases](https://github.com/tursodatabase/libsql-client-ts/releases)
- [tursodatabase/libsql-client-ts on GitHub](https://github.com/tursodatabase/libsql-client-ts)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/tursodatabase-libsql-client-ts
