• Blog
  • Docs
  • Pricing
  • We’re hiring!
Log inSign up
figleaf

figleaf

whenwasthelasttime

Public
Like
whenwasthelasttime
Home
Code
8
.claude
1
backend
2
frontend
5
public
4
.vtignore
README.md
VALTOWN_SQLITE_GUIDE.md
spec.md
Branches
1
Pull requests
Remixes
History
Environment variables
Val Town is a collaborative website to build and scale JavaScript apps.
Deploy APIs, crons, & store data – all from the browser, and deployed in milliseconds.
Sign up now
Code
/
VALTOWN_SQLITE_GUIDE.md
Code
/
VALTOWN_SQLITE_GUIDE.md
Search
…
VALTOWN_SQLITE_GUIDE.md

Val Town SQLite Syntax Guide

Important Differences from Standard SQLite

Val Town uses Turso (libSQL), which is mostly SQLite-compatible but has some syntax preferences:

1. Use Lowercase Keywords

-- ✅ CORRECT create table if not exists items (...) -- ❌ AVOID CREATE TABLE IF NOT EXISTS items (...)

2. Column Type Names

Use lowercase type names:

-- ✅ CORRECT id text primary key name text not null count integer default 0 -- ❌ AVOID id TEXT PRIMARY KEY name TEXT NOT NULL count INTEGER DEFAULT 0

3. No AUTOINCREMENT Needed

SQLite automatically increments INTEGER PRIMARY KEY:

-- ✅ CORRECT (auto-increments) id integer primary key -- ❌ UNNECESSARY id integer primary key autoincrement

4. Use text for UUIDs

Store UUIDs as text, generate with crypto.randomUUID():

-- ✅ CORRECT id text primary key -- In JavaScript: const id = crypto.randomUUID(); await sqlite.execute({ sql: 'insert into items (id, name) values (?, ?)', args: [id, 'Item name'] });

5. Foreign Keys Syntax

-- ✅ CORRECT - Simple foreign key create table if not exists items ( id text primary key, category_id text not null, foreign key (category_id) references categories(id) ) -- ✅ CORRECT - With cascade create table if not exists occurrences ( id text primary key, item_id text not null, foreign key (item_id) references items(id) on delete cascade )

6. Index Creation

-- ✅ CORRECT create index if not exists idx_items_category on items(category_id); -- ✅ CORRECT - Composite index create index if not exists idx_items_cat_arch on items(category_id, archived); -- ✅ CORRECT - Unique index create unique index if not exists idx_categories_name on categories(name);

7. Default Values

-- ✅ CORRECT archived integer default 0 created_at text not null -- For timestamps, use datetime in queries: await sqlite.execute({ sql: 'insert into items (created_at) values (?)', args: [new Date().toISOString()] });

8. Unique Constraints

-- ✅ CORRECT - Column constraint name text not null unique -- ✅ CORRECT - Table constraint create table categories ( id text primary key, name text not null, unique(name) )

Complete Schema Example

-- Categories create table if not exists categories ( id text primary key, name text not null unique, created_at text not null ); -- Items create table if not exists items ( id text primary key, category_id text not null, name text not null, description text, created_at text not null, archived integer default 0, foreign key (category_id) references categories(id) ); -- Occurrences create table if not exists occurrences ( id text primary key, item_id text not null, occurrence_date text not null, note text, created_at text not null, foreign key (item_id) references items(id) on delete cascade ); -- Indexes create index if not exists idx_items_category_id on items(category_id); create index if not exists idx_items_archived on items(archived); create index if not exists idx_occurrences_item_id on occurrences(item_id); create index if not exists idx_occurrences_date on occurrences(occurrence_date);

Val Town Usage Patterns

Initialize Database

import { sqlite } from "https://esm.town/v/std/sqlite"; // Create tables (idempotent - safe to run multiple times) await sqlite.execute(` create table if not exists categories ( id text primary key, name text not null unique, created_at text not null ) `);

Insert Data

// Method 1: Positional parameters (?) await sqlite.execute({ sql: 'insert into categories (id, name, created_at) values (?, ?, ?)', args: [crypto.randomUUID(), 'Health', new Date().toISOString()] }); // Method 2: Named parameters (:name) await sqlite.execute({ sql: 'insert into categories (id, name, created_at) values (:id, :name, :created_at)', args: { id: crypto.randomUUID(), name: 'Health', created_at: new Date().toISOString() } });

