JavaScript SDK - Data#

The data builder mirrors PostgREST-style DX and is fully typed.

Typed rows#

type Message = {
  message_id: string;
  user_id?: string | null;
  message: string;
  full_name?: string | null;
  created_at: string; // ISO string
};

const rows = await db.from<Message>('messages').select('id as message_id, message').limit(10);
rows.data![0].message_id; // typed string
TypeScript

from<Row>() types execute, get, single, insert, update and awaited chains (including joins).

Read#

// Select all
await db.from('profiles').select();

// Select columns
await db.from('profiles').select('id, nationality');

// Single row by primary key
await db.from('profiles').get(recordId);

// First matching row
await db.from('profiles').eq('nationality', 'Kenya').single();

// Row count only (no rows transferred)
const { data: { count } } = await db.from('profiles').head();
TypeScript

Filters#

MethodSQL
.eq(col, v)col = v
.neq(col, v)col != v
.gt(col, v) / .gte(col, v)col > v / col >= v
.lt(col, v) / .lte(col, v)col < v / col <= v
.like(col, '%x%')LIKE
.ilike(col, '%x%')ILIKE (case-insensitive)
.isNull(col) / .isNotNull(col)IS NULL / IS NOT NULL
.in(col, ['a','b'])IN (...)

Filters chain with AND.

Ordering and pagination#

await db.from('profiles').order('created_at', { ascending: false });
await db.from('profiles').limit(50).offset(100);
TypeScript

For large tables use keyset pagination (O(1) per page vs O(n) for offset):

let page = await db.from('logs').select('id').limit(100);
while (page.data!.length) {
  const lastId = String(page.data![page.data!.length - 1].id);
  // process page...
  page = await db.from('logs').select('id').limit(100).cursor(lastId);
}
TypeScript

Joins#

Column-picker joins - no SQL needed:

const rows = await db
  .from('messages')
  .select('id as message_id, message, users.full_name as full_name')
  .join({ type: 'LEFT JOIN', table: 'users', from_column: 'user_id', to_column: 'id' })
  .limit(20);
// rows include users.full_name (aliased as full_name)
TypeScript
  • .join({ type, table, from_column, to_column }) builds the ON clause
  • .join({ type, table, on: 'base.id = join.user_id' }) raw clause (advanced)
  • Chain multiple joins; the users table joins are auto-scoped to the project
  • Joined columns are keyed as {table}.{column} (or your alias)

Write#

// Insert single or many (server fills id/created_at)
const one = await db.from<Message>('messages').insert({ message: 'hi' });
const many = await db.from<Message>('messages').insert([{ message: 'a' }, { message: 'b' }]);

// Update (requires .eq('id', ...))
await db.from('profiles').update({ nationality: 'Kenya' }).eq('id', recordId);

// Delete (requires .eq('id', ...))
await db.from('profiles').delete().eq('id', recordId);
TypeScript

Raw request#

For endpoints without a dedicated method:

await db.request('/projects/.../custom', { method: 'POST', body: JSON.stringify({...}) });
TypeScript

Next: Auth.