First App: UI & Custom Endpoints
This article is a short introduction to TrailBase and some of its features. We’ll bootstrap a database with coffee data, implement a custom TypeScript HTTP handler for finding the best matches using vector search, and deploy a simple production-ready web app all in ~100 lines of code.
This introductory tutorial is part of TrailBase’s main code repository, which can be downloaded to follow along by running:
Importing Data
Before building the app, let’s import some data. Keeping it simple,
we’ll use the sqlite3
CLI1 directly to import
examples/coffeesearch/arabica_data_cleaned.csv
with the following SQL
script:
Note that we didn’t initialize the vector embedding
. This is merely because
sqlite3
doesn’t have the necessary extensions built-in.
We’ll update the entries later on, adding the embedding as part of our initial
database migrations2.
From within the example/coffeesearch
directory, you can execute the script
above and import the coffee data by running:
After importing the data while still in the same directory, we can start the
trail
server:
Because trail
starts for the first time the migrations in
traildepot/migrations
will be applied, which are essentially:
initializing the previously skipped coffee.embedding
for all records.
Custom TypeScript Endpoint
Any time you start trail run
3, JavaScript and TypeScript files under
traildepot/scripts
will be executed.
We can use this to register custom HTTP API routes among other things.
Let’s have a quick look at examples/coffeesearch/traildepot/scripts/main.ts
,
which defines a /search
API route we’ll later use in our application to
find coffees most closely matching our desired coffee notes:
While trail run
is up, we can test the public /search
endpoint simply by
running:
That’s it, we’re done with the server side. This is enough to build a simple search UI. With little code and a few commands we’ve ingested CSV data and built a custom HTTP endpoint using vector search. If you’re not interested in a UI, the same approach setup could be used to identify relevant documents for AI applications.
A simple Web UI
After setting up our database, vector search and APIs, we should probably use them for good measure. For example, we could build a mobile app, have an LLM answer coffee prompts, or build a small web UI. Here we’ll do the latter. It’s quick and also lets us touch more generally on bundling and deploying web applications with TrailBase.
Note that this is not a web dev tutorial. The specifics of the UI aren’t the
focus. We chose React as a well-known option and kept the implementation to
less than 80 lines of code.
In case you want to build your own, we recommend
vite to quickly set up an SPA with your favorite JS
framework, e.g.: npm create vite@latest my-project -- --template react
.
Our provided reference implementation, renders 4 numeric input fields to search for coffee with a certain aroma, flavor, acidity and sweetness score:
We can start a dev-server with the UI from above and hot-reload running:
Deployment: Putting Everything Together
Whether you’ve followed along or skipped to here, we can now put everything
together.
Let’s start by compiling our JSX/TSX
web UI down to pure HTML, JS, and CSS
artifacts the browser can understand:
The artifacts are written to ./dist
and can be served alongside our database
as well as custom API by running:
You can now check out your fully self-contained app under http://localhost:4000/ or browse the coffee data and access logs in the admin dashboard. The admin credentials are logged to the terminal on first start.
All4 we need to serve our application in production is:
- the static
trail
binary, - the
traildepot
folder containing the data and endpoints, - the
dist
folder containing our web app.
At the end of the day it’s just a bunch of hermetic files without transitively
depending on a pyramid of shared libraries or requiring other services to be up
and running like a separate database server.
This makes it very easy to just copy the files over to your server or bundle
everything in a single container.
examples/coffeesearch/Dockerfile
is an example of how you can both build and
bundle using Docker. In fact,
will speed-run this entire tutorial by building and starting the app listening at http://localhost:4000/.
That’s it. We hope this was a fun little intro to some of TrailBase’s features. There’s more we haven’t touched on: CRUD APIs, auth, admin dash, file uploads, just to name a few. If you have any feedback, don’t hesitate and reach out on GitHub.
Footnotes
-
If you don’t have
sqlite3
already installed, you can install it usingbrew install sqlite
,apt-get install sqlite3
, or download pre-built binaries ↩ -
Migrations are versioned SQL scripts that will be executed by the database on first encounter to programmatically and consistently evolve your database schema and data along with your code. For example, when you add a new column you’ll likely want all your integration tests, development setups, and production deployments to add the column so your application logic has a consistent schema to target. ↩
-
Unless explicitly disabled. ↩
-
To serve HTTPS you’ll either need a reverse proxy in front to terminate TLS or if you don’t require end-to-end encryption (e.g. you’re not using auth or handling sensitive data) you can fall back to TLS termination via a CDN like cloudflare. ↩