Pilotbase Generated API dialog with the customers and products tables ticked

A REST API for your tables, without writing a backend

You have a database and a small app (a mobile app, a form, a script on another server) that needs to read and write a few tables. Usually that means writing and hosting a backend just to expose them. Pilotbase can do it for you: right-click a database, choose Enable API, tick the tables your app may use, and it serves list, read, create, update and delete routes for them, protected by a token.

We ran the whole flow against a test PostgreSQL database with three tables (customers, products, orders). Every screenshot and response below comes from that run.

Prefer to watch first? Here is the 33-second walkthrough video (vertical, no sound).

Where it works

The generated API is served by the Pilotbase backend itself, so it needs to run where your app can reach it: the Docker version on a server. It supports PostgreSQL, MySQL, MariaDB, SQL Server and MongoDB.

It is not available in the desktop app. The desktop backend only listens on 127.0.0.1, on a port that changes every launch, and it rejects any request without a per-launch token. In our test, an outside call to the desktop backend got 401 missing local token, so a phone or another server could never use it. The desktop app now hides Enable API, and the backend refuses to switch it on there. SQLite and DuckDB files are not supported yet either, because their database is a file path rather than a name.

Step 1: Enable the API on a database

In the connection tree, right-click the database (not the connection) and choose Enable API.

Pilotbase database context menu with Enable API between Plan Migration and Export as SQL
Right-click the database: Enable API sits next to Run Backup, Plan Migration and Export as SQL.

The dialog says what will happen. Click Enable.

Generated API dialog showing Disabled and an Enable button
Before you enable it: Pilotbase lists the four columns it will add to every table.

Enabling does two things to that database:

1. It adds four system columns to every table: guid (the row's ID in the API), created_at, last_updated and is_deleted. Rows that already exist get a guid and timestamps straight away, so your current data is reachable through the API too.
2. It creates an apitokens table that records each token, when it expires and whether it was revoked.

Because it changes your tables, enable it on a database you control and take a backup first (Run Backup is in the same menu).

Step 2: Choose which tables are reachable

Enabling the database doesn't expose anything yet. The dialog shows the base URL and a list of tables, and only the ones you tick can be called. We ticked customers and products and left orders off.

Generated API dialog enabled, with base URL and customers and products ticked, orders unticked
Enabled: the base URL to copy, and the table list. Only ticked tables answer.

The base URL has the form https://<your-pilotbase>/api/v1/papi/<connection-id>/<database>. Copy it with the button next to it.

Step 3: Get a token

Your app asks for a token with one POST to /apitokens, then sends it as a Bearer token on every other call. Tokens last 30 days and are stored in the apitokens table, so you can revoke one by setting its revoked column.

bash
export BASE="https://pilotbase.example.com/api/v1/papi/<connection-id>/shop"

curl -X POST "$BASE/apitokens"
# {"token": "eyJhbGciOiJIUzI1NiIs..."}

export TOKEN="eyJhbGciOiJIUzI1NiIs..."

Step 4: Read, create, update and delete rows

Each ticked table gets five routes:

text
GET    $BASE/{table}?limit=100&offset=0   list rows (not deleted)
GET    $BASE/{table}/{guid}               one row
POST   $BASE/{table}                      create a row (JSON body)
PUT    $BASE/{table}/{guid}               update fields (JSON body)
DELETE $BASE/{table}/{guid}               soft delete

Listing the first two customers. The rows that were there before we enabled the API already have a guid:

Terminal: POST apitokens returns a token, GET customers returns two rows with guid and timestamps
Mint a token, then list rows. Existing rows came back with a guid and timestamps.

Creating a customer returns the new row with its guid. Use that guid to update or read it:

Terminal: POST creates Esme Lund in Oslo, PUT changes the city to Bergen, GET returns the updated row
Create, update, read back. last_updated moves on every change.
bash
curl -X POST "$BASE/customers" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name":"Esme Lund","email":"esme@example.com","city":"Oslo"}'

curl -X PUT "$BASE/customers/<guid>" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"city":"Bergen"}'

Deletes are soft

DELETE never removes the row. It sets is_deleted to true, and from then on the API treats the row as gone: it drops out of lists, and reading, updating or deleting it again returns 404. The data is still in your table if you need it back.

Pilotbase query editor showing customers with guid, last_updated and is_deleted, the deleted row flagged true
The same table in Pilotbase after the delete: Esme Lund is still there, with is_deleted = true.

What happens without a token, or on a table you didn't tick

Terminal: DELETE returns Deleted (soft), GET after delete returns 404, no token returns 401, orders returns 404 not enabled
Delete, then 404. No token: 401. The unticked orders table: 404.

Know the limits before you ship it

The generated API is a thin, fast way to get data in and out. It is not a full backend, so plan for these:

- No validation. It writes what you send. Foreign keys and data checks are not applied by the API, so validate on the client or in the database (constraints, triggers).
- Anyone who can reach the URL can get a token. Token requests are rate-limited, but there are no user accounts or per-row permissions. Put it behind your own network rules or a gateway if the data is sensitive.
- Simple listing. Lists support limit and offset only, ordered by created_at. There is no filtering or sorting by other columns yet.
- Rows written outside the API get a guid when you next enable the database or a table. Until then they are listed but can't be read or changed by guid.

To switch it off, open the same dialog and click Disable. The extra columns and the apitokens table stay, so you can turn it back on later without losing anything.

Try it

Run Pilotbase with Docker on a server your app can reach, connect your database, and enable the API on a copy first. The Docker quick start takes a few minutes.

Pilotbase is free and open source (MIT). Run it with Docker and give your tables a REST API.

Get Pilotbase