@palbase/backend 2.0.0 → 2.0.2
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/README.md +24 -0
- package/docs/README.md +51 -0
- package/docs/background.md +56 -0
- package/docs/database.md +60 -0
- package/docs/endpoints.md +116 -0
- package/docs/errors.md +48 -0
- package/docs/events.md +59 -0
- package/docs/getting-started.md +49 -0
- package/docs/llms-full.txt +713 -0
- package/docs/llms.txt +16 -0
- package/docs/routing.md +34 -0
- package/docs/schema.md +82 -0
- package/docs/services.md +105 -0
- package/package.json +5 -3
package/docs/llms.txt
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Palbase Backend SDK (`@palbase/backend`)
|
|
2
|
+
|
|
3
|
+
> File-based TypeScript backend SDK. Endpoints use `req` (PBRequest) + imported service singletons (`Database`, `Cache`, …). Workers/jobs/hooks/webhooks use a `ctx` object. Not Express, not Supabase Edge Functions.
|
|
4
|
+
|
|
5
|
+
## Docs
|
|
6
|
+
|
|
7
|
+
- [README](./README.md)
|
|
8
|
+
- [getting-started](./getting-started.md)
|
|
9
|
+
- [routing](./routing.md)
|
|
10
|
+
- [endpoints](./endpoints.md)
|
|
11
|
+
- [database](./database.md)
|
|
12
|
+
- [schema](./schema.md)
|
|
13
|
+
- [services](./services.md)
|
|
14
|
+
- [errors](./errors.md)
|
|
15
|
+
- [background](./background.md)
|
|
16
|
+
- [events](./events.md)
|
package/docs/routing.md
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# File-based routing
|
|
2
|
+
|
|
3
|
+
The path of a file under `endpoints/` plus its filename determine the route.
|
|
4
|
+
The filename is the HTTP method.
|
|
5
|
+
|
|
6
|
+
| File | Route |
|
|
7
|
+
|------|-------|
|
|
8
|
+
| `endpoints/hello/get.ts` | `GET /hello` |
|
|
9
|
+
| `endpoints/items/post.ts` | `POST /items` |
|
|
10
|
+
| `endpoints/posts/[id]/get.ts` | `GET /posts/:id` |
|
|
11
|
+
| `endpoints/posts/[id]/patch.ts` | `PATCH /posts/:id` |
|
|
12
|
+
| `endpoints/rooms/[id]/sessions/post.ts` | `POST /rooms/:id/sessions` |
|
|
13
|
+
|
|
14
|
+
Rules:
|
|
15
|
+
|
|
16
|
+
- The method file name is one of `get`, `post`, `put`, `patch`, `delete` (`.ts`).
|
|
17
|
+
- A `[segment]` directory becomes a `:segment` path param, read via `req.params.segment`.
|
|
18
|
+
- Each method file `export default defineEndpoint({...})` — one endpoint per file.
|
|
19
|
+
- There is no central router file and no manual registration. Adding a file adds a route.
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
// endpoints/posts/[id]/get.ts → GET /posts/:id
|
|
23
|
+
import { defineEndpoint, z, Database, HttpError } from "@palbase/backend";
|
|
24
|
+
|
|
25
|
+
export default defineEndpoint({
|
|
26
|
+
method: "GET",
|
|
27
|
+
output: z.object({ id: z.string(), title: z.string() }),
|
|
28
|
+
handler: async (req) => {
|
|
29
|
+
const post = await Database.findById("posts", req.params.id!);
|
|
30
|
+
if (!post) throw new HttpError(404, "post_not_found", "No such post");
|
|
31
|
+
return { id: post.id as string, title: post.title as string };
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
```
|
package/docs/schema.md
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Schema & typed database access
|
|
2
|
+
|
|
3
|
+
Declare your tables in `db/schema.ts` with `defineSchema`. This drives
|
|
4
|
+
migrations and, via `typedDatabase`, a fully-typed `.tables.*` API.
|
|
5
|
+
|
|
6
|
+
## Defining a schema
|
|
7
|
+
|
|
8
|
+
```ts
|
|
9
|
+
import {
|
|
10
|
+
defineSchema, table,
|
|
11
|
+
uuid, text, integer, boolean, timestamp, jsonb, enumType,
|
|
12
|
+
} from "@palbase/backend";
|
|
13
|
+
|
|
14
|
+
export default defineSchema({
|
|
15
|
+
rooms: table("rooms", {
|
|
16
|
+
id: uuid().primaryKey().defaultRandom(),
|
|
17
|
+
name: text().notNull(),
|
|
18
|
+
capacity: integer().nullable(),
|
|
19
|
+
is_active: boolean().default(true),
|
|
20
|
+
created_at: timestamp().defaultNow(),
|
|
21
|
+
}),
|
|
22
|
+
sessions: table("sessions", {
|
|
23
|
+
id: uuid().primaryKey().defaultRandom(),
|
|
24
|
+
room_id: uuid().notNull().references("rooms", "id").onDelete("cascade"),
|
|
25
|
+
user_id: uuid().notNull(),
|
|
26
|
+
data: jsonb().nullable(),
|
|
27
|
+
started_at: timestamp().defaultNow(),
|
|
28
|
+
}),
|
|
29
|
+
orders: table("orders", {
|
|
30
|
+
id: uuid().primaryKey().defaultRandom(),
|
|
31
|
+
status: enumType("order_status", ["pending", "paid", "shipped", "cancelled"]),
|
|
32
|
+
amount: integer().notNull(),
|
|
33
|
+
}),
|
|
34
|
+
});
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Column builders
|
|
38
|
+
|
|
39
|
+
| Builder | Postgres type |
|
|
40
|
+
|---------|---------------|
|
|
41
|
+
| `uuid()` | `uuid` |
|
|
42
|
+
| `text()` | `text` |
|
|
43
|
+
| `integer()` | `integer` |
|
|
44
|
+
| `boolean()` | `boolean` |
|
|
45
|
+
| `timestamp()` | `timestamptz` |
|
|
46
|
+
| `jsonb()` | `jsonb` |
|
|
47
|
+
| `enumType(name, values)` | a Postgres enum |
|
|
48
|
+
|
|
49
|
+
Chainable modifiers: `.primaryKey()`, `.notNull()` (default), `.nullable()`,
|
|
50
|
+
`.default(value)`, `.defaultRandom()` (uuid → `gen_random_uuid()`),
|
|
51
|
+
`.defaultNow()` (timestamp → `now()`), `.references(table, column)`,
|
|
52
|
+
`.onDelete("cascade" | "set null" | "restrict" | "no action")`.
|
|
53
|
+
|
|
54
|
+
## Typed DB access
|
|
55
|
+
|
|
56
|
+
`typedDatabase(schema)` returns a typed facade. `insert` demands the right
|
|
57
|
+
columns; rows come back typed; nullable columns are `T | null`.
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
import { defineEndpoint, z, typedDatabase } from "@palbase/backend";
|
|
61
|
+
import schema from "../../db/schema.js";
|
|
62
|
+
|
|
63
|
+
const Db = typedDatabase(schema);
|
|
64
|
+
|
|
65
|
+
export default defineEndpoint({
|
|
66
|
+
method: "POST",
|
|
67
|
+
input: z.object({ name: z.string() }),
|
|
68
|
+
output: z.object({ id: z.string(), name: z.string() }),
|
|
69
|
+
handler: async (req) => {
|
|
70
|
+
const room = await Db.tables.rooms.insert({ name: req.input.name });
|
|
71
|
+
return { id: room.id, name: room.name };
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Note the `.js` extension on `../../db/schema.js` even though the file is
|
|
77
|
+
`db/schema.ts` — this is standard ESM module resolution; you still author the
|
|
78
|
+
file as `.ts`.
|
|
79
|
+
|
|
80
|
+
`Db.tables.<name>` exposes `insert`, `update(id, data)`, `delete(id)`,
|
|
81
|
+
`findById(id)`, `findMany(query?)`, and `Db.transaction(fn)` mirrors the
|
|
82
|
+
untyped transaction with typed tables.
|
package/docs/services.md
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Services
|
|
2
|
+
|
|
3
|
+
In **endpoints**, import service singletons from `@palbase/backend`. In
|
|
4
|
+
**workers/jobs/hooks/webhooks**, the equivalents live on `ctx` (`ctx.cache`,
|
|
5
|
+
`ctx.queue`, `ctx.log`, `ctx.db`).
|
|
6
|
+
|
|
7
|
+
Available singletons: `Database`, `Documents`, `Storage`, `Cache`, `Queue`,
|
|
8
|
+
`Log`, `Notifications`, `Flags`.
|
|
9
|
+
|
|
10
|
+
**Not available to backend handlers** (do not import them here): Realtime,
|
|
11
|
+
Functions, CMS, Links, Analytics, and Auth. Auth runs on the client SDK; the
|
|
12
|
+
others are out of scope for backend endpoints.
|
|
13
|
+
|
|
14
|
+
## Cache
|
|
15
|
+
|
|
16
|
+
JSON-typed key/value cache.
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
import { Cache } from "@palbase/backend";
|
|
20
|
+
|
|
21
|
+
await Cache.set("k", { hits: 1 }, 60); // value + TTL seconds
|
|
22
|
+
const v = await Cache.get<{ hits: number }>("k"); // typed, null on miss
|
|
23
|
+
await Cache.incr("counter");
|
|
24
|
+
await Cache.del("k");
|
|
25
|
+
|
|
26
|
+
// Stampede-safe read-through: only one caller across all pods runs fn.
|
|
27
|
+
const profile = await Cache.getOrSet("user:42", 300, async () => {
|
|
28
|
+
return Database.findById("users", "42");
|
|
29
|
+
});
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
`getOrSet` caches whatever `fn` returns, including `null` — return a sentinel or
|
|
33
|
+
guard upstream if you don't want misses cached.
|
|
34
|
+
|
|
35
|
+
## Queue
|
|
36
|
+
|
|
37
|
+
Enqueue work for a worker (see [background.md](./background.md)).
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
import { Queue } from "@palbase/backend";
|
|
41
|
+
const { jobId } = await Queue.push("process-order", { orderId: "ord_1", amount: 1000 });
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Log
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
import { Log } from "@palbase/backend";
|
|
48
|
+
Log.info("created order", { orderId });
|
|
49
|
+
Log.warn("retrying");
|
|
50
|
+
Log.error("failed", err);
|
|
51
|
+
Log.debug("detail");
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Storage
|
|
55
|
+
|
|
56
|
+
Bucket-scoped file operations.
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
import { Storage } from "@palbase/backend";
|
|
60
|
+
const bucket = Storage.bucket("avatars");
|
|
61
|
+
const { data, error } = await bucket.upload("u/42.png", file);
|
|
62
|
+
const { data: signed } = await bucket.createSignedUrl("u/42.png", 3600);
|
|
63
|
+
const pub = bucket.getPublicUrl("u/42.png"); // sync, no network
|
|
64
|
+
await bucket.remove(["u/42.png"]);
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
All Storage calls return `{ data, error }` — check `error` before using `data`.
|
|
68
|
+
|
|
69
|
+
## Documents
|
|
70
|
+
|
|
71
|
+
Firestore-like document store.
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
import { Documents } from "@palbase/backend";
|
|
75
|
+
const col = Documents.collection("rooms");
|
|
76
|
+
const { data: ref } = await col.add({ name: "Lobby" });
|
|
77
|
+
const snap = await col.where("active", "==", true).orderBy("name").limit(10).get();
|
|
78
|
+
const { data: doc } = await Documents.doc("rooms/abc").get();
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Notifications
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
import { Notifications } from "@palbase/backend";
|
|
85
|
+
await Notifications.email.send({ /* PalbaseEmailSendParams */ });
|
|
86
|
+
await Notifications.push.send({ /* PalbasePushSendParams */ });
|
|
87
|
+
await Notifications.sms.send({ /* PalbaseSmsSendParams */ });
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Flags
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
import { defineEndpoint, z, Flags } from "@palbase/backend";
|
|
94
|
+
|
|
95
|
+
export default defineEndpoint({
|
|
96
|
+
method: "GET",
|
|
97
|
+
auth: { required: true }, // req.user is non-null here
|
|
98
|
+
output: z.object({ enabled: z.boolean() }),
|
|
99
|
+
handler: async (req) => {
|
|
100
|
+
const { data: enabled } = await Flags.isEnabled("new-checkout", { userId: req.user.id });
|
|
101
|
+
const { data: variant } = await Flags.getVariant("button-color", { userId: req.user.id });
|
|
102
|
+
return { enabled: enabled ?? false };
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@palbase/backend",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.2",
|
|
4
4
|
"description": "Palbase Backend SDK — defineEndpoint, context types, schema DSL",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -45,7 +45,8 @@
|
|
|
45
45
|
}
|
|
46
46
|
},
|
|
47
47
|
"files": [
|
|
48
|
-
"dist"
|
|
48
|
+
"dist",
|
|
49
|
+
"docs"
|
|
49
50
|
],
|
|
50
51
|
"dependencies": {
|
|
51
52
|
"@asteasolutions/zod-to-openapi": "^7.3.4",
|
|
@@ -65,6 +66,7 @@
|
|
|
65
66
|
"build": "tsup",
|
|
66
67
|
"test": "vitest run",
|
|
67
68
|
"test:watch": "vitest",
|
|
68
|
-
"typecheck": "tsc --noEmit"
|
|
69
|
+
"typecheck": "tsc --noEmit",
|
|
70
|
+
"docs:build": "node scripts/build-llms.mjs"
|
|
69
71
|
}
|
|
70
72
|
}
|