Custom Tables
Store app-specific structured data in ezAuth. Create typed tables, insert rows, and query with filtering and sorting.
Overview
Custom tables let you store structured data alongside your users without running a separate database. Each application can create tables with typed columns, and rows can optionally be scoped to individual users.
Use cases: user profiles, settings, contacts, form submissions, feature flags, app configuration.
Concepts
- Table — a named collection of columns and rows, scoped to your application
- Column — a typed field definition (text, int, bool, float, date, json)
- Row — a JSON object containing data that matches the column definitions
- User-scoped rows — rows can optionally be tied to a specific user via
user_id
Column Types
| Type | Description | Example |
|---|---|---|
text | String value | "Alice" |
int | Integer | 30 |
float | Floating-point number | 3.14 |
bool | Boolean | true |
date | Date/datetime string | "2025-01-15" |
json | Arbitrary JSON | {"key": "value"} |
Create a Table
const table = await ez.tables.create({
name: 'contacts',
columns: [
{ name: 'name', type: 'text', required: true },
{ name: 'age', type: 'int' },
{ name: 'active', type: 'bool', defaultValue: true },
],
})
Add / Update / Remove Columns
// Add a column to an existing table
const col = await ez.tables.columns.add(table.id, {
name: 'email',
type: 'text',
required: false,
position: 2,
})
// Update a column
await ez.tables.columns.update(table.id, col.id, {
name: 'email_address',
required: true,
})
// Delete a column
await ez.tables.columns.delete(table.id, col.id)
Insert Rows
const row = await ez.tables.rows.insert(table.id, {
data: { name: 'Alice', age: 30, active: true },
})
// row.id, row.data, row.created_at, row.updated_at
User-Scoped Rows
Pass user_id to tie a row to a specific user. When authenticated users access the table, they only see their own rows.
// Backend: insert a row for a specific user
await ez.tables.rows.insert(table.id, {
data: { name: 'Alice' },
userId: 'user-uuid',
})
Query Rows
Query rows with filtering, sorting, and cursor-based pagination:
const result = await ez.tables.rows.query(table.id, {
filter: { field: 'age', op: 'gte', value: 18 },
sort: { field: 'name', dir: 'asc' },
limit: 50,
})
// result.rows — array of row objects
// result.next_cursor — pass to next query for pagination
Filter Operators
| Operator | Description | Example |
|---|---|---|
eq | Equal | {"field": "name", "op": "eq", "value": "Alice"} |
neq | Not equal | {"field": "active", "op": "neq", "value": false} |
gt | Greater than | {"field": "age", "op": "gt", "value": 21} |
gte | Greater than or equal | {"field": "age", "op": "gte", "value": 18} |
lt | Less than | {"field": "age", "op": "lt", "value": 65} |
lte | Less than or equal | {"field": "age", "op": "lte", "value": 100} |
Sort
| Field | Description |
|---|---|
field | Column name to sort by |
dir | "asc" or "desc" |
Update & Delete Rows
// Partial update
await ez.tables.rows.update(table.id, row.id, {
data: { age: 31 },
})
// Delete
await ez.tables.rows.delete(table.id, row.id)
Storage Limits
Custom tables have a configurable storage limit per application (default: 100 MB).
const usage = await ez.storage.get()
console.log(`${usage.used_bytes} / ${usage.limit_bytes} bytes`)
Configure via CUSTOM_TABLES_STORAGE_LIMIT_BYTES environment variable.