Building Custom Extensions for Turso with ExtensionApi: A Complete Guide

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 (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:


# 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 (lines 64-80) defines the interface for Rust-native functions. Use the #[scalar] attribute from macros/src/lib.rs to automatically generate C wrappers:

// 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:

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.

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:

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 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 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.

Frequently Asked Questions

What is the ExtensionApi in Turso?

The ExtensionApi is a Rust struct defined in 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, and the #[scalar] attribute macro in 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).

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →