docs(tutorial): use Effect-native SQL and HTTP primitives#400
docs(tutorial): use Effect-native SQL and HTTP primitives#400sam-goodwin wants to merge 7 commits into
Conversation
- neon-hyperdrive: replace raw pg/mysql2 + Effect.promise with @effect/sql-pg / @effect/sql-mysql2 PgClient.make / MysqlClient.make - queue-consumer: replace `new Response` with HttpServerResponse.text, and put Stream.runForEach at the end of stream.pipe instead of wrapping the per-message effect Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
Install the packages built from this commit: alchemy bun add alchemy@https://pkg.ing/alchemy/ac03071@alchemy.run/better-auth bun add @alchemy.run/better-auth@https://pkg.ing/@alchemy.run/better-auth/ac03071@alchemy.run/pr-package bun add @alchemy.run/pr-package@https://pkg.ing/@alchemy.run/pr-package/ac03071 |
Website Preview DeployedURL: https://alchemyeffectwebsite-worker-pr-400-nfqvmp3ylra5rt7a.testing-2b2.workers.dev Built from commit This comment updates automatically with each push. |
| + | ||
| return { | ||
| fetch: Effect.gen(function* () { | ||
| + const connectionString = yield* hd.connectionString; |
There was a problem hiding this comment.
You can't yield* hd.connectionString here. PgClient needs to be lazily evaluated (maybe provided as a Layer to the fetch effect)
There was a problem hiding this comment.
Fixed in 25145a3. Replaced PgClient.make({ url: yield* hd.connectionString }) at init with a Layer.unwrapEffect(hd.connectionString.pipe(Effect.map(url => PgClient.layer({ url })))) provided to the fetch effect — connectionString is now read lazily under the request's RuntimeContext.
| }, | ||
| Effect.gen(function* () { | ||
| + const hd = yield* Cloudflare.Hyperdrive.bind(Hyperdrive); | ||
| + const sql = yield* PgClient.make({ url: yield* hd.connectionString }); |
There was a problem hiding this comment.
This won't work. Perhaps we need PgClient.layer and Layer.unwrap?
There was a problem hiding this comment.
Done — now using PgClient.layer / MysqlClient.layer wrapped in Layer.unwrapEffect, provided to the fetch boundary. See 25145a3.
| Three things to notice (regardless of engine): | ||
|
|
||
| - The `nodejs_compat` flag is required because the underlying | ||
| driver (`pg` / `mysql2`) uses Node's `net` and `crypto` APIs. | ||
| - `PgClient.make` / `MysqlClient.make` returns a scoped Effect that | ||
| builds the client once per Worker isolate. Subsequent requests | ||
| reuse it — no per-request connect/disconnect — while Hyperdrive | ||
| handles the pooling on the Cloudflare side of the wire. | ||
| - Queries are written as tagged templates (`` sql`...` ``) that are | ||
| `Effect`s. You `yield*` them directly inside `fetch` — no | ||
| `Effect.promise(async () => ...)` wrappers, and errors flow into | ||
| the typed error channel as `SqlError`. |
There was a problem hiding this comment.
Don't use bullet points like this to point of "things to notice". Instead, break the code into diff/delta snippets and explain step by step, drawing the user's attention to the appropriate code
There was a problem hiding this comment.
Restructured in 25145a3: dropped the trailing bullet list and split the section into three delta diff snippets (bind at init → derive SqlLive via Layer.unwrapEffect → query in fetch + provide), each with one prose paragraph drawing attention to the new code.
PR feedback: - yield* hd.connectionString at init doesn't work — connectionString requires RuntimeContext, only available at request time - Replace PgClient.make / MysqlClient.make at init with Layer.unwrapEffect over PgClient.layer / MysqlClient.layer, provided at the fetch boundary - Break the "Bind Hyperdrive to your Worker" mega-snippet into delta diff steps with prose per step, drop the trailing "things to notice" bullet list Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Replaces the tabbed Neon/PlanetScale Postgres/PlanetScale MySQL guide with three provider-specific tutorials sharing the same structure: - neon-hyperdrive (Neon Postgres, order 7) - planetscale-postgres-hyperdrive (new, order 8) - planetscale-mysql-hyperdrive (new, order 9) Bumped drizzle (10), branch-from-shared-database (11), and queue-consumer (12) to keep the chapter sequence. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
nodejs_compat is on by default for Effect-native Cloudflare Workers, so the explicit flag (and its accompanying comments/bullets) is just noise. Removed from the three hyperdrive tutorials, drizzle, and artifacts. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Per the tutorial standard, each new concept gets its own heading, diff snippet, and prose paragraph instead of dumping the full Db.ts at once. - neon-hyperdrive: split into "Create the project" + "Fork a branch" - planetscale-postgres-hyperdrive: "Create the cluster" + "Fork a branch" + "Materialize a role for Hyperdrive" - planetscale-mysql-hyperdrive: "Create the cluster" + "Fork a branch" + "Materialize a password for Hyperdrive" Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…drive Adds Worker + Hyperdrive + SQL-client fixtures so the three tutorial guides are actually exercised against a real cluster: - Planetscale Postgres: restructured fixtures into `hyperdrive-drizzle/` and `hyperdrive-effect-sql/`. Existing Hyperdrive.test.ts → Hyperdrive.drizzle.test.ts. New Hyperdrive.effect-sql.test.ts uses `@effect/sql-pg` PgClient.layer + Layer.unwrapEffect. - Planetscale MySQL: new Stack + Hyperdrive.effect-sql.test.ts using `@effect/sql-mysql2`. Skipped the Drizzle variant — drizzle-orm has no `effect-mysql2` adapter. - Neon: new Stack + Hyperdrive.drizzle.test.ts (Drizzle.postgres) and Hyperdrive.effect-sql.test.ts (`@effect/sql-pg`). Each effect-sql worker uses a separate `*_sql` table so it can share the same Hyperdrive cluster as the drizzle variant without colliding. Drive-by fixes: - `SqlClient` import path corrected to `effect/unstable/sql/SqlClient` (it lives under effect's unstable tree in v4, not `@effect/sql`). Updated in the three tutorial docs to match. - Tutorial docs now reference `Cloudflare.HyperdriveBindingLive` (the actual export name) instead of `HyperdriveConnectionLive`. - Added `@effect/sql-mysql2` to the catalog + alchemy optional peer deps. Tests are gated behind `NEON_TEST=1` / `PLANETSCALE_TEST=1`. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Two tutorial fixes:
tutorial/cloudflare/neon-hyperdriveSwap the raw
pg/mysql2snippets (wrapped inEffect.promise(async () => …)) for the Effect-native SQL clients.Install lines change from
pg+@types/pg/mysql2to@effect/sql-pg/@effect/sql-mysql2(the underlying driver is bundled). MySQL tab gets the equivalentMysqlClient.maketreatment.tutorial/cloudflare/queue-consumerreturn new Response("ok")withHttpServerResponse.text("ok")(added the missing import).Stream.runForEachis the terminal step onstream.pipe(...)rather than wrapping the per-message effect, and drop the redundantEffect.asVoid:yield* Cloudflare.messages<QueueMessageBody>(queueResource).subscribe( (stream) => - Stream.runForEach(stream, (msg) => - bucket - .put(`/queue/${msg.body.id}`, JSON.stringify(msg.body), { - httpMetadata: { contentType: "application/json" }, - }) - .pipe(Effect.asVoid), - ), + stream.pipe( + Stream.runForEach((msg) => + bucket.put(`/queue/${msg.body.id}`, JSON.stringify(msg.body), { + httpMetadata: { contentType: "application/json" }, + }), + ), + ), );