1---
2title: Hyperdrive
3description: Cloudflare Hyperdrive pools connections to your external Postgres or MySQL database at the edge — provision a database (Neon or PlanetScale), front it with Hyperdrive, and bind the connection into your Worker.
4---
5
6import { Code, Tabs, TabItem } from "@astrojs/starlight/components";
7
8export const pgDeps = `pg`;
9export const pgDevDeps = `@types/pg`;
10export const mysqlDeps = `mysql2`;
11
12Hyperdrive is a Cloudflare-managed connection pooler that sits
13between Workers and an external Postgres or MySQL database. The
14Worker sees a familiar connection string, but the connection itself
15is already pooled at the edge — no per-request TCP handshakes, no
16cold-start connection storms.
17
18Reach for Hyperdrive whenever your app needs a real relational
19database: managed Postgres/MySQL providers like Neon and
20PlanetScale, or a database you already run. (For serverless SQLite
21inside Cloudflare, use [D1](/cloudflare/data/d1) instead.) This page
22provisions a serverless database, fronts it with Hyperdrive, and
23binds the connection into your Worker.
24
25Three providers are wired up out of the box. Pick whichever you
26prefer; the rest of the page follows your selection:
27
28- **Neon** — serverless Postgres with copy-on-write branching.
29- **PlanetScale (Postgres)** — managed Postgres with branch-per-PR
30 workflows.
31- **PlanetScale (MySQL)** — Vitess-backed MySQL, same branching model.
32
33## Add the provider
34
35Each database provider is its own `providers()` layer. Register it
36alongside `Cloudflare.providers()` in your stack:
37
38<Tabs syncKey="db-provider">
39 <TabItem label="Neon">
40
41```diff lang="typescript"
42// alchemy.run.ts
43import * as Alchemy from "alchemy";
44import * as Cloudflare from "alchemy/Cloudflare";
45+import * as Neon from "alchemy/Neon";
46+import * as Layer from "effect/Layer";
47
48export default Alchemy.Stack(
49 "MyStack",
50 {
51- providers: Cloudflare.providers(),
52+ providers: Layer.mergeAll(Cloudflare.providers(), Neon.providers()),
53 state: Alchemy.localState(),
54 },
55 // ...
56);
57```
58
59Neon's auth is API-key-based. After this change,
60`bun alchemy profile edit --add Neon` either reads `NEON_API_KEY`
61from the environment (good for CI) or stores a key under
62`~/.alchemy/credentials/<profile>/neon-stored.json`.
63
64 </TabItem>
65 <TabItem label="PlanetScale (Postgres)">
66
67```diff lang="typescript"
68// alchemy.run.ts
69import * as Alchemy from "alchemy";
70import * as Cloudflare from "alchemy/Cloudflare";
71+import * as Planetscale from "alchemy/Planetscale";
72+import * as Layer from "effect/Layer";
73
74export default Alchemy.Stack(
75 "MyStack",
76 {
77- providers: Cloudflare.providers(),
78+ providers: Layer.mergeAll(Cloudflare.providers(), Planetscale.providers()),
79 state: Alchemy.localState(),
80 },
81 // ...
82);
83```
84
85PlanetScale's auth uses an API token id + secret.
86`bun alchemy profile edit --add Planetscale` either reads `PLANETSCALE_API_TOKEN_ID`,
87`PLANETSCALE_API_TOKEN`, and `PLANETSCALE_ORGANIZATION` from the
88environment (good for CI) or stores the credentials under
89`~/.alchemy/credentials/<profile>/planetscale-stored.json`.
90
91 </TabItem>
92 <TabItem label="PlanetScale (MySQL)">
93
94```diff lang="typescript"
95// alchemy.run.ts
96import * as Alchemy from "alchemy";
97import * as Cloudflare from "alchemy/Cloudflare";
98+import * as Planetscale from "alchemy/Planetscale";
99+import * as Layer from "effect/Layer";
100
101export default Alchemy.Stack(
102 "MyStack",
103 {
104- providers: Cloudflare.providers(),
105+ providers: Layer.mergeAll(Cloudflare.providers(), Planetscale.providers()),
106 state: Alchemy.localState(),
107 },
108 // ...
109);
110```
111
112PlanetScale's auth uses an API token id + secret.
113`bun alchemy profile edit --add Planetscale` either reads `PLANETSCALE_API_TOKEN_ID`,
114`PLANETSCALE_API_TOKEN`, and `PLANETSCALE_ORGANIZATION` from the
115environment (good for CI) or stores the credentials under
116`~/.alchemy/credentials/<profile>/planetscale-stored.json`.
117
118 </TabItem>
119</Tabs>
120
121## Provision the database
122
123Create `src/Db.ts`. The shape is the same across providers — a
124top-level database resource, a branch, and (for PlanetScale) a
125credentials resource that owns the password.
126
127<Tabs syncKey="db-provider">
128 <TabItem label="Neon">
129
130A `Neon.Project` is the top-level container. It owns a default
131branch, a default role, and the WAL history that copy-on-write
132branches fork from:
133
134```typescript
135// src/Db.ts
136import * as Neon from "alchemy/Neon";
137import * as Effect from "effect/Effect";
138
139export const Db = Effect.gen(function* () {
140 const project = yield* Neon.Project("app-db", {
141 region: "aws-us-east-1",
142 });
143
144 const branch = yield* Neon.Branch("app-branch", {
145 project,
146 });
147
148 return { project, branch };
149});
150```
151
152`Neon.Branch` is a copy-on-write fork — cheap to create, fast to
153destroy, and ideal for preview environments. Each branch has its
154own connection string (`branch.connectionUri`) and a pooled variant
155(`branch.pooledConnectionUri`).
156
157 </TabItem>
158 <TabItem label="PlanetScale (Postgres)">
159
160A `Planetscale.PostgresDatabase` owns the long-lived cluster.
161`PostgresBranch` is a branch off of it (cheap to fork for previews),
162and `PostgresRole` materializes a user + password we can feed to
163Hyperdrive:
164
165```typescript
166// src/Db.ts
167import * as Planetscale from "alchemy/Planetscale";
168import * as Effect from "effect/Effect";
169
170export const Db = Effect.gen(function* () {
171 const database = yield* Planetscale.PostgresDatabase("app-db", {
172 region: { slug: "us-east" },
173 clusterSize: "PS_10",
174 });
175
176 const branch = yield* Planetscale.PostgresBranch("app-branch", {
177 database,
178 isProduction: false,
179 });
180
181 const role = yield* Planetscale.PostgresRole("app-role", {
182 database,
183 branch,
184 inheritedRoles: ["postgres"],
185 });
186
187 return { database, branch, role };
188});
189```
190
191The role's `origin` (host / port / database / user / password) is
192what Hyperdrive consumes — it's materialized lazily so the actual
193password never lands in plan output.
194
195 </TabItem>
196 <TabItem label="PlanetScale (MySQL)">
197
198A `Planetscale.MySQLDatabase` is the long-lived cluster.
199`MySQLBranch` is a branch off of it (cheap to fork for previews),
200and `MySQLPassword` materializes a user + password we can feed to
201Hyperdrive:
202
203```typescript
204// src/Db.ts
205import * as Planetscale from "alchemy/Planetscale";
206import * as Effect from "effect/Effect";
207
208export const Db = Effect.gen(function* () {
209 const database = yield* Planetscale.MySQLDatabase("app-db", {
210 region: { slug: "us-east" },
211 clusterSize: "PS_10",
212 allowForeignKeyConstraints: true,
213 });
214
215 const branch = yield* Planetscale.MySQLBranch("app-branch", {
216 database,
217 isProduction: false,
218 });
219
220 const password = yield* Planetscale.MySQLPassword("app-password", {
221 database,
222 branch,
223 role: "readwriter",
224 });
225
226 return { database, branch, password };
227});
228```
229
230The password's `origin` (host / port / database / user / password)
231is what Hyperdrive consumes — it's materialized lazily so the
232actual secret never lands in plan output.
233
234 </TabItem>
235</Tabs>
236
237## Put Hyperdrive in front
238
239Hyperdrive needs an `origin` describing where your database lives.
240Each provider exposes a pre-parsed `origin` output (`{ scheme, host,
241port, database, user, password }`) ready to feed straight in:
242
243<Tabs syncKey="db-provider">
244 <TabItem label="Neon">
245
246```diff lang="typescript"
247// src/Db.ts
248+import * as Cloudflare from "alchemy/Cloudflare";
249import * as Neon from "alchemy/Neon";
250import * as Effect from "effect/Effect";
251
252export const Db = Effect.gen(function* () {
253 const project = yield* Neon.Project("app-db", { region: "aws-us-east-1" });
254 const branch = yield* Neon.Branch("app-branch", { project });
255 return { project, branch };
256});
257
258+export const Hyperdrive = Effect.gen(function* () {
259+ const { branch } = yield* Db;
260+ return yield* Cloudflare.Hyperdrive.Connection("app-hyperdrive", {
261+ origin: branch.origin,
262+ });
263+});
264```
265
266`branch.origin` points at the **direct** (non-pooled) Neon endpoint —
267the recommended target when sitting behind Hyperdrive, since
268Hyperdrive already does its own connection pooling.
269
270 </TabItem>
271 <TabItem label="PlanetScale (Postgres)">
272
273```diff lang="typescript"
274// src/Db.ts
275+import * as Cloudflare from "alchemy/Cloudflare";
276import * as Planetscale from "alchemy/Planetscale";
277import * as Effect from "effect/Effect";
278
279export const Db = Effect.gen(function* () {
280 // ...
281 return { database, branch, role };
282});
283
284+export const Hyperdrive = Effect.gen(function* () {
285+ const { role } = yield* Db;
286+ return yield* Cloudflare.Hyperdrive.Connection("app-hyperdrive", {
287+ origin: role.origin,
288+ });
289+});
290```
291
292`role.origin` is the connection target Hyperdrive will pool. The
293`origin` is an `Output`, so alchemy orders PostgresDatabase →
294PostgresBranch → PostgresRole → Hyperdrive on the deploy graph.
295
296 </TabItem>
297 <TabItem label="PlanetScale (MySQL)">
298
299```diff lang="typescript"
300// src/Db.ts
301+import * as Cloudflare from "alchemy/Cloudflare";
302import * as Planetscale from "alchemy/Planetscale";
303import * as Effect from "effect/Effect";
304
305export const Db = Effect.gen(function* () {
306 // ...
307 return { database, branch, password };
308});
309
310+export const Hyperdrive = Effect.gen(function* () {
311+ const { password } = yield* Db;
312+ return yield* Cloudflare.Hyperdrive.Connection("app-hyperdrive", {
313+ origin: password.origin,
314+ });
315+});
316```
317
318`password.origin` is the connection target Hyperdrive will pool —
319its `scheme` is `"mysql"`, so Hyperdrive provisions a MySQL pooler.
320The `origin` is an `Output`, so alchemy orders MySQLDatabase →
321MySQLBranch → MySQLPassword → Hyperdrive on the deploy graph.
322
323 </TabItem>
324</Tabs>
325
326## Dev mode and the pooled origin
327
328In `bun alchemy dev`, alchemy doesn't create a Hyperdrive config at
329all — the local Worker gets a connection string that points straight
330at the database. With Hyperdrive out of the path, nothing pools
331those local connections, so point the `dev` override at the
332provider's **own** pooled endpoint instead:
333
334<Tabs syncKey="db-provider">
335 <TabItem label="Neon">
336
337```diff lang="typescript"
338// src/Db.ts
339export const Hyperdrive = Effect.gen(function* () {
340 const { branch } = yield* Db;
341 return yield* Cloudflare.Hyperdrive.Connection("app-hyperdrive", {
342 origin: branch.origin,
343+ dev: branch.pooledOrigin,
344 });
345});
346```
347
348`branch.pooledOrigin` is the same connection parsed against Neon's
349pgbouncer endpoint (the raw URI is `branch.pooledConnectionUri`).
350Deployed Workers still go through Hyperdrive at the direct endpoint;
351local dev goes through Neon's pooler.
352
353 </TabItem>
354 <TabItem label="PlanetScale (Postgres)">
355
356```diff lang="typescript"
357// src/Db.ts
358export const Hyperdrive = Effect.gen(function* () {
359 const { role } = yield* Db;
360 return yield* Cloudflare.Hyperdrive.Connection("app-hyperdrive", {
361 origin: role.origin,
362+ dev: role.pooledOrigin,
363 });
364});
365```
366
367`role.pooledOrigin` targets PSBouncer on port 6432 — the direct
368`role.origin` uses 5432 — and the raw pooled URL is available as
369`role.connectionUrlPooled`. Deployed Workers still go through
370Hyperdrive at the direct endpoint; local dev goes through
371PlanetScale's pooler.
372
373 </TabItem>
374 <TabItem label="PlanetScale (MySQL)">
375
376`MySQLPassword` has no pooled variant — when `dev` is omitted the
377binding falls back to `password.origin` automatically, so there is
378nothing to add. Set `dev` only when local dev should hit a
379*different* database, e.g. a MySQL running on `localhost`.
380
381 </TabItem>
382</Tabs>
383
384The pooled endpoint is also the right target when Hyperdrive isn't
385in the picture at all. A Hyperdrive connection string only exists
386inside a deployed Worker — the binding resolves it at runtime — so
387anything else that talks to the database (CI jobs, a container,
388`psql` on your laptop) should connect to the pooled URI directly:
389`branch.pooledConnectionUri` on Neon, `role.connectionUrlPooled` on
390PlanetScale. For a container that means passing the pooled URI in as
391an environment variable — see
392[Connect to a SQL database](/cloudflare/compute/containers#connect-to-a-sql-database).
393Just don't do the reverse — `origin` deliberately
394points Hyperdrive at the *direct* endpoint, because Hyperdrive
395already pools, and stacking it on top of the provider's pooler
396means two poolers fighting over the same connections.
397
398## Install the client library
399
400The Worker uses a Node-compatible driver to talk to Hyperdrive over
401the binding. Pick the one that matches your engine:
402
403<Tabs syncKey="db-provider">
404 <TabItem label="Neon">
405
406[`pg`](https://node-postgres.com) — node-postgres:
407
408<Tabs syncKey="pkgManager">
409 <TabItem label="bun" icon="bun">
410 <Code code={`bun add ${pgDeps}\nbun add -d ${pgDevDeps}`} lang="sh" />
411 </TabItem>
412 <TabItem label="npm" icon="npm">
413 <Code code={`npm install ${pgDeps}\nnpm install -D ${pgDevDeps}`} lang="sh" />
414 </TabItem>
415 <TabItem label="pnpm" icon="pnpm">
416 <Code code={`pnpm add ${pgDeps}\npnpm add -D ${pgDevDeps}`} lang="sh" />
417 </TabItem>
418 <TabItem label="yarn" icon="seti:yarn">
419 <Code code={`yarn add ${pgDeps}\nyarn add -D ${pgDevDeps}`} lang="sh" />
420 </TabItem>
421</Tabs>
422
423 </TabItem>
424 <TabItem label="PlanetScale (Postgres)">
425
426[`pg`](https://node-postgres.com) — node-postgres:
427
428<Tabs syncKey="pkgManager">
429 <TabItem label="bun" icon="bun">
430 <Code code={`bun add ${pgDeps}\nbun add -d ${pgDevDeps}`} lang="sh" />
431 </TabItem>
432 <TabItem label="npm" icon="npm">
433 <Code code={`npm install ${pgDeps}\nnpm install -D ${pgDevDeps}`} lang="sh" />
434 </TabItem>
435 <TabItem label="pnpm" icon="pnpm">
436 <Code code={`pnpm add ${pgDeps}\npnpm add -D ${pgDevDeps}`} lang="sh" />
437 </TabItem>
438 <TabItem label="yarn" icon="seti:yarn">
439 <Code code={`yarn add ${pgDeps}\nyarn add -D ${pgDevDeps}`} lang="sh" />
440 </TabItem>
441</Tabs>
442
443 </TabItem>
444 <TabItem label="PlanetScale (MySQL)">
445
446[`mysql2`](https://github.com/sidorares/node-mysql2):
447
448<Tabs syncKey="pkgManager">
449 <TabItem label="bun" icon="bun">
450 <Code code={`bun add ${mysqlDeps}`} lang="sh" />
451 </TabItem>
452 <TabItem label="npm" icon="npm">
453 <Code code={`npm install ${mysqlDeps}`} lang="sh" />
454 </TabItem>
455 <TabItem label="pnpm" icon="pnpm">
456 <Code code={`pnpm add ${mysqlDeps}`} lang="sh" />
457 </TabItem>
458 <TabItem label="yarn" icon="seti:yarn">
459 <Code code={`yarn add ${mysqlDeps}`} lang="sh" />
460 </TabItem>
461</Tabs>
462
463 </TabItem>
464</Tabs>
465
466## Bind Hyperdrive to your Worker
467
468`Cloudflare.Hyperdrive.Connect(...)` returns a typed accessor for the
469runtime binding — connection string, host, port, user, password,
470database — plus a `raw` escape hatch for libraries that want the
471underlying `Hyperdrive` object:
472
473<Tabs syncKey="db-provider">
474 <TabItem label="Neon">
475
476```diff lang="typescript"
477// src/Api.ts
478import * as Cloudflare from "alchemy/Cloudflare";
479import * as Effect from "effect/Effect";
480+import * as Redacted from "effect/Redacted";
481import * as HttpServerResponse from "effect/http/HttpServerResponse";
482+import { Client } from "pg";
483+import { Hyperdrive } from "./Db.ts";
484
485export default class Api extends Cloudflare.Worker<Api>()(
486 "Api",
487 {
488 main: import.meta.url,
489+ compatibility: {
490+ // node-postgres needs Node.js APIs to run inside a Worker.
491+ flags: ["nodejs_compat"],
492+ },
493 },
494 Effect.gen(function* () {
495+ const hd = yield* Cloudflare.Hyperdrive.Connect(Hyperdrive);
496+
497 return {
498 fetch: Effect.gen(function* () {
499+ const connectionString = Redacted.value(yield* hd.connectionString);
500+ const rows = yield* Effect.promise(async () => {
501+ // One client per request — Hyperdrive does the pooling.
502+ const client = new Client({ connectionString });
503+ await client.connect();
504+ try {
505+ const r = await client.query("SELECT now() as now");
506+ return r.rows;
507+ } finally {
508+ await client.end().catch(() => {});
509+ }
510+ });
511+ return yield* HttpServerResponse.json({ ok: true, rows });
512 }),
513 };
514- }),
515+ }).pipe(Effect.provide(Cloudflare.Hyperdrive.ConnectBinding)),
516) {}
517```
518
519 </TabItem>
520 <TabItem label="PlanetScale (Postgres)">
521
522```diff lang="typescript"
523// src/Api.ts
524import * as Cloudflare from "alchemy/Cloudflare";
525import * as Effect from "effect/Effect";
526+import * as Redacted from "effect/Redacted";
527import * as HttpServerResponse from "effect/http/HttpServerResponse";
528+import { Client } from "pg";
529+import { Hyperdrive } from "./Db.ts";
530
531export default class Api extends Cloudflare.Worker<Api>()(
532 "Api",
533 {
534 main: import.meta.url,
535+ compatibility: {
536+ // node-postgres needs Node.js APIs to run inside a Worker.
537+ flags: ["nodejs_compat"],
538+ },
539 },
540 Effect.gen(function* () {
541+ const hd = yield* Cloudflare.Hyperdrive.Connect(Hyperdrive);
542+
543 return {
544 fetch: Effect.gen(function* () {
545+ const connectionString = Redacted.value(yield* hd.connectionString);
546+ const rows = yield* Effect.promise(async () => {
547+ // One client per request — Hyperdrive does the pooling.
548+ const client = new Client({ connectionString });
549+ await client.connect();
550+ try {
551+ const r = await client.query("SELECT now() as now");
552+ return r.rows;
553+ } finally {
554+ await client.end().catch(() => {});
555+ }
556+ });
557+ return yield* HttpServerResponse.json({ ok: true, rows });
558 }),
559 };
560- }),
561+ }).pipe(Effect.provide(Cloudflare.Hyperdrive.ConnectBinding)),
562) {}
563```
564
565 </TabItem>
566 <TabItem label="PlanetScale (MySQL)">
567
568```diff lang="typescript"
569// src/Api.ts
570import * as Cloudflare from "alchemy/Cloudflare";
571import * as Effect from "effect/Effect";
572+import * as Redacted from "effect/Redacted";
573import * as HttpServerResponse from "effect/http/HttpServerResponse";
574+import { createConnection } from "mysql2/promise";
575+import { Hyperdrive } from "./Db.ts";
576
577export default class Api extends Cloudflare.Worker<Api>()(
578 "Api",
579 {
580 main: import.meta.url,
581+ compatibility: {
582+ // mysql2 needs Node.js APIs to run inside a Worker.
583+ flags: ["nodejs_compat"],
584+ },
585 },
586 Effect.gen(function* () {
587+ const hd = yield* Cloudflare.Hyperdrive.Connect(Hyperdrive);
588+
589 return {
590 fetch: Effect.gen(function* () {
591+ const connectionString = Redacted.value(yield* hd.connectionString);
592+ const rows = yield* Effect.promise(async () => {
593+ // One connection per request — Hyperdrive does the pooling.
594+ const conn = await createConnection(connectionString);
595+ try {
596+ const [r] = await conn.query("SELECT NOW() as now");
597+ return r;
598+ } finally {
599+ await conn.end().catch(() => {});
600+ }
601+ });
602+ return yield* HttpServerResponse.json({ ok: true, rows });
603 }),
604 };
605- }),
606+ }).pipe(Effect.provide(Cloudflare.Hyperdrive.ConnectBinding)),
607) {}
608```
609
610 </TabItem>
611</Tabs>
612
613Two things to notice (regardless of engine):
614
615- The `nodejs_compat` flag is required because the driver uses
616 Node's `net` and `crypto` APIs.
617- The fetch handler opens a fresh connection per request and ends
618 it on the way out. This is intentional — Hyperdrive does the
619 pooling on Cloudflare's side, so the Worker doesn't need its
620 own. [Effect SQL](#use-effect-sql) and [Drizzle](/cloudflare/data/drizzle)
621 manage a client per execution scope rather than retaining sockets across requests.
622
623## Wire the resources into the stack
624
625Update `alchemy.run.ts` to yield `Db` and `Hyperdrive` so they
626become part of the deploy graph:
627
628```diff lang="typescript"
629// alchemy.run.ts
630import Api from "./src/Api.ts";
631+import { Db, Hyperdrive } from "./src/Db.ts";
632
633export default Alchemy.Stack(
634 "MyStack",
635 { /* ... */ },
636 Effect.gen(function* () {
637+ yield* Db;
638+ yield* Hyperdrive;
639 const api = yield* Api;
640 return { url: api.url.as<string>() };
641 }),
642);
643```
644
645## Deploy
646
647```sh
648bun alchemy deploy
649```
650
651Watch the deploy plan: alchemy creates the database, then the
652branch, then the credentials (for PlanetScale), then the Hyperdrive
653that points at the connection origin, then the Worker with the
654Hyperdrive binding attached. After it completes, hit your Worker
655URL and you should see something like:
656
657```json
658{ "ok": true, "rows": [{ "now": "2026-05-04T07:53:48.123Z" }] }
659```
660
661You're now talking to your database from the edge through
662Hyperdrive.
663
664## Use Effect SQL
665
666```typescript
667// src/Api.ts
668import * as Cloudflare from "alchemy/Cloudflare";
669import * as SQL from "alchemy/SQL/Postgres";
670import * as Effect from "effect/Effect";
671import * as HttpServerResponse from "effect/http/HttpServerResponse";
672import { Hyperdrive } from "./Db.ts";
673
674export default class Api extends Cloudflare.Worker<Api>()(
675 "Api",
676 { main: import.meta.url, compatibilityFlags: ["nodejs_compat"] },
677 Effect.gen(function* () {
678 const connection = yield* Cloudflare.Hyperdrive.Connect(Hyperdrive);
679 const sql = yield* SQL.Postgres({ url: connection.connectionString });
680 return {
681 fetch: Effect.gen(function* () {
682 const rows = yield* sql`SELECT NOW() AS now`;
683 return yield* HttpServerResponse.json({ ok: true, rows });
684 }),
685 };
686 }).pipe(Effect.provide(Cloudflare.Hyperdrive.ConnectBinding)),
687) {}
688```
689
690Install `@effect/sql-pg` and `pg` for this Postgres variant. The binding and stack
691stay the same; Effect SQL opens and closes a pool for each execution scope.
692
693For a MySQL origin, install `@effect/sql-mysql2` and `mysql2`, import
694`alchemy/SQL/MySQL`, and construct `SQL.MySQL({ url: connection.connectionString })`.
695The [Postgres](/sql/effect-sql/postgres) and [MySQL](/sql/effect-sql/mysql) client
696guides cover queries, typed errors, and transactions.
697
698## Workers defaults
699
700`SQL.MySQL` and `Drizzle.MySQL` disable prepared statements and mysql2's eval-based
701row parsers when running in workerd. Hyperdrive's MySQL proxy uses the text
702protocol, and Workers prohibit runtime JavaScript compilation.
703
704```typescript
705const sql = yield* SQL.MySQL({
706 url: connection.connectionString,
707 disablePreparedStatements: true,
708 poolConfig: { disableEval: true },
709});
710```
711
712These are detected defaults, not required boilerplate. Explicit configuration
713wins; direct connections outside workerd retain prepared statements and normal
714parsers by default. The adapter parses URL fields and query parameters into the
715driver configuration so `poolConfig` options reach mysql2.
716
717## Where next
718
719Guides that build on Hyperdrive:
720
721- [Add Drizzle ORM](/cloudflare/data/drizzle) — swap the raw
722 driver for typed schemas and alchemy-managed migrations.
723- [Shared database across stages](/cloudflare/data/shared-database)
724 — point PR-preview stages at one long-lived database.
725- [Branch from a shared database](/cloudflare/data/branch-from-shared-database)
726 — copy-on-write branches per preview stage.
727
728Database integrations:
729
730- [PlanetScale](/planetscale)
731- [Neon](/neon)