Skip to content

Commit 70c1698

Browse files
committed
Add golem-db-app skill and scenario with HTTP + DB verification
- New skill: golem-db-app — teaches agents to build PostgreSQL-backed Golem apps with HTTP endpoints using golem:rdbms/postgres - New scenario: golem-db-app-ts — creates app, deploys, verifies HTTP POST/GET endpoints, and checks DB rows via psql - Tested with codex (pass) and gemini (build/deploy pass)
1 parent 9224cb7 commit 70c1698

3 files changed

Lines changed: 237 additions & 2 deletions

File tree

.github/workflows/skills-test.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,8 @@ jobs:
4949
postgres:
5050
image: postgres:16
5151
env:
52-
POSTGRES_USER: golem
53-
POSTGRES_PASSWORD: golem
52+
POSTGRES_USER: postgres
53+
POSTGRES_PASSWORD: postgres
5454
POSTGRES_DB: golem_test
5555
ports:
5656
- "5432:5432"
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
---
2+
name: golem-db-app
3+
description: "Building a Golem application with PostgreSQL database integration. Use when creating agents that store and query data using golem:rdbms/postgres, defining HTTP endpoints, and configuring environment variables."
4+
---
5+
6+
# Building a Database-Backed Golem Application
7+
8+
**Important: Do not try to build golem from scratch or install it manually.**
9+
10+
Assume the `golem` or `golem-cli` binary exists and is added to PATH.
11+
Try `golem --version` to check if it exists. If not, try `golem-cli --version`.
12+
13+
## Step 1: Create the Project
14+
15+
```shell
16+
golem new <APP_NAME> --template ts -Y
17+
```
18+
19+
## Step 2: Determine the rdbms Module Version
20+
21+
Check which rdbms version the installed SDK provides:
22+
23+
```shell
24+
ls node_modules/@golemcloud/golem-ts-sdk/types/ | grep rdbms
25+
```
26+
27+
This will show files like `golem_rdbms_X_Y_Z_postgres.d.ts`. Use that version in your import (e.g., `@1.5.0` or `@0.0.2`).
28+
29+
## Step 3: Write the Agent with PostgreSQL
30+
31+
Import the database module and define an agent with HTTP endpoints. Use the rdbms version found in Step 2:
32+
33+
```typescript
34+
import {
35+
BaseAgent,
36+
agent,
37+
prompt,
38+
description,
39+
endpoint
40+
} from '@golemcloud/golem-ts-sdk';
41+
// Use the version from Step 2 (check node_modules/@golemcloud/golem-ts-sdk/types/)
42+
import { DbConnection, DbValue, DbResult } from 'golem:rdbms/postgres@1.5.0';
43+
44+
@agent({
45+
mount: "/items/{name}"
46+
})
47+
export class ItemAgent extends BaseAgent {
48+
private readonly name: string;
49+
private initialized: boolean = false;
50+
51+
constructor(name: string) {
52+
super();
53+
this.name = name;
54+
}
55+
56+
private getDb(): DbConnection {
57+
return DbConnection.open(process.env.DB_POSTGRES_URL!);
58+
}
59+
60+
private ensureTable(): void {
61+
if (!this.initialized) {
62+
const db = this.getDb();
63+
db.execute(
64+
"CREATE TABLE IF NOT EXISTS items (id SERIAL PRIMARY KEY, name TEXT NOT NULL)",
65+
[]
66+
);
67+
this.initialized = true;
68+
}
69+
}
70+
71+
@endpoint({ post: "/add" })
72+
async addItem(name: string): Promise<string> {
73+
this.ensureTable();
74+
const db = this.getDb();
75+
db.execute(
76+
"INSERT INTO items (name) VALUES ($1)",
77+
[{ tag: 'text', val: name } as DbValue]
78+
);
79+
return `Added: ${name}`;
80+
}
81+
82+
@endpoint({ get: "/list" })
83+
async listItems(): Promise<string[]> {
84+
this.ensureTable();
85+
const db = this.getDb();
86+
const result: DbResult = db.query("SELECT name FROM items", []);
87+
return result.rows.map(row => {
88+
const val = row.values[0];
89+
return val.tag === 'text' ? val.val : String(val.val);
90+
});
91+
}
92+
}
93+
```
94+
95+
### DbValue Tagged Union
96+
97+
All query parameters use the `DbValue` tagged union. Common tags:
98+
99+
| TypeScript | DbValue |
100+
|-----------|---------|
101+
| `string` | `{ tag: 'text', val: "hello" }` |
102+
| `number` (int) | `{ tag: 'int4', val: 42 }` |
103+
| `number` (float) | `{ tag: 'float8', val: 3.14 }` |
104+
| `boolean` | `{ tag: 'boolean', val: true }` |
105+
| `bigint` | `{ tag: 'int8', val: 100n }` |
106+
| `null` | `{ tag: 'null' }` |
107+
108+
### DbResult Structure
109+
110+
```typescript
111+
const result: DbResult = db.query("SELECT id, name FROM items", []);
112+
// result.columns: DbColumn[] — column metadata
113+
// result.rows: DbRow[] — array of rows
114+
// result.rows[0].values: DbValue[] — values in column order
115+
```
116+
117+
### Transactions
118+
119+
```typescript
120+
const db = this.getDb();
121+
const tx = db.beginTransaction();
122+
try {
123+
tx.execute("INSERT INTO items (name) VALUES ($1)", [{ tag: 'text', val: "a" }]);
124+
tx.execute("INSERT INTO items (name) VALUES ($1)", [{ tag: 'text', val: "b" }]);
125+
tx.commit();
126+
} catch (e) {
127+
tx.rollback();
128+
throw e;
129+
}
130+
```
131+
132+
## Step 4: Export the Agent
133+
134+
In `src/main.ts`:
135+
136+
```typescript
137+
export { ItemAgent } from './item-agent';
138+
```
139+
140+
## Step 5: Configure Environment Variables
141+
142+
In `golem.yaml`, add the database URL under the component's `env` section:
143+
144+
```yaml
145+
components:
146+
<app-name>:ts-main:
147+
env:
148+
DB_POSTGRES_URL: "postgres://golem:golem@localhost:5432/golem_test"
149+
```
150+
151+
The component name follows the pattern `<app-name>:ts-main` for single-component TypeScript apps.
152+
153+
## Step 6: Build and Deploy
154+
155+
```shell
156+
cd <APP_NAME>
157+
golem build
158+
golem deploy --yes
159+
```
160+
161+
After deployment, HTTP endpoints are available at `http://<app-name>.localhost:9006/<mount-path>/<endpoint-path>`.
162+
163+
## Checklist
164+
165+
1. `golem new` executed successfully
166+
2. SDK versions fixed to `1.0.0-dev.1` in `package.json`
167+
3. Agent imports `DbConnection` from `'golem:rdbms/postgres@1.5.0'`
168+
4. Agent uses `@agent({ mount: "..." })` and `@endpoint({ get/post: "..." })`
169+
5. `DB_POSTGRES_URL` set in `golem.yaml` under component env
170+
6. `golem build` succeeds
171+
7. `golem deploy --yes` succeeds
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
name: "golem-db-app-ts"
2+
settings:
3+
timeout_per_subprompt: 600
4+
golem_server:
5+
custom_request_port: 9006
6+
steps:
7+
- id: "create-db-app"
8+
prompt: |
9+
Create a new Golem application called db-app with TypeScript.
10+
11+
Then modify the app to be a simple task tracker backed by PostgreSQL:
12+
1. Create a TaskAgent with mount path "/tasks/{name}" that:
13+
- Has a POST endpoint at "/add" that takes a title (string) parameter, inserts it into a "tasks" table, and returns a confirmation message
14+
- Has a GET endpoint at "/list" that returns all task titles from the table as a string array
15+
- On first call, creates the tasks table if it doesn't exist (id SERIAL PRIMARY KEY, title TEXT NOT NULL)
16+
2. Check node_modules/@golemcloud/golem-ts-sdk/types/ for the rdbms version (e.g. golem_rdbms_X_Y_Z_postgres.d.ts) and use that version in the import: import { DbConnection, DbValue, DbResult } from 'golem:rdbms/postgres@X.Y.Z'
17+
3. Read the DB connection URL from process.env.DB_POSTGRES_URL
18+
4. Set DB_POSTGRES_URL environment variable in golem.yaml under the component's env section to "postgres://postgres:postgres@localhost:5432/golem_test"
19+
20+
Build the project with golem build, then deploy with golem deploy --yes.
21+
expectedSkills:
22+
- "golem-db-app"
23+
allowedExtraSkills:
24+
- "golem-new-project"
25+
verify:
26+
deploy: true
27+
28+
- id: "check-project-files"
29+
shell:
30+
command: "ls"
31+
args: ["db-app/golem.yaml"]
32+
expect:
33+
exit_code: 0
34+
35+
- id: "add-task-via-http"
36+
http:
37+
url: "http://db-app.localhost:9006/tasks/default/add"
38+
method: "POST"
39+
headers:
40+
Content-Type: "application/json"
41+
body: '{"title": "Test task from harness"}'
42+
expect:
43+
status: 200
44+
retry:
45+
attempts: 3
46+
delay: 5
47+
48+
- id: "list-tasks-via-http"
49+
http:
50+
url: "http://db-app.localhost:9006/tasks/default/list"
51+
method: "GET"
52+
expect:
53+
status: 200
54+
body_contains: "Test task from harness"
55+
56+
- id: "verify-db-rows"
57+
shell:
58+
command: "bash"
59+
args:
60+
- "-c"
61+
- "PGPASSWORD=postgres psql -h localhost -U postgres -d golem_test -t -c \"SELECT count(*) FROM tasks WHERE title = 'Test task from harness'\" | tr -d ' '"
62+
expect:
63+
exit_code: 0
64+
stdout_matches: "[1-9]"

0 commit comments

Comments
 (0)