betavoidbase is in public beta. It runs, the API is PocketBase's and is not moving, and the version is still 0.x for everything around that.Help us get it to 1.0

Start a stack app

One application: pages, typed endpoints, background jobs and a database, which builds into a voidbase instance and deploys as a single Worker serving the site, the API and the admin panel from one address.

It is a Void app with an adapter added. The project stays a plain Void app the whole way through: the adapter generates the voidbase instance from it rather than asking you to keep one.

From an empty directory

bun add void
bunx void init          # asks which framework and starter; React and a database-backed one here
bun add @voidbase-cloud/voidbase

Then add the adapter to the Vite config, which is the only wiring there is:

// vite.config.ts
import { defineConfig } from "vite";
import { voidPlugin } from "void";
import { voidReact } from "@void/react/plugin";
import { voidbaseAdapter } from "@voidbase-cloud/voidbase/adapter/plugin";

export default defineConfig({ plugins: [voidPlugin(), voidReact(), voidbaseAdapter()] });

And ignore the two things that should never be committed: the generated instance, and the local values of your configuration.

Git
.voidbase/
vb_secrets/secrets.json

What the project looks like

Your application:

  • pages/: the site, server-rendered
  • routes/: typed API endpoints
  • middleware/: what runs on every request
  • crons/: scheduled work
  • queues/: background jobs
  • db/: the app's own tables, in Drizzle
  • src/: library code the rest imports

What voidbase reads:

  • vb_hooks/: handlers that run around record writes
  • vb_migrations/: collections, for the schema the panel manages
  • vb_secrets/: configuration, for the server, the build and the browser

Written by the build:

  • .voidbase/: the instance this becomes, and git-ignored

Everything above the gap is Void's and means what Void means by it. The three directories below it are the ones the adapter adds, each named for the voidbase thing it is, each optional, and each with its own page:

Build and run it

bun run build                                  # or: bunx --bun vite build
bun .voidbase/main.ts --http 127.0.0.1:8090    # site at /, API at /api, panel at /_/

The build writes a complete voidbase project into .voidbase/: your pages as its static files, your routes and hooks compiled into its hook bundle, your collections as its migrations. It is git-ignored, because it is output, and it is an ordinary instance in every other respect.

Deploy it

cd .voidbase && voidbase deploy

Everything on the project page applies from here: the same token and the same deploy. To have a push do it instead, so the site and its backend ship together on every commit, Deploy on every push is two commands and one dashboard step.

Updating

voidbase update

Run it in the project, the same as anywhere else: the @voidbase-cloud/voidbase dependency is bumped and installed, and the caret or pin you had is kept. Void and the rest of the toolchain are separate dependencies with their own versions, and this leaves them alone.

A stack app is one Worker, so the backend and the site go live together. Build it first and look at it locally before you deploy, because a voidbase upgrade and your own application ship in the same artifact.

voidbase update --check changes nothing. It prints the version you are on and the newest one, and exits 1 when you are behind and 2 when it could not find out, which is what a pipeline reads. --to 0.9.0 goes to a particular version rather than the newest, and --dry-run prints the command it would run without running it.

You do not have to remember to check. Any voidbase command mentions a new release once a day, from an answer cached under ~/.voidbase. It never speaks in CI or when the output is not a terminal, and VOIDBASE_NO_UPDATE_CHECK=1 silences it everywhere. Every release is written up in the release notes.

Two things that are different

Two databases, on purpose. Void's db/ is your application's own tables, in Drizzle, typed end to end, for the data your code owns. Collections are for the data the admin panel and the SDK own, with API rules and a schema someone can change without a deploy. Most apps want both, and vb_migrations is where the second kind is written down.

Routes and hooks are not the same thing. A route in routes/ is an endpoint you call. A hook in vb_hooks runs because a record changed, whoever changed it, including someone clicking in the admin panel. Reach for the first when you are writing an API, the second when a rule has to hold no matter who is writing.

This site is built this way: the page you are reading, its API and the admin panel are one Worker.

Found something wrong on this page?

Fix it yourself. The link below opens this file in GitHub's editor and forks the repository for you if you need one, and your change becomes a pull request without leaving the browser.