Quickstart
Requires Node.js 22 or newer (new projects install a wrangler version that needs it). Early (v0.1): APIs may change before 1.0.
Using a coding agent?
See the Agent Quickstart for a single page you can paste into your agent's chat.
Run the CLI
Run every command as npx teenybase <command>. If you install the CLI globally with npm i -g teenybase, you can type teeny <command> instead.
Don't use npx teeny: it downloads an unrelated npm package named teeny.
Create a project
npx teenybase create my-app --yes
cd my-app--yes uses the defaults: the with-auth template (a users table with sign-up, login, JWT auth and row-level security). Pass -t blank for an empty project. The command scaffolds teenybase.ts, a worker entry point, dev secrets (.dev.vars) and the Cloudflare config, then runs npm install.
Run it locally
npx teenybase deploy --local --yes
npx teenybase devdeploy --local generates migrations from teenybase.ts and applies them to a local SQLite database. dev starts the API server at http://localhost:8787:
http://localhost:8787/api/v1/health(health check)http://localhost:8787/api/v1/doc/ui(Swagger UI)http://localhost:8787/api/v1/pocket/(admin panel, a fork of PocketBase's admin UI)
When you change teenybase.ts, run npx teenybase deploy --local --yes again. Leave dev running: it reloads with the new config by itself.
Add a table
The scaffolded teenybase.ts holds your whole backend. To add a posts table, add it to the tables array:
import { DatabaseSettings, TableRulesExtensionData, sqlValue } from 'teenybase'
import { baseFields, createdTrigger, updatedTrigger } from 'teenybase/scaffolds/fields'
export default {
appUrl: 'http://localhost:8787',
jwtSecret: '$JWT_SECRET',
tables: [
{
name: 'users',
// ... (scaffolded by create)
},
{
name: 'posts', // add this table
autoSetUid: true,
fields: [
...baseFields,
{ name: 'author_id', type: 'relation', sqlType: 'text', notNull: true,
foreignKey: { table: 'users', column: 'id' } },
{ name: 'title', type: 'text', sqlType: 'text', notNull: true },
{ name: 'body', type: 'text', sqlType: 'text' },
{ name: 'published', type: 'bool', sqlType: 'boolean', default: sqlValue(false) },
],
triggers: [createdTrigger, updatedTrigger],
extensions: [
{
name: 'rules',
listRule: 'published == true | auth.uid == author_id',
viewRule: 'published == true | auth.uid == author_id',
createRule: 'auth.uid != null & author_id == auth.uid',
updateRule: 'auth.uid == author_id',
deleteRule: 'auth.uid == author_id',
} as TableRulesExtensionData,
],
},
],
} satisfies DatabaseSettingsEvery table needs a rules extension or public: true; deploy refuses to run otherwise. Apply the change with npx teenybase deploy --local --yes. See the Config Reference for all options.
Test it
Create a user (passwordConfirm is required by the with-auth template):
curl -X POST http://localhost:8787/api/v1/table/users/auth/sign-up \
-H 'Content-Type: application/json' \
-d '{"username":"alice","email":"alice@test.com","password":"secret123","passwordConfirm":"secret123","name":"Alice"}'The response includes a token (JWT) and a record with the user's id.
Create a post (replace YOUR_TOKEN and YOUR_USER_ID with values from the sign-up response):
curl -X POST http://localhost:8787/api/v1/table/posts/insert \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer YOUR_TOKEN' \
-d '{"values":{"title":"Hello World","body":"My first post","author_id":"YOUR_USER_ID","published":true},"returning":"*"}'List published posts (no auth needed):
curl http://localhost:8787/api/v1/table/posts/selectDeploy to your Cloudflare account
npx wrangler login
npx teenybase deploy --remote --yeswrangler login opens a browser once to authenticate with Cloudflare (the Workers free plan is enough to start). The first remote deploy creates the D1 database, generates production secrets into .prod.vars (gitignored, keep it private), deploys the worker and applies the migrations. It prints the worker URL; use it in place of http://localhost:8787 in the curl commands above.
Later deploys run the same command and only apply new migrations. After you edit .prod.vars, upload the secrets with:
npx teenybase secrets --remote --uploadSet appUrl in teenybase.ts to your frontend URL (or the worker URL if you have no frontend yet); it is used for OAuth redirects and email links.
Try it without a Cloudflare account (new)
npx teenybase deploy --temporaryThis deploys to a temporary Cloudflare account, no login needed, and prints the live URL and a claim URL. Open the claim URL and sign in to (or create) a Cloudflare account within 60 minutes to keep the deployment; otherwise it is deleted with its data. Treat the claim URL like a password.