Skip to content

Commit 594ec4b

Browse files
brainkimclaude
andcommitted
docs: add Views section and document ensure errors
- Add Views section explaining view() function, read-only nature, and .active - Add view and isView to Core Exports - Document EnsureError and SchemaDriftError in error types 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
1 parent c79bdb2 commit 594ec4b

File tree

1 file changed

+54
-1
lines changed

1 file changed

+54
-1
lines changed

README.md

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,55 @@ const posts = await db.all([Posts, UserSummary])`
386386

387387
**Table identity**: A table definition is a singleton value which is passed to database methods for validation, normalization, schema management, and convenient CRUD operations. It is not a class.
388388

389+
## Views
390+
391+
Views are read-only projections of tables with predefined WHERE clauses:
392+
393+
```typescript
394+
import {z, table, view} from "@b9g/zen";
395+
396+
const Users = table("users", {
397+
id: z.string().db.primary(),
398+
name: z.string(),
399+
role: z.enum(["user", "admin"]),
400+
deletedAt: z.date().nullable().db.softDelete(),
401+
});
402+
403+
// Define views with explicit names
404+
const ActiveUsers = view("active_users", Users)`
405+
WHERE ${Users.cols.deletedAt} IS NULL
406+
`;
407+
408+
const AdminUsers = view("admin_users", Users)`
409+
WHERE ${Users.cols.role} = ${"admin"}
410+
`;
411+
412+
// Query from views (same API as tables)
413+
const admins = await db.all(AdminUsers)``;
414+
const admin = await db.get(AdminUsers, "u1");
415+
416+
// Views are read-only — mutations throw errors
417+
await db.insert(AdminUsers, {...}); // ✗ Error
418+
await db.update(AdminUsers, {...}); // ✗ Error
419+
await db.delete(AdminUsers, "u1"); // ✗ Error
420+
```
421+
422+
**Auto-generated `.active` view:** Tables with a `.db.softDelete()` field automatically get an `.active` view:
423+
424+
```typescript
425+
// Equivalent to: view("users_active", Users)`WHERE deletedAt IS NULL`
426+
const activeUsers = await db.all(Users.active)``;
427+
```
428+
429+
**Views preserve table relationships:** Views inherit references from their base table, so JOINs work identically:
430+
431+
```typescript
432+
const posts = await db.all([Posts, AdminUsers])`
433+
JOIN "admin_users" ON ${AdminUsers.on(Posts)}
434+
`;
435+
posts[0].author?.role; // "admin"
436+
```
437+
389438
## Queries
390439

391440
Tagged templates with automatic parameterization:
@@ -852,6 +901,8 @@ await db.transaction(async (tx) => {
852901
- `AlreadyExistsError` — Unique constraint violated (tableName, field, value)
853902
- `QueryError` — SQL execution failed (sql)
854903
- `MigrationError` / `MigrationLockError` — Migration failures (fromVersion, toVersion)
904+
- `EnsureError` — Schema ensure operation failed (operation, table, step)
905+
- `SchemaDriftError` — Existing schema doesn't match definition (table, drift)
855906
- `ConnectionError` / `TransactionError` — Connection/transaction issues
856907

857908
## Debugging
@@ -922,9 +973,11 @@ import {
922973
// Zod (extended with .db namespace)
923974
z, // Re-exported Zod with .db already available
924975

925-
// Table definition
976+
// Table and view definition
926977
table, // Create a table definition from Zod schema
978+
view, // Create a read-only view from a table
927979
isTable, // Type guard for Table objects
980+
isView, // Type guard for View objects
928981
extendZod, // Extend a separate Zod instance (advanced)
929982

930983
// Database

0 commit comments

Comments
 (0)