@palbase/backend 39.0.0 → 39.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +3 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +43 -10
- package/dist/index.d.ts +43 -10
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/docs/database.md +50 -0
- package/docs/llms-full.txt +50 -0
- package/package.json +1 -1
package/docs/database.md
CHANGED
|
@@ -29,6 +29,56 @@ await Database.public.todos.delete(todo.id);
|
|
|
29
29
|
|
|
30
30
|
See [schema.md](./schema.md) for the full typed-table surface.
|
|
31
31
|
|
|
32
|
+
## Tenant-scoped CRUD — `defineRepository`
|
|
33
|
+
|
|
34
|
+
Most repository classes are the same two lines per method: add the tenant column
|
|
35
|
+
to the predicate, then unwrap the result. Writing that by hand is what makes it
|
|
36
|
+
forgettable, and one forgotten predicate shows one tenant's row to another.
|
|
37
|
+
`defineRepository` produces a BASE CLASS that writes the predicate once.
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
import { defineRepository } from "@palbase/backend";
|
|
41
|
+
|
|
42
|
+
export class TodoRepository extends defineRepository(
|
|
43
|
+
Database.public.todos,
|
|
44
|
+
{ tenant: "household_id" },
|
|
45
|
+
) {}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
The subclass gets six typed methods, all scoped to the tenant you pass first:
|
|
49
|
+
`list(tenant)`, `find(tenant, id)`, `insert(tenant, values)`,
|
|
50
|
+
`update(tenant, id, patch)`, `updateScoped(tenant, id, patch)` and
|
|
51
|
+
`delete(tenant, id)`. Add your own methods in the subclass body as usual.
|
|
52
|
+
|
|
53
|
+
`insert` does NOT take the tenant column in its payload — the repository writes
|
|
54
|
+
it. `update` throws `NotFound` when nothing matches, so the caller does not need
|
|
55
|
+
a `null` branch; `updateScoped` runs the same predicate and returns `null`
|
|
56
|
+
instead, for when absence is a value.
|
|
57
|
+
|
|
58
|
+
The base class constructor takes no arguments, so the DI container resolves the
|
|
59
|
+
subclass by its own name with no extra metadata. `Database` stays ambient rather
|
|
60
|
+
than injected: it is request-scoped, so a reference captured in a constructor
|
|
61
|
+
would carry one request's client into another.
|
|
62
|
+
|
|
63
|
+
### A row key that is not `id`
|
|
64
|
+
|
|
65
|
+
The row key defaults to `id`, but it is not fixed — `defineTable` does not
|
|
66
|
+
require an `id` column, and a primary key can sit on any column. Name it:
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
export class DocRepository extends defineRepository(
|
|
70
|
+
Database.public.docs,
|
|
71
|
+
{ tenant: "org_id", key: "slug" },
|
|
72
|
+
) {}
|
|
73
|
+
|
|
74
|
+
await new DocRepository().find("org_1", "getting-started");
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
The key parameter carries the column's OWN type, so a numeric key is typed
|
|
78
|
+
`number` rather than assumed to be a string. If the table has no `id` column and
|
|
79
|
+
you do not name a key, the surface refuses at compile time and the refusal says
|
|
80
|
+
what to do (`MissingRowKey`).
|
|
81
|
+
|
|
32
82
|
## Transaction boundaries and retries
|
|
33
83
|
|
|
34
84
|
Ordinary Database writes commit when the request completes. `$transaction(fn)`
|
package/docs/llms-full.txt
CHANGED
|
@@ -1163,6 +1163,56 @@ await Database.public.todos.delete(todo.id);
|
|
|
1163
1163
|
|
|
1164
1164
|
See [schema.md](./schema.md) for the full typed-table surface.
|
|
1165
1165
|
|
|
1166
|
+
## Tenant-scoped CRUD — `defineRepository`
|
|
1167
|
+
|
|
1168
|
+
Most repository classes are the same two lines per method: add the tenant column
|
|
1169
|
+
to the predicate, then unwrap the result. Writing that by hand is what makes it
|
|
1170
|
+
forgettable, and one forgotten predicate shows one tenant's row to another.
|
|
1171
|
+
`defineRepository` produces a BASE CLASS that writes the predicate once.
|
|
1172
|
+
|
|
1173
|
+
```ts
|
|
1174
|
+
import { defineRepository } from "@palbase/backend";
|
|
1175
|
+
|
|
1176
|
+
export class TodoRepository extends defineRepository(
|
|
1177
|
+
Database.public.todos,
|
|
1178
|
+
{ tenant: "household_id" },
|
|
1179
|
+
) {}
|
|
1180
|
+
```
|
|
1181
|
+
|
|
1182
|
+
The subclass gets six typed methods, all scoped to the tenant you pass first:
|
|
1183
|
+
`list(tenant)`, `find(tenant, id)`, `insert(tenant, values)`,
|
|
1184
|
+
`update(tenant, id, patch)`, `updateScoped(tenant, id, patch)` and
|
|
1185
|
+
`delete(tenant, id)`. Add your own methods in the subclass body as usual.
|
|
1186
|
+
|
|
1187
|
+
`insert` does NOT take the tenant column in its payload — the repository writes
|
|
1188
|
+
it. `update` throws `NotFound` when nothing matches, so the caller does not need
|
|
1189
|
+
a `null` branch; `updateScoped` runs the same predicate and returns `null`
|
|
1190
|
+
instead, for when absence is a value.
|
|
1191
|
+
|
|
1192
|
+
The base class constructor takes no arguments, so the DI container resolves the
|
|
1193
|
+
subclass by its own name with no extra metadata. `Database` stays ambient rather
|
|
1194
|
+
than injected: it is request-scoped, so a reference captured in a constructor
|
|
1195
|
+
would carry one request's client into another.
|
|
1196
|
+
|
|
1197
|
+
### A row key that is not `id`
|
|
1198
|
+
|
|
1199
|
+
The row key defaults to `id`, but it is not fixed — `defineTable` does not
|
|
1200
|
+
require an `id` column, and a primary key can sit on any column. Name it:
|
|
1201
|
+
|
|
1202
|
+
```ts
|
|
1203
|
+
export class DocRepository extends defineRepository(
|
|
1204
|
+
Database.public.docs,
|
|
1205
|
+
{ tenant: "org_id", key: "slug" },
|
|
1206
|
+
) {}
|
|
1207
|
+
|
|
1208
|
+
await new DocRepository().find("org_1", "getting-started");
|
|
1209
|
+
```
|
|
1210
|
+
|
|
1211
|
+
The key parameter carries the column's OWN type, so a numeric key is typed
|
|
1212
|
+
`number` rather than assumed to be a string. If the table has no `id` column and
|
|
1213
|
+
you do not name a key, the surface refuses at compile time and the refusal says
|
|
1214
|
+
what to do (`MissingRowKey`).
|
|
1215
|
+
|
|
1166
1216
|
## Transaction boundaries and retries
|
|
1167
1217
|
|
|
1168
1218
|
Ordinary Database writes commit when the request completes. `$transaction(fn)`
|
package/package.json
CHANGED