---
title: "Quickstart"
description: "Create a teenybase project, run it locally with no Cloudflare account, add a table, call the API with curl, then deploy it to your own Cloudflare account."
url: https://teenybase.com/docs/getting-started/quickstart
last_updated: 2026-10-26
---

> Documentation index: https://teenybase.com/llms.txt lists every teenybase docs page with a link to its Markdown version. All docs in one file: https://teenybase.com/llms-full.txt

# 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](https://teenybase.com/docs/getting-started/quickstart-agent) 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](https://teenybase.com/docs/reference/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

- [Connect Your Frontend](https://teenybase.com/docs/guides/frontend)
- [Choose Your Auth Flow](https://teenybase.com/docs/reference/auth-flows)
- [Deployment Checklist](https://teenybase.com/docs/deployment-checklist)
- [CLI Reference](https://teenybase.com/docs/reference/cli)
