Skip to content

Commit 7a50740

Browse files
authored
Update dependencies and change goose joke generator to work with D1 (#63)
* Update .gitignore * Update all wranglers * Update cloudflare workers types * Update goose-joke-generator to work with D1 * Remove jsnation hashtag * Update examples README description of goose-joke-generator
1 parent a1e8d57 commit 7a50740

File tree

37 files changed

+1413
-680
lines changed

37 files changed

+1413
-680
lines changed

.gitignore

+13-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,15 @@ templates/**/package-lock.json
66
# Ignore package-lock.json files in examples
77
examples/**/package-lock.json
88

9+
# Ignore .wrangler folder in examples
10+
examples/**/.wrangler
11+
12+
# Ignore .dev.vars files in examples
13+
examples/**/.dev.vars
14+
15+
# Ignore .prod.vars files in examples
16+
examples/**/.prod.vars
17+
918
# Ignore test apps
1019
cli/test-apps/**
1120
!cli/test-apps/.gitkeep
@@ -14,4 +23,7 @@ cli/test-apps/**
1423
.DS_Store
1524

1625
# Ignore VSCode settings
17-
.vscode/
26+
.vscode/
27+
28+
# script for removing nodemodules
29+
remove_node_modules.sh

examples/README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ A GitHub PR review bot that provides "goosey" code reviews using Claude. Built w
8787
</details>
8888

8989
### 😄 [Goose Joke Generator](./goose-joke-generator)
90-
A web app that generates (terrible) goose-themed jokes using Cloudflare AI. Stores jokes in a Neon Postgres database and includes rate limiting functionality.
90+
A web app that generates (terrible) goose-themed jokes using Cloudflare AI. Stores jokes in a D1 sqlite database and includes rate limiting functionality.
9191

9292
- See it live: https://goose-jokes.fp.dev
9393

examples/cf-retrieval-augmented-goose/package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,14 @@
2626
"zod": "^3.23.8"
2727
},
2828
"devDependencies": {
29-
"@cloudflare/workers-types": "^4.20241205.0",
29+
"@cloudflare/workers-types": "^4.20250321.0",
3030
"@fiberplane/hono-otel": "^0.6.2",
3131
"@types/node": "^22.2.0",
3232
"drizzle-kit": "^0.28.1",
3333
"pg": "^8.13.1",
3434
"pgvector": "^0.2.0",
3535
"tsx": "^4.19.2",
3636
"typescript": "^5.5.4",
37-
"wrangler": "^3.95.0"
37+
"wrangler": "^4.4.0"
3838
}
3939
}
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
DATABASE_URL=
1+
# no env vars necessary for this project

examples/goose-joke-generator/.fpxconfig/.gitignore

-5
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
CLOUDFLARE_D1_TOKEN=
2+
CLOUDFLARE_ACCOUNT_ID=
3+
CLOUDFLARE_DATABASE_ID=
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# D1 — what's going on here?
2+
3+
D1 is Cloudflare's relational database. It's basically sqlite!
4+
5+
Working with HONC + D1 locally is a little peculiar. We've done as best as goosily possible to make it easy for you, but you'll see some eye-raising funkiness if you look at the migration and seed scripts :goose:.
6+
7+
Visit https://developers.cloudflare.com/d1/ for more information
8+
9+
## Local development
10+
11+
Wrangler spins up a local D1 database for you to work with.
12+
13+
You don't really have to worry about this too much, though, because the `db:setup` command will try to take care of everything for you.
14+
15+
When you run `npm run db:setup`, it runs the following commands:
16+
17+
```sh
18+
npm run db:touch
19+
npm run db:generate
20+
npm run db:migrate
21+
npm run db:seed
22+
```
23+
24+
This is how it works
25+
26+
- runs `db:touch` to make sure the local database exists
27+
- runs `db:generate` to create the database migration _files_
28+
- runs `db:migrate` to apply the migration files (run them) on the local database
29+
- runs `db:seed` to seed the local database with some data
30+
31+
32+
## Production
33+
34+
Create a Cloudflare account and D1 instance, retrieve the database key and your account id from the dashboard and addionally create an API token with D1 edit rights, and add it to your .prod.vars file.
35+
36+
```sh
37+
CLOUDFLARE_D1_TOKEN=""
38+
CLOUDFLARE_ACCOUNT_ID=""
39+
CLOUDFLARE_DATABASE_ID=""
40+
```
41+
42+
(This is also covered in the README.md file.)
43+
44+
Then run `npm run db:migrate:prod` to apply the migrations to the production database.

examples/goose-joke-generator/README.md

+23-17
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,14 @@
11
## 🪿 Goose Joke Generator
22

3-
This is a project created with the Neon HONC template.
3+
This is a project created with the D1 HONC template.
44

5-
It is a simple Goose Joke Generator that uses Cloudflare AI to generate jokes, and store them in a Neon Postgres database.
5+
It is a simple Goose Joke Generator that uses Cloudflare AI to generate jokes, and store them in a D1 database.
66

77
The jokes are of a particularly poor quality.
88

99
### Getting started
1010

11-
Make sure you have Neon set up and the api is configured to use your database.
12-
13-
To do this, create a `.dev.vars` file with your Neon connection string as the `DATABASE_URL` key and value (see: `.dev.vars.example`).
14-
15-
Also make sure you are logged in to Cloudflare and have access to Workers AI.
11+
Make sure you are logged in to Cloudflare and have access to Workers AI.
1612

1713
Then, run `pnpm dev` to kick off the app locally.
1814

@@ -26,7 +22,6 @@ Then, run `pnpm dev` to kick off the app locally.
2622
│ └── db
2723
│ └── schema.ts # Database schema
2824
├── seed.ts # Seeding script
29-
├── .dev.vars.example # Example .dev.vars file
3025
├── wrangler.toml # Cloudflare Workers configuration
3126
├── drizzle.config.ts # Drizzle configuration
3227
├── tsconfig.json # TypeScript configuration
@@ -49,25 +44,36 @@ Run the development server:
4944
pnpm dev
5045
```
5146

52-
Test and debug with Fiberplane:
53-
54-
```sh
55-
pnpm fiberplane
56-
```
57-
5847
### Deploying to Cloudflare
5948

6049
> **Remember this uses some AI!** So you will be billed for any newly generated goose jokes.
6150
6251
Deploy with Cloudflare Wrangler.
6352

64-
First set the DATABASE_URL as a secret:
53+
#### Setting up the D1 database
54+
First, create the D1 database:
6555

6656
```sh
67-
pnpx wrangler secret put DATABASE_URL
68-
# when prompted, enter your Neon connection string
57+
pnpx wrangler d1 create goose-joke-generator
6958
```
7059

60+
Then, update your wrangler.toml file to include the D1 database id:
61+
62+
```toml
63+
[[d1_databases]]
64+
binding = "DB"
65+
database_name = "goose-joke-generator"
66+
database_id = "<result from previous command>"
67+
```
68+
69+
Finally, create a `.prod.vars` file with your Cloudflare API token, account ID, and database ID (See: `D1-explained.md`), and run the migrations against production:
70+
71+
```sh
72+
pnpm run db:migrate:prod
73+
```
74+
75+
#### Setting up KV for the rate limiter
76+
7177
Then, create the KV binding on your account, since we use that for the rate limiter:
7278

7379
```sh
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,68 @@
1+
import fs from "node:fs";
2+
import path from "node:path";
13
import { config } from "dotenv";
24
import { defineConfig } from "drizzle-kit";
35

4-
config({ path: "./.dev.vars" });
6+
let dbConfig: ReturnType<typeof defineConfig>;
7+
if (process.env.ENVIRONMENT === "production") {
8+
config({ path: "./.prod.vars" });
9+
dbConfig = defineConfig({
10+
schema: "./src/db/schema.ts",
11+
out: "./drizzle/migrations",
12+
dialect: "sqlite",
13+
driver: "d1-http",
14+
dbCredentials: {
15+
accountId: process.env.CLOUDFLARE_ACCOUNT_ID ?? "",
16+
databaseId: process.env.CLOUDFLARE_DATABASE_ID ?? "",
17+
token: process.env.CLOUDFLARE_D1_TOKEN ?? "",
18+
},
19+
});
20+
} else {
21+
config({ path: "./.dev.vars" });
22+
const localD1DB = getLocalD1DB();
23+
if (!localD1DB) {
24+
process.exit(1);
25+
}
526

6-
export default defineConfig({
7-
schema: "./src/db/schema.ts",
8-
out: "./drizzle",
9-
dialect: "postgresql",
10-
dbCredentials: {
11-
url: process.env.DATABASE_URL ?? "",
12-
},
13-
});
27+
dbConfig = defineConfig({
28+
schema: "./src/db/schema.ts",
29+
out: "./drizzle/migrations",
30+
dialect: "sqlite",
31+
dbCredentials: {
32+
url: localD1DB,
33+
},
34+
});
35+
}
36+
37+
export default dbConfig;
38+
39+
function getLocalD1DB() {
40+
try {
41+
const basePath = path.resolve(".wrangler");
42+
const files = fs
43+
.readdirSync(basePath, { encoding: "utf-8", recursive: true })
44+
.filter((f) => f.endsWith(".sqlite"));
45+
46+
// In case there are multiple .sqlite files, we want the most recent one.
47+
files.sort((a, b) => {
48+
const statA = fs.statSync(path.join(basePath, a));
49+
const statB = fs.statSync(path.join(basePath, b));
50+
return statB.mtime.getTime() - statA.mtime.getTime();
51+
});
52+
const dbFile = files[0];
53+
54+
if (!dbFile) {
55+
throw new Error(`.sqlite file not found in ${basePath}`);
56+
}
57+
58+
const url = path.resolve(basePath, dbFile);
59+
60+
return url;
61+
} catch (err) {
62+
if (err instanceof Error) {
63+
console.log(`Error resolving local D1 DB: ${err.message}`);
64+
} else {
65+
console.log(`Error resolving local D1 DB: ${err}`);
66+
}
67+
}
68+
}

examples/goose-joke-generator/drizzle/0000_abandoned_red_ghost.sql

-5
This file was deleted.

examples/goose-joke-generator/drizzle/meta/0000_snapshot.json

-44
This file was deleted.

examples/goose-joke-generator/drizzle/meta/_journal.json

-13
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
CREATE TABLE `jokes` (
2+
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
3+
`content` text NOT NULL,
4+
`created_at` text DEFAULT (CURRENT_TIMESTAMP) NOT NULL,
5+
`updated_at` text DEFAULT (CURRENT_TIMESTAMP) NOT NULL
6+
);

0 commit comments

Comments
 (0)