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 ​

bash
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 ​

bash
npx teenybase deploy --local --yes
npx teenybase dev

deploy --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:

ts
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 DatabaseSettings

Every 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):

bash
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):

bash
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):

bash
curl http://localhost:8787/api/v1/table/posts/select

Deploy to your Cloudflare account ​

bash
npx wrangler login
npx teenybase deploy --remote --yes

wrangler 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:

bash
npx teenybase secrets --remote --upload

Set 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) ​

bash
npx teenybase deploy --temporary

This 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.

Next steps ​