Config Reference

Your backend is defined in teenybase.ts. This file exports a DatabaseSettings object.

Top-level settings

ts
export default {
  appUrl: 'http://localhost:8787',       // Base URL (used in OAuth redirects, emails)
  appName: 'My App',                     // Used in email templates
  jwtSecret: '$JWT_SECRET',              // Global JWT signing secret
  jwtIssuer: '$db',                      // JWT issuer claim (optional)
  tables: [],                            // Table definitions
  authCookie: { name: 'teeny_auth' },    // Optional, needed for OAuth redirect flows and SSR cookie sessions
  authProviders: [],                     // OAuth providers (optional)
  // email: { ... },                     // Email sending config (optional)
  actions: [],                           // Server-side actions (optional)
} satisfies DatabaseSettings

Values prefixed with $ (like $JWT_SECRET) are environment variables. They're read from .dev.vars when running locally and from .prod.vars in production. This keeps secrets out of your config file.

Tables

ts
{
  name: 'posts',
  autoSetUid: true,          // Auto-generate unique ID on insert
  fields: [...],
  triggers: [...],
  extensions: [...],
  r2Base: 'posts',           // R2 path prefix for file fields (optional)
  autoDeleteR2Files: true,   // Delete files when record deleted (optional)
}

Fields

ts
{ name: 'title', type: 'text', sqlType: 'text' }
{ name: 'count', type: 'number', sqlType: 'integer', default: '0' }
{ name: 'published', type: 'bool', sqlType: 'boolean', default: '0' }
{ name: 'data', type: 'json', sqlType: 'text' }
{ name: 'avatar', type: 'file', sqlType: 'text' }

Use scaffolds for common patterns:

ts
import { baseFields, authFields, createdTrigger, updatedTrigger } from 'teenybase/scaffolds/fields'

// baseFields: id, created, updated
// authFields: username, email, email_verified, password, password_salt, name, avatar, role, meta

Auth extension

Adds sign-up, login, password reset, email verification, and OAuth endpoints to a table.

ts
{
  name: 'auth',
  jwtSecret: '$JWT_SECRET_USERS',   // Table-specific secret (combined with global)
  jwtTokenDuration: 3600,           // Token TTL in seconds (default: 3600)
  maxTokenRefresh: 5,               // Required. Set 0 for unlimited refreshes
  passwordConfirmSuffix: 'Confirm', // Optional. Requires passwordConfirm on sign-up/reset
} as TableAuthExtensionData

Rules extension

Row-level security. Each rule is an expression evaluated on every request to decide whether the operation is allowed. The expression can reference auth.uid (the signed-in user's ID), auth.verified (email verified), auth.role (role), and any column on the current row.

ts
{
  name: 'rules',
  listRule: 'published == true',           // Who can list records
  viewRule: 'published == true',           // Who can view a single record
  createRule: 'auth.uid != null',          // Who can insert
  updateRule: 'auth.uid == author',        // Who can update
  deleteRule: 'auth.uid == author',        // Who can delete
} as TableRulesExtensionData

Special values:

  • 'true' — allow everyone, including unauthenticated users
  • 'false' or omitted — block everyone (admins still bypass rules)
  • auth.uid — the signed-in user's record ID (null if not signed in)
  • auth.verified — true if the user's email is verified
  • auth.role — the user's role/audience claim from their JWT

Auth providers

ts
authProviders: [
  {
    name: 'google',                          // Preset: google, github, discord, linkedin
    clientId: '$GOOGLE_CLIENT_ID',
    clientSecret: '$GOOGLE_CLIENT_SECRET',   // Omit for One Tap only
    redirectUrl: 'https://myapp.com/dashboard',
  },
]

For browser-based OAuth redirect flows, also configure authCookie:

ts
authCookie: {
  name: 'teeny_auth',
  httpOnly: true,
  secure: true,
  sameSite: 'Lax',
  path: '/',
}

Redirect flows (/auth/oauth/...) and google-login set this cookie automatically when configured. JSON auth endpoints like login-password and sign-up still return tokens in the response body — your frontend stores them as bearer tokens, or your SSR layer sets the cookie itself using the returned token.

Email

ts
email: {
  from: 'My App <noreply@myapp.com>',
  variables: {
    company_name: 'My App',
    company_url: 'https://myapp.com',
    company_address: '123 Main St, San Francisco, CA',
    company_copyright: 'Copyright 2026 My App',
    support_email: 'support@myapp.com',
  },
  resend: {
    RESEND_API_KEY: '$RESEND_API_KEY',
  },
}

Use mailgun instead of resend if you prefer Mailgun. Email verification and password reset only work when this section is configured.

Practical notes:

  • the from address should belong to a sender/domain your email provider accepts
  • local testing is easiest with mock: true
  • the request-password-reset and confirm-password-reset routes are only mounted when email is configured

For a full setup and test flow, see Email Verification & Password Reset.

Actions

Server-side logic callable via POST /api/v1/action/{name}.

ts
actions: [
  {
    name: 'increment-views',
    params: { post_id: 'string' },
    sql: sql`UPDATE posts SET views = views + 1 WHERE id = ${sqlValue(':post_id')}`,
  },
]