Native Extensions Experimental
⚠️
Experimental — the native extension API may change before v1.0.
Native extensions let you write Rust code that your JavaScript can call directly. Use them for performance-critical work, C library bindings, or OS APIs not exposed by VeloxKit.
When to use native extensions
Use a native extension when you need to:
- Process large files without blocking the JS thread
- Bind to a C/C++ library (image processing, cryptography, etc.)
- Access OS APIs not yet covered by VeloxKit's built-in bindings
- Run CPU-intensive loops at native speed
For most apps, the built-in bindings cover everything needed.
Setup
You need Rust installed for native extensions (unlike the rest of VeloxKit):
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | shCreate an extension directory:
npx veloxkit-cli extension new my-extensionThis scaffolds:
extensions/my-extension/
├── Cargo.toml
└── src/
└── lib.rsWrite the extension
// extensions/my-extension/src/lib.rs
use velox_extension::prelude::*;
#[velox_command]
fn hash_file(path: String) -> Result<String, VeloxError> {
use std::io::Read;
let mut file = std::fs::File::open(&path)
.map_err(|e| VeloxError::io(e))?;
let mut hasher = sha2::Sha256::new();
let mut buf = [0u8; 8192];
loop {
let n = file.read(&mut buf)?;
if n == 0 { break; }
hasher.update(&buf[..n]);
}
Ok(format!("{:x}", hasher.finalize()))
}
// Async extension (runs on background thread, non-blocking)
#[velox_command(async)]
async fn compress_file(input: String, output: String) -> Result<u64, VeloxError> {
// ... compression logic
Ok(bytes_written)
}Call from JavaScript
import { velox } from 'veloxkit'
// Synchronous
const hash = velox.ext('my-extension', 'hash_file', '/path/to/file.txt')
// Async (non-blocking — runs on background thread)
const bytes = await velox.extAsync('my-extension', 'compress_file', {
input: '/path/to/large.csv',
output: '/path/to/large.csv.gz',
})Types
Define TypeScript types for your extension:
// extensions/my-extension/index.d.ts
import { velox } from 'veloxkit'
export function hashFile(path: string): string
export function compressFile(input: string, output: string): Promise<number>And create a wrapper module:
// extensions/my-extension/index.ts
import { velox } from 'veloxkit'
export const hashFile = (path: string) =>
velox.ext<string>('my-extension', 'hash_file', path)
export const compressFile = (input: string, output: string) =>
velox.extAsync<number>('my-extension', 'compress_file', { input, output })Build
VeloxKit builds extensions automatically when you run veloxkit build or veloxkit dev. The Rust compilation happens once and is cached.
# Explicitly build the extension only
npx veloxkit-cli extension build my-extensionCargo.toml
[package]
name = "my-extension"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib"]
[dependencies]
velox-extension = "0.3"
sha2 = "0.10"