# Building Custom Extensions for Turso with ExtensionApi: A Complete Guide

> Easily build custom Turso extensions using Rust and the ExtensionApi. Create scalar functions, aggregates, virtual tables, and VFS hooks without unsafe C code.

- Repository: [Turso Database/turso](https://github.com/tursodatabase/turso)
- Tags: how-to-guide
- Published: 2026-06-22

---

**Turso exposes a Rust-native ExtensionApi that lets you build dynamic SQLite extensions as shared libraries, registering scalar functions, aggregates, virtual tables, and VFS hooks without writing unsafe C glue code.**

Turso is a lightweight, SQLite-compatible database engine written in Rust. Building custom extensions for Turso with ExtensionApi involves creating dynamic libraries that export a standardized entry point, allowing the runtime to inject custom functions and storage backends through a safe Rust abstraction layer.

## Understanding the ExtensionApi Architecture

### The ExtensionApi Struct

In [`extensions/core/src/lib.rs`](https://github.com/tursodatabase/turso/blob/main/extensions/core/src/lib.rs) (lines 34-43), the `ExtensionApi` struct serves as the bridge between Turso's runtime and your extension. It holds a raw context pointer and a set of registration callbacks for scalar functions, aggregates, virtual-table modules, and optional VFS interfaces.

### Entry Point and Registration

Every extension must export an `ExtensionEntryPoint` symbol—specifically `register_extension`—that Turso calls during loading. This C-compatible function receives a pointer to the `ExtensionApi` and returns a `ResultCode`. Upon invocation, your extension uses the callbacks provided in the `ExtensionApi` to register its capabilities with the database engine.

## Extension Lifecycle and Runtime Mechanics

The lifecycle follows four distinct phases:

1. **Dynamic Loading**: Turso's `load_extension` routine opens the shared object and locates the exported `register_extension` symbol.
2. **Registration**: The entry point invokes registration callbacks from the `ExtensionApi` to publish scalar functions, aggregates, or virtual-table modules.
3. **Execution**: Once registered, functions behave like native SQLite primitives—callable via `SELECT my_func(arg)`, usable as aggregates, or exposing virtual tables via `CREATE VIRTUAL TABLE ... USING my_vtab`.
4. **Cleanup**: When the database connection closes, the runtime drops the loaded library, and the framework cleans up any Rust state attached through the `State` type in `ScalarFunc`.

## Creating Your First Turso Extension

### Project Setup

Create a new Cargo library and configure it to produce a C-compatible dynamic library:

```toml

# Cargo.toml

[package]
name = "my_turso_ext"
version = "0.1.0"
edition = "2021"

[dependencies]
turso_ext = { path = "../turso/extensions/core" }

[lib]
crate-type = ["cdylib"]

```

### Implementing a Scalar Function

The `ScalarFunc` trait in [`extensions/core/src/functions.rs`](https://github.com/tursodatabase/turso/blob/main/extensions/core/src/functions.rs) (lines 64-80) defines the interface for Rust-native functions. Use the `#[scalar]` attribute from [`macros/src/lib.rs`](https://github.com/tursodatabase/turso/blob/main/macros/src/lib.rs) to automatically generate C wrappers:

```rust
// src/lib.rs
use turso_ext::{register_extension, scalar, Value, ResultCode};

#[scalar]
pub fn str_len(arg: Value) -> Value {
    let txt = arg.as_text();
    Value::from_int64(txt.len() as i64)
}

register_extension! {
    scalars: { str_len }
}

```

### Loading and Testing

Compile the extension, then load it into a Turso session:

```sql
SELECT load_extension('/path/to/libmy_turso_ext.so');
SELECT str_len('hello world');  -- Returns 11

```

## Implementing Advanced Features

### Virtual Table Modules

Turso supports custom virtual-table implementations through the `register_vtab_module` callback in `ExtensionApi`. These modules allow you to expose external data sources as queryable SQL tables, as implemented in [`core/vtab.rs`](https://github.com/tursodatabase/turso/blob/main/core/vtab.rs).

### Custom VFS Hooks

When the `vfs` feature is enabled, the `ExtensionApi` exposes `vfs_interface` for registering custom virtual file systems. Define a struct implementing `VfsExtension` and use the `#[VfsDerive]` macro from [`extensions/core/src/vfs_modules.rs`](https://github.com/tursodatabase/turso/blob/main/extensions/core/src/vfs_modules.rs):

```rust
use turso_ext::{register_extension, VfsDerive, VfsExtension, VfsFile, ResultCode};

#[derive(Default)]
pub struct MyVfs;

#[VfsDerive]
impl VfsExtension for MyVfs {
    const NAME: &'static str = "myvfs";
    
    fn open_file(&self, path: &str, flags: i32, _direct: bool) 
        -> Result<VfsFile, ResultCode> {
        // Implementation logic here
        unimplemented!()
    }
}

register_extension! {
    vfs: { MyVfs }
}

```

## Summary

- **ExtensionApi** in [`extensions/core/src/lib.rs`](https://github.com/tursodatabase/turso/blob/main/extensions/core/src/lib.rs) provides the core registration callbacks for building custom extensions for Turso.
- Extensions export a `register_extension` entry point that receives the API struct and registers functions through type-safe callbacks.
- The `#[scalar]` and `#[aggregate]` attributes in [`macros/src/lib.rs`](https://github.com/tursodatabase/turso/blob/main/macros/src/lib.rs) eliminate manual C wrapper code, while `register_extension!` generates the required entry point.
- Compile extensions as `cdylib` crate types and load them at runtime using `SELECT load_extension('path')`.
- Optional VFS support enables custom storage backends via the `VfsExtension` trait in [`extensions/core/src/vfs_modules.rs`](https://github.com/tursodatabase/turso/blob/main/extensions/core/src/vfs_modules.rs).

## Frequently Asked Questions

### What is the ExtensionApi in Turso?

The `ExtensionApi` is a Rust struct defined in [`extensions/core/src/lib.rs`](https://github.com/tursodatabase/turso/blob/main/extensions/core/src/lib.rs) that exposes registration callbacks for scalar functions, aggregates, virtual tables, and VFS hooks. It serves as the bridge between Turso's SQLite-compatible runtime and custom extension libraries written in Rust.

### How do I load a custom extension into Turso?

Compile your Rust code as a `cdylib` (shared library), then use the SQL command `SELECT load_extension('/path/to/library.so');` in your Turso session. Turso locates the `register_extension` symbol, calls it with an `ExtensionApi` pointer, and registers your extension's functions automatically.

### Can I write Turso extensions without using unsafe Rust?

Yes. The `turso_ext` crate provides safe abstractions through the `ScalarFunc` and `AggFunc` traits in [`extensions/core/src/functions.rs`](https://github.com/tursodatabase/turso/blob/main/extensions/core/src/functions.rs), and the `#[scalar]` attribute macro in [`macros/src/lib.rs`](https://github.com/tursodatabase/turso/blob/main/macros/src/lib.rs) generates the necessary `unsafe extern "C"` wrappers automatically. You only write idiomatic Rust code.

### What types of extensions can I build with the ExtensionApi?

You can build scalar functions, aggregate functions, virtual-table modules, and custom VFS implementations. The `ExtensionApi` exposes specific registration callbacks for each type: `register_scalar_function`, `register_aggregate_function`, `register_vtab_module`, and `vfs_interface` (when the `vfs` feature is enabled).