Skip to content

Extending

This article explores different ways to integrate your App with TrailBase, extend it and your custom logic.

The question of where code should run weighs heavily on the web: push everything to the server, more to the client or even the edge? Answering this question is s a lot simpler for rich client-side applications such as mobile, desktop, and progressive web apps or SPAs where the inclination is to run on the users device providing privacy friendly, snappy interactivity and offline capabilities. There are perfectly good reasons to not run everything in an untrusted, battery limited, SEO unfriendly client-side sandbox but the overall need for server-side execution is greatly reduced. It’s rich client-side apps where application servers like TrailBase can shine providing common server-side functionality and strategic extension points.

Bring your own Backend

The most flexible and likewise de-coupled way of running your own code is to deploy a separate service alongside TrailBase. This gives you full control over your destiny: runtime, scaling, deployment, etc.

TrailBase is designed with the explicit goal of running along a sea of other services. Its stateless tokens using asymmetric crypto make it easy for other resource servers to hermetically authenticate your users. TrailBase’s APIs can be accessed transitively, simply by forwarding user tokens 1. Alternatively, you can fall back to raw SQLite for reads, writes and even schema alterations2.

Custom APIs in TrailBase

TrailBase provides a couple of ways to embed custom logic and provide custom APIs endpoints:

  1. Rust HTTP handlers using Axum,
  2. JS/TS handlers APIs,
  3. Stored database procedures,
  4. SQLite extensions and modules (virtual tables).

Using ES6 JavaScript & TypeScript

You can write custom HTTP endpoints using both full ES6 JavaScript and/or TypeScript. TrailBase will transpile your code on the fly and execute it on a speedy V8-engine, the same engine found across Chrome, node.js and deno. More information can be found in the API docs.

Using Rust

The Rust APIs aren’t yet stable and fairly undocumented. That said, similar to using PocketBase as a Go framework, you can build your own TrailBase binary and register custom Axum handlers written in rust with the main application router, see /examples/custom-binary.

Stored Procedures

Unlike Postgres or MySQL, SQLite does not support stored procedures out of the box. However, TrailBase has integrated sqlean’s user-defined functions to fill the gap. You can easily adopt SQLean in your own backends avoiding lock-in.

SQLite Extensions and Modules a.k.a. Virtual Tables

Likely the most bespoke approach is to expose your functionality as a custom SQLite extension or module similar to how TrailBase extends SQLite itself.

This approach can be somewhat limiting in terms of dependencies you have access to and things you can do especially for extensions. Modules are quite a bit more flexible but also involved. Take a look at SQLite’s list and osquery to get a sense of what’s possible.

Besides their limitations, major advantages of using extensions or modules are:

  • you have extremely low-overhead access to your data,
  • extensions and modules can also be used by services accessing the underlying SQLite databases.

Footnotes

  1. We would like to add service accounts in the future to authorize privileged services independent from user-provided tokens or using fake user-accounts for services.

  2. SQLite is running in WAL mode, which allows for parallel reads and concurrent writes. That said, when possible you should probably use the APIs since falling back to raw database access is a priviledge practically reserved to processes with access to a shared file-system.