mirror of
https://github.com/trailbaseio/trailbase.git
synced 2026-01-01 07:09:56 -06:00
153 lines
5.4 KiB
Markdown
153 lines
5.4 KiB
Markdown
# TrailBase Tutorial
|
|
|
|
In this tutorial, we'll set up a database with an IMDB test dataset, spin up
|
|
TrailBase and write a small program to access the data.
|
|
|
|
In an effort to demonstrate TrailBase's loose coupling and the possibility of
|
|
simply trying out TrailBase with an existing SQLite-based data analysis
|
|
project, we will also offer a alternative path to bootstrapping the database
|
|
using the vanilla `sqlite3` CLI.
|
|
|
|
## Create the Schema
|
|
|
|
By simply starting TrailBase, the migrations in `traildepot/migrations` will be
|
|
applied, including `U1728810800__create_table_movies.sql`:
|
|
|
|
```sql
|
|
CREATE TABLE IF NOT EXISTS movies (
|
|
rank INTEGER PRIMARY KEY,
|
|
name TEXT NOT NULL,
|
|
year ANY NOT NULL,
|
|
watch_time INTEGER NOT NULL,
|
|
rating REAL NOT NULL,
|
|
metascore ANY,
|
|
gross ANY,
|
|
votes TEXT NOT NULL,
|
|
description TEXT NOT NULL
|
|
) STRICT;
|
|
```
|
|
|
|
Note that the only schema requirement for exposing an API is: `STRICT` typing
|
|
and an integer (or UUIDv7) primary key column.
|
|
|
|
The main benefit of relying on TrailBase to apply the above schema as migrations
|
|
over manually applying the schema yourself, is to:
|
|
* document your database's schema alongside your code and
|
|
* even more importantly, letting TrailBase bootstrap from scratch and
|
|
sync-up databases across your dev setup, your colleague's, every time
|
|
integration tests run, QA stages, and in production.
|
|
|
|
That said, TrailBase will happily work on existing datasets, in which
|
|
case it is your responsibility to provide a SQLite database file that
|
|
meets expectations expressed as configured TrailBase API endpoints.
|
|
|
|
Feel free to run:
|
|
|
|
```bash
|
|
$ mkdir traildepot/data
|
|
$ sqlite3 traildepot/data/main.db < traildepot/migrations/U1728810800__create_table_movies.sql
|
|
```
|
|
|
|
before starting TrailBase the first time, if you prefer bootstrapping the
|
|
database yourself.
|
|
|
|
## Importing the Data
|
|
|
|
After creating the schema above, either manually or starting TrailBase to apply
|
|
migrations, we're ready to import the IMDB test dataset.
|
|
We could now expose an API endpoint and write a small program to first read the
|
|
CSV file to then write movie database records... and we'll do that in a little
|
|
later.
|
|
For now, let's start by harnessing the fact that SQLite databases are simply a
|
|
local file and import the data using the `sqlite3` CLI side-stepping TrailBase:
|
|
|
|
```
|
|
$ sqlite3 traildepot/data/main.db
|
|
sqlite> .mode csv
|
|
sqlite> .import ./data/Top_1000_IMDb_movies_New_version.csv movies
|
|
```
|
|
|
|
There will be a warning for the first line of the CSV, which contains textual
|
|
table headers rather than data matching our schema. That's expected.
|
|
We can validate that we successfully imported 1000 movies by running:
|
|
|
|
```sql
|
|
sqlite> SELECT COUNT(*) FROM movies;
|
|
1000
|
|
```
|
|
|
|
## Accessing the Data
|
|
|
|
With TrailBase up and running (`trail run`), the easiest way to explore your
|
|
data is go to the admin dashboard under
|
|
[http://localhost:4000](http://localhost:4000)
|
|
and log in with the admin credentials provided to you in the terminal upon
|
|
first start (you can also use the `trail` CLI to reset the password `trail user
|
|
reset-password admin@localhost`).
|
|
|
|
In this tutorial we want to explore more programmatic access and using
|
|
TrailBase record APIs.
|
|
|
|
```textproto
|
|
record_apis: [
|
|
# ...
|
|
{
|
|
name: "movies"
|
|
table_name: "movies"
|
|
acl_world: [READ]
|
|
acl_authenticated: [CREATE, READ, UPDATE, DELETE]
|
|
}
|
|
]
|
|
```
|
|
|
|
By adding the above snippet to your configuration (which is already the case
|
|
for the checked-in configuration) you expose a world-readable API. We're using
|
|
the config here but you can also configure the API using the admin dashboard
|
|
via the
|
|
[tables view](http://localhost:4000/_/admin/tables?pageIndex=0&pageSize=20&table=movies)
|
|
and the "Record API" settings in the top right.
|
|
|
|
Let's try it out by querying the top-3 ranked movies with less than 120min
|
|
watch time:
|
|
|
|
```bash
|
|
curl -g 'localhost:4000/api/records/v1/movies?limit=3&order=rank&filter[watch_time][$lt]=120'
|
|
```
|
|
|
|
You can also use your browser. Either way, you should see some JSON output with
|
|
the respective movies.
|
|
|
|
## Type-safe APIs and Mutations
|
|
|
|
Finally, let's authenticate and use privileged APIs to first delete all movies
|
|
and then add them pack using type-safe APIs rather than `sqlite3`.
|
|
|
|
Let's first create the JSON Schema type definitions from the database schema we
|
|
added above. Note, that the type definition for creation, reading, and updating
|
|
are all different. Creating a new record requires values for all `NOT NULL`
|
|
columns w/o a default value, while reads guarantees values for all `NOT NULL`
|
|
columns, and updates only require values for columns that are being updated.
|
|
In this tutorial we'll "cheat" by using the same type definition for reading
|
|
existing and creating new records, since our schema doesn't define any default
|
|
values (except implicitly for the primary key), they're almost identical.
|
|
|
|
In preparation for deleting and re-adding the movies, let's run:
|
|
|
|
```bash
|
|
$ trail schema movies --mode insert
|
|
```
|
|
|
|
This will output a standard JSON schema type definition file. There's quite a few
|
|
code-generators you can use to generate bindings for your favorite language.
|
|
For this example we'll use *quicktype* to generate *TypeScript* definitions,
|
|
which also happens to support some other ~25 languages. You can install it, but
|
|
for the tutorial we'll stick with the [browser](https://app.quicktype.io/)
|
|
version and copy&paste the JSON schema from above.
|
|
|
|
With the generated types, we can use the TrailBase TypeScript client to write
|
|
the following program:
|
|
|
|
```ts
|
|
# scripts/src/fill.ts
|
|
```
|