Query Data

// Simple query const result = await sqlite.execute('select * from categories'); console.log(result.columns); // ['id', 'name', 'created_at'] console.log(result.rows); // Array of arrays // With parameters const result = await sqlite.execute({ sql: 'select * from items where category_id = ?', args: [categoryId] }); // Convert rows to objects const items = result.rows.map(row => Object.fromEntries( row.map((value, index) => [result.columns[index], value]) ) );

Update Data

await sqlite.execute({ sql: 'update items set name = ?, description = ? where id = ?', args: [newName, newDescription, itemId] });

Delete Data

await sqlite.execute({ sql: 'delete from occurrences where id = ?', args: [occurrenceId] });

Batch Operations (Transactions)

await sqlite.batch([ 'create table if not exists items (...)', { sql: 'insert into items (id, name) values (?, ?)', args: [id1, name1] }, { sql: 'insert into items (id, name) values (?, ?)', args: [id2, name2] } ]);

Common Queries for This App

Get all items with latest occurrence

const result = await sqlite.execute(` select i.*, c.name as category_name, max(o.occurrence_date) as latest_occurrence, count(o.id) as total_occurrences from items i join categories c on c.id = i.category_id left join occurrences o on o.item_id = i.id where i.archived = 0 group by i.id order by latest_occurrence desc `);

Get item with all occurrences

const result = await sqlite.execute({ sql: ` select o.id, o.occurrence_date, o.note, o.created_at from occurrences o where o.item_id = ? order by o.occurrence_date desc `, args: [itemId] });

Search items

const searchTerm = '%' + query + '%'; const result = await sqlite.execute({ sql: ` select distinct i.* from items i left join occurrences o on o.item_id = i.id where i.archived = 0 and ( i.name like ? or i.description like ? or o.note like ? ) `, args: [searchTerm, searchTerm, searchTerm] });

Common Errors and Solutions

Error: "no such table"

Cause: Table hasn't been created yet Solution: Run your CREATE TABLE statements first

Error: "UNIQUE constraint failed"

Cause: Trying to insert duplicate value in unique column Solution: Check for existing record first, or use INSERT OR REPLACE

Error: "FOREIGN KEY constraint failed"

Cause: Referenced record doesn't exist Solution: Create parent record first (e.g., category before item)

Error: "near 'TEXT': syntax error"

Cause: Using uppercase keywords (Val Town prefers lowercase) Solution: Use lowercase: text not TEXT

Best Practices

  1. Always use if not exists in CREATE statements
  2. Always use parameterized queries - never string concatenation
  3. Use transactions (batch) for multiple related operations
  4. Create indexes on foreign keys and frequently queried columns
  5. Use ISO 8601 dates - new Date().toISOString()
  6. Generate UUIDs - crypto.randomUUID()
  7. Test your queries in isolation before integrating

Date Handling

// Store dates as ISO 8601 strings const now = new Date().toISOString(); // "2024-11-14T10:30:45.123Z" // For date-only (YYYY-MM-DD) const today = new Date().toISOString().split('T')[0]; // "2024-11-14" // Query by date await sqlite.execute({ sql: 'select * from occurrences where occurrence_date = ?', args: [today] }); // Date range query await sqlite.execute({ sql: 'select * from occurrences where occurrence_date between ? and ?', args: [startDate, endDate] });

Performance Tips

  1. Use indexes on columns you filter/sort by
  2. Limit result sets for large tables
  3. Use batch operations instead of many individual queries
  4. Avoid SELECT * - select only needed columns
  5. Use prepared statements (automatic with Val Town sqlite)

Debugging

// Log query results const result = await sqlite.execute('select * from items'); console.log('Columns:', result.columns); console.log('Rows:', result.rows); console.log('Rows affected:', result.rowsAffected); // Check table schema const schema = await sqlite.execute(` select sql from sqlite_master where type='table' and name='items' `); console.log(schema.rows[0][0]);
FeaturesVersion controlCode intelligenceCLI
Use cases
TeamsAI agentsSlackGTM
DocsShowcaseTemplatesNewestTrendingAPI examplesNPM packages
PricingNewsletterBlogAboutCareers
We’re hiring!
Brandhi@val.townStatus
X (Twitter)
Discord community
GitHub discussions
YouTube channel
Bluesky
Open Source Pledge
Terms of usePrivacy policyAbuse contact
© 2025 Val Town, Inc.