Develop locally with the bool CLI
The bool CLI lets you build an app on your own machine, use a Bool project as
its backend (entities, records, end-user auth), and publish to Bool hosting —
all from the command line. Your local code reads and writes the project’s real
data through the same gateway a deployed Bool uses; there’s no separate local
database to keep in sync.
Before you start
Local development talks to a project through the Bool gateway, so the
project must be a gateway-backed project. bool create makes one for you, so
the quickest start is to let the CLI create the project and the app together
(below).
If instead you want to link an existing project and it was created before
gateway support, linking fails with:
This project runs on the v1 runtime (no gateway), so it can't be used as a
managed backend from a local app. Create a new project to get the gateway runtime.You have two ways forward: start a new project with bool create (or the Bool
editor) and develop against that, or have Bool make an upgraded copy of the
existing one and link that instead — see Upgrading an older app’s
backend.
You’ll also need a personal access token for the CLI to authenticate. Create
one at Settings → API tokens and either pass it with --token or export
it as BOOL_TOKEN (see Environment variables).
Install
Install once, globally, to get bool on your PATH:
npm install -g bool-sdkStable since
bool-sdk@0.3.0— no tag needed. Anextchannel still carries prereleases if you want to test something unreleased.
Prefer not to install globally? Add bool-sdk as a dev dependency and prefix
commands with npx (npx bool <command>). The rest of this page uses the
global bool form.
Quick start
Create a project and a working todo app in one step, then publish it:
export BOOL_TOKEN=<your-access-token> # from Settings → API tokens
bool create my-todos --deployThis creates a gateway-backed project, scaffolds a Vite + React todo app wired
to it through bool-sdk, links the two (writing bool.config.json, .env.bool,
and generated types), declares a public todos entity, and — with --deploy —
publishes it to a live URL. Drop --deploy to stop after scaffolding and run it
locally first:
cd my-todos
npm install
npm run dev # reads and writes your Bool project's real data
bool deploy # publish when readyThe project name is optional — a bare bool create picks a friendly name and
scaffolds into a matching folder.
Commands
bool create [name]
Scaffold a new Bool project and a matching todo app in one command, then link and (optionally) deploy it.
bool create my-todos # create project + scaffold ./my-todos
bool create my-todos --path apps/todos # scaffold into a specific folder
bool create my-todos --deploy # also publish it immediatelycreate checks that the new project is ready for local development before it
writes any files — if it isn’t, the command stops and scaffolds nothing.
bool link --project <id>
Connect a folder you already have to an existing Bool project.
bool link --project <id>Writes three files into the current directory:
bool.config.json— public connection config (commit this).env.bool— the project’s admin data key,BOOL_API_KEY(gitignored — keep it secret;linkadds it to.gitignorefor you)bool/types.d.ts— generated TypeScript types for your entities
Then wire up the client (see Using the client).
The admin data key is written only for the project owner; a collaborator’s link still works for types, entities, and deploy — they just supply their own key.
bool types
Regenerate bool/types.d.ts from the project’s current entity schemas. Run it
after changing your data model so your editor’s types match.
bool typesbool entities
Inspect and manage your data model.
bool entities # list the project's entities and their fields
bool entities pull # write the project's schemas into bool/entities/
bool entities push # declare local bool/entities/*.jsonc on the projectpush reads bool/entities/ by default; pass --dir <path> to point elsewhere.
Schema changes are additive migrations applied server-side — you add
entities and fields; existing data is preserved.
See Entity schema files for the file format.
bool deploy
Zip the app’s source and publish it. Bool builds in the cloud and serves it at a stable URL.
bool deploy
bool deploy --dir apps/todos # deploy from a subdirectory
bool deploy --token <access-token> # use an explicit tokenBuild output, dependencies, and machine-local files (node_modules, dist,
.git, bool.config.json, .env*) are excluded from the archive automatically.
There’s a size cap on the uploaded archive; if you exceed it the CLI reports the
exact limit — see Troubleshooting for how to slim a deploy.
Entity schema files
Entities live in bool/entities/ as .jsonc files (JSON with comments). Each
file is a JSON-Schema object with a top-level name, a properties map, and
Bool’s x-bool-access keyword. Bool manages id, created_at, and (on private
entities) owner_id — don’t declare those yourself.
A public entity is one shared table anyone can read and write (no sign-in):
{
"name": "todos",
"type": "object",
"properties": {
"title": { "type": "string", "description": "The task text" },
"completed": { "type": "boolean", "default": false }
},
"required": ["title"],
"x-bool-access": "public"
}A private entity gives each signed-in user their own rows. Set
"x-bool-access": "private" and Bool adds an owner_id column plus row-level
security — you never declare owner_id in properties:
{
"name": "tasks",
"type": "object",
"properties": {
"title": { "type": "string" },
"done": { "type": "boolean", "default": false }
},
"required": ["title"],
"x-bool-access": "private"
}For a private entity, owner_id defaults to the signed-in user, so app code
doesn’t set it. But the admin data key (BOOL_API_KEY) has no end-user
identity, so a create from a local script or backend must pass owner_id
explicitly:
// Signed-in end user — owner_id is filled in automatically:
await bool.entities.tasks.create({ title: "Buy milk" });
// Admin key (BOOL_API_KEY) — no identity, so set owner_id yourself:
await bool.entities.tasks.create({ title: "Buy milk", owner_id: userId });Using the client
After create or link, create the client from the generated config:
import { createBoolClient } from "bool-sdk";
import config from "./bool.config.json";
export const bool = createBoolClient({
supabaseUrl: config.supabaseUrl,
supabaseAnonKey: config.supabaseAnonKey,
schema: config.schema,
appOrigin: config.appOrigin,
slug: config.slug,
apiKey: process.env.BOOL_API_KEY, // from .env.bool; VITE_BOOL_API_KEY in Vite
});
const todos = await bool.entities.todos.list("-created_at");
await bool.entities.todos.create({ title: "Ship it" });Live data in React
The methods above are one-shot promises — right for scripts, event handlers, and
anything outside React. For a screen, use useQuery on the same handler
instead: it loads the rows, keeps them current as anyone else changes them, and
makes your own writes appear instantly (undoing them if the save fails).
import { bool } from "./bool"; // wherever you created the client
function Todos() {
const todos = bool.entities.todos.useQuery({ filter: { done: false }, sort: "-created_at" });
if (todos.loading) return <Spinner />;
if (todos.error) return <button onClick={() => todos.refetch()}>Couldn't load — retry</button>;
return (
<>
{todos.data.map((t) => (
<label key={t.id}>
<input type="checkbox" onChange={() => todos.update(t.id, { done: true })} />
{t.title}
</label>
))}
<button onClick={() => todos.create({ title: "New" })}>Add</button>
</>
);
}useQuery is a React hook, so the usual rules apply — call it at the top of a
component, unconditionally. It takes the same filter and sort you’d pass to
.filter(...), plus limit, and an inline options object is fine (no
useMemo needed). Handle all three states: loading, error, and the rows —
rendering only data turns a failed load into a convincing empty screen.
One setup step: the hook lives in the React entry point, so your app must import it once —
import "bool-sdk/react";next to where you create the client. Apps Bool generates already have this line. Without it,useQuerythrows an error telling you exactly this.
Environment variables
BOOL_TOKEN
Your personal access token, used to authenticate CLI calls to the Bool platform (create, link, types, entities, deploy).
export BOOL_TOKEN=<your-access-token>Get one from Settings → API tokens. You can also pass --token on any
command instead of exporting it.
BOOL_API_KEY
The project’s admin data key, written to .env.bool by link/create. Pass
it to createBoolClient as apiKey so local code can read and write the
project’s data. It has full access to every row, so it’s for your own
scripts and local development only — never ship it in client code. Deployed
apps don’t use it; their end users authenticate through Bool auth instead.
Troubleshooting
command not found: bool — the global install didn’t land on your PATH.
Reinstall with the -g flag (npm install -g bool-sdk); if
it’s still missing, check npm bin -g and add that directory to your shell
profile, or skip the global install and use npx bool <command>.
No access token — set BOOL_TOKEN (or pass --token). Create the token at
Settings → API tokens.
This project runs on the v1 runtime — the project you’re linking predates
gateway support and can’t be used as a local backend. Either create a new
project with bool create, or upgrade the existing
one and link the
upgraded copy.
Archive is over the size limit — the CLI prints the exact cap. Slim the deploy by removing unused dependencies, splitting code / using dynamic imports, moving large assets to a CDN, or trimming your schema.
Types out of date — run bool types to regenerate bool/types.d.ts from the
project’s current schemas.
Learn more
- Database — entities, records, and your data model
- Sign-in for your app’s users — add accounts and per-user data
- Publishing — how deploys go live and who can see them
- Connect your AI (MCP) — give coding agents the same powers