@rdlabo/workers-hono-kit 0.11.0 → 0.11.1
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/dist/db/database.d.ts +32 -10
- package/dist/db/database.js +33 -20
- package/dist/db/index.d.ts +1 -1
- package/docs/api-db.md +3 -3
- package/docs/data-layer.md +2 -1
- package/package.json +1 -1
package/dist/db/database.d.ts
CHANGED
|
@@ -6,8 +6,8 @@ import type { HyperdriveLike } from './connection.js';
|
|
|
6
6
|
* @remarks
|
|
7
7
|
* The two sides of the database are deliberately handled differently:
|
|
8
8
|
*
|
|
9
|
-
* - Reads go to the **replica** as raw SQL (`QueryRunner.query`)
|
|
10
|
-
*
|
|
9
|
+
* - Reads go to the **replica** as raw SQL (`QueryRunner.query`) by default, returning plain rows.
|
|
10
|
+
* Hyperdrive-backed databases also expose `query()` for freshness-sensitive primary SELECTs.
|
|
11
11
|
* - Writes and transactions go to the **primary** through the Drizzle ORM for type safety, but only
|
|
12
12
|
* via `write(fn)` / `transaction(fn)` — the raw query builder is never exposed. The builder is
|
|
13
13
|
* awaited inside those methods, which removes a foot-gun: a Drizzle builder is a lazy thenable, so
|
|
@@ -99,6 +99,28 @@ export interface DisposableDatabase<TDrizzle, TTx = TxOf<TDrizzle>> extends Data
|
|
|
99
99
|
*/
|
|
100
100
|
dispose(): Promise<void>;
|
|
101
101
|
}
|
|
102
|
+
/**
|
|
103
|
+
* A Hyperdrive-backed database with an explicit primary query path.
|
|
104
|
+
*
|
|
105
|
+
* @remarks
|
|
106
|
+
* Use `query()` only for SELECTs that require read-after-write consistency or cannot use the
|
|
107
|
+
* configured replica. Fatal connection errors recreate the primary connection and repeat the
|
|
108
|
+
* SELECT at most once. Writes and transactions are never repeated for connection errors.
|
|
109
|
+
*
|
|
110
|
+
* @typeParam TDrizzle - the consumer's Drizzle ORM type.
|
|
111
|
+
* @typeParam TTx - the transaction-handle type, inferred from `TDrizzle` by default.
|
|
112
|
+
*/
|
|
113
|
+
export interface HyperdriveDatabase<TDrizzle, TTx = TxOf<TDrizzle>> extends DisposableDatabase<TDrizzle, TTx> {
|
|
114
|
+
/**
|
|
115
|
+
* Run a raw SQL SELECT against the primary database.
|
|
116
|
+
*
|
|
117
|
+
* @typeParam T - the complete rows result type (for example, `User[]`).
|
|
118
|
+
* @param sql - the SQL text, with `?` placeholders for `params`.
|
|
119
|
+
* @param params - optional positional parameters.
|
|
120
|
+
* @returns the rows returned by the query, typed as `T`.
|
|
121
|
+
*/
|
|
122
|
+
query<T = unknown>(sql: string, params?: unknown[]): Promise<T>;
|
|
123
|
+
}
|
|
102
124
|
/**
|
|
103
125
|
* Options for {@link createMysqlDatabase}.
|
|
104
126
|
*
|
|
@@ -156,19 +178,19 @@ export interface CreateHyperdriveDatabaseOptions<TDrizzle> {
|
|
|
156
178
|
connectionOptions?: Record<string, unknown>;
|
|
157
179
|
}
|
|
158
180
|
/**
|
|
159
|
-
* Create a {@link
|
|
181
|
+
* Create a {@link HyperdriveDatabase} that lazily opens its connections from Hyperdrive bindings.
|
|
160
182
|
*
|
|
161
183
|
* @remarks
|
|
162
184
|
* Construct one per request. Connections and the ORM are created on first use and reused for the
|
|
163
|
-
* lifetime of the instance
|
|
164
|
-
*
|
|
165
|
-
*
|
|
166
|
-
*
|
|
167
|
-
*
|
|
185
|
+
* lifetime of the instance. `read()` uses the replica, while `query()` provides an explicit
|
|
186
|
+
* primary SELECT path. Workers automatically cleans up connections at the end of the invocation,
|
|
187
|
+
* so callers do not need to close them manually. Either SELECT path is repeated once on a fresh
|
|
188
|
+
* connection after a fatal mysql2 connection error. Writes and transactions are never repeated
|
|
189
|
+
* for connection errors because their commit state can be ambiguous.
|
|
168
190
|
*
|
|
169
191
|
* @typeParam TDrizzle - the consumer's Drizzle ORM type.
|
|
170
192
|
* @param options - the primary/replica Hyperdrive bindings, the ORM factory, and connection options.
|
|
171
|
-
* @returns a {@link
|
|
193
|
+
* @returns a {@link HyperdriveDatabase} whose compatibility `dispose()` method is a no-op; Workers
|
|
172
194
|
* cleans up its invocation-scoped connections automatically.
|
|
173
195
|
* @example
|
|
174
196
|
* ```ts
|
|
@@ -180,7 +202,7 @@ export interface CreateHyperdriveDatabaseOptions<TDrizzle> {
|
|
|
180
202
|
* await db.write((dz) => dz.insert(users).values(user));
|
|
181
203
|
* ```
|
|
182
204
|
*/
|
|
183
|
-
export declare function createHyperdriveDatabase<TDrizzle>(options: CreateHyperdriveDatabaseOptions<TDrizzle>):
|
|
205
|
+
export declare function createHyperdriveDatabase<TDrizzle>(options: CreateHyperdriveDatabaseOptions<TDrizzle>): HyperdriveDatabase<TDrizzle>;
|
|
184
206
|
/**
|
|
185
207
|
* Internal helper that assembles a {@link Database} from an ORM and a replica connection.
|
|
186
208
|
*
|
package/dist/db/database.js
CHANGED
|
@@ -24,19 +24,19 @@ export function createMysqlDatabase(options) {
|
|
|
24
24
|
return databaseFrom(options.orm, options.replica);
|
|
25
25
|
}
|
|
26
26
|
/**
|
|
27
|
-
* Create a {@link
|
|
27
|
+
* Create a {@link HyperdriveDatabase} that lazily opens its connections from Hyperdrive bindings.
|
|
28
28
|
*
|
|
29
29
|
* @remarks
|
|
30
30
|
* Construct one per request. Connections and the ORM are created on first use and reused for the
|
|
31
|
-
* lifetime of the instance
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
31
|
+
* lifetime of the instance. `read()` uses the replica, while `query()` provides an explicit
|
|
32
|
+
* primary SELECT path. Workers automatically cleans up connections at the end of the invocation,
|
|
33
|
+
* so callers do not need to close them manually. Either SELECT path is repeated once on a fresh
|
|
34
|
+
* connection after a fatal mysql2 connection error. Writes and transactions are never repeated
|
|
35
|
+
* for connection errors because their commit state can be ambiguous.
|
|
36
36
|
*
|
|
37
37
|
* @typeParam TDrizzle - the consumer's Drizzle ORM type.
|
|
38
38
|
* @param options - the primary/replica Hyperdrive bindings, the ORM factory, and connection options.
|
|
39
|
-
* @returns a {@link
|
|
39
|
+
* @returns a {@link HyperdriveDatabase} whose compatibility `dispose()` method is a no-op; Workers
|
|
40
40
|
* cleans up its invocation-scoped connections automatically.
|
|
41
41
|
* @example
|
|
42
42
|
* ```ts
|
|
@@ -60,20 +60,33 @@ export function createHyperdriveDatabase(options) {
|
|
|
60
60
|
const [rows] = (await (await connection).query(sql, params));
|
|
61
61
|
return rows;
|
|
62
62
|
});
|
|
63
|
+
const readWithRecovery = async (connectionFor, reset, sql, params) => {
|
|
64
|
+
const connection = connectionFor();
|
|
65
|
+
const outcome = await readFrom(connection, sql, params).then((rows) => ({ ok: true, rows }), (error) => ({ ok: false, error }));
|
|
66
|
+
if (outcome.ok) {
|
|
67
|
+
return outcome.rows;
|
|
68
|
+
}
|
|
69
|
+
if (!isFatalConnectionError(outcome.error)) {
|
|
70
|
+
throw outcome.error;
|
|
71
|
+
}
|
|
72
|
+
reset(connection);
|
|
73
|
+
return readFrom(connectionFor(), sql, params);
|
|
74
|
+
};
|
|
63
75
|
return {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
76
|
+
read(sql, params = []) {
|
|
77
|
+
return readWithRecovery(replica, (failedConnection) => {
|
|
78
|
+
if (replicaConn === failedConnection) {
|
|
79
|
+
replicaConn = undefined;
|
|
80
|
+
}
|
|
81
|
+
}, sql, params);
|
|
82
|
+
},
|
|
83
|
+
query(sql, params = []) {
|
|
84
|
+
return readWithRecovery(primary, (failedConnection) => {
|
|
85
|
+
if (primaryConn === failedConnection) {
|
|
86
|
+
primaryConn = undefined;
|
|
87
|
+
orm = undefined;
|
|
88
|
+
}
|
|
89
|
+
}, sql, params);
|
|
77
90
|
},
|
|
78
91
|
async write(fn) {
|
|
79
92
|
const dz = await ormFor();
|
package/dist/db/index.d.ts
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
export { retryWhenDeadlock } from './retry.js';
|
|
13
13
|
export { createMysqlDatabase, createHyperdriveDatabase, databaseFrom } from './database.js';
|
|
14
|
-
export type { Database, DisposableDatabase, QueryRunner, TxOf, CreateMysqlDatabaseOptions, CreateHyperdriveDatabaseOptions, Connection, Pool, } from './database.js';
|
|
14
|
+
export type { Database, DisposableDatabase, HyperdriveDatabase, QueryRunner, TxOf, CreateMysqlDatabaseOptions, CreateHyperdriveDatabaseOptions, Connection, Pool, } from './database.js';
|
|
15
15
|
export { insertIdOf, affectedRowsOf, insertedIdsOf } from './write-result.js';
|
|
16
16
|
export type { DzWriteResult } from './write-result.js';
|
|
17
17
|
export { hyperdriveConnectionOptions, withMysqlConnections } from './connection.js';
|
package/docs/api-db.md
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
# API: `@rdlabo/workers-hono-kit/db`
|
|
2
2
|
|
|
3
|
-
Requires the `drizzle-orm` and `mysql2` peers. Reads
|
|
3
|
+
Requires the `drizzle-orm` and `mysql2` peers. Reads use raw SQL against the replica by default, with an explicit primary read path for freshness; writes/transactions run against the primary through the Drizzle ORM with deadlock retry. The kit deliberately does not depend on the ORM's type identity — you pass the Drizzle instance in.
|
|
4
4
|
|
|
5
5
|
| Export | Description |
|
|
6
6
|
| --- | --- |
|
|
7
|
-
| `createHyperdriveDatabase(options)` | `
|
|
7
|
+
| `createHyperdriveDatabase(options)` | `HyperdriveDatabase` that lazily opens primary/replica connections from Hyperdrive bindings per request. `read()` targets the replica and `query()` targets the primary; either SELECT is repeated at most once on a fresh connection after a fatal mysql2 connection error. Writes and transactions are not repeated. Workers cleans connections up at invocation end; the legacy `dispose()` is a no-op. |
|
|
8
8
|
| `createMysqlDatabase(options)` | Assemble a `Database` from an already-connected Drizzle ORM + replica `QueryRunner`. |
|
|
9
9
|
| `databaseFrom(orm, replica)` | Build a `Database` from an existing Drizzle instance + replica handle. |
|
|
10
|
-
| `Database` / `DisposableDatabase` / `QueryRunner` / `TxOf` | The `read` / `write` / `transaction` API and its supporting types. |
|
|
10
|
+
| `Database` / `DisposableDatabase` / `HyperdriveDatabase` / `QueryRunner` / `TxOf` | The `read` / `query` / `write` / `transaction` API and its supporting types. |
|
|
11
11
|
| `hyperdriveConnectionOptions(hyperdrive, overrides?)` / `HyperdriveLike` / `ExecutionContextLike` | Build mysql2 `createConnection` options from a Hyperdrive binding (`disableEval`, `decimalNumbers`, `timezone '+09:00'` by default). `timezone` controls mysql2's JavaScript `Date` conversion; it does not change the MySQL session timezone. |
|
|
12
12
|
| `withMysqlConnections(...)` | Open primary/replica connections in parallel and run a function. Workers cleans them up at invocation end. |
|
|
13
13
|
| `retryWhenDeadlock(fn, retries?, delay?)` | Same deadlock-retry helper as the root export. |
|
package/docs/data-layer.md
CHANGED
|
@@ -2,7 +2,7 @@ Import database helpers from `@rdlabo/workers-hono-kit/db`. This entry point req
|
|
|
2
2
|
|
|
3
3
|
## Hyperdrive database
|
|
4
4
|
|
|
5
|
-
`createHyperdriveDatabase()` lazily opens primary and replica connections from Hyperdrive bindings.
|
|
5
|
+
`createHyperdriveDatabase()` lazily opens primary and replica connections from Hyperdrive bindings. `read()` uses the replica query runner; `query()` provides an explicit raw primary SELECT for read-after-write consistency; writes and transactions use the primary Drizzle instance. After a fatal mysql2 connection error, either read path opens a fresh connection and repeats the SELECT at most once. Writes and transactions are not repeated because their commit state may be ambiguous. Workers owns connection cleanup at invocation end.
|
|
6
6
|
|
|
7
7
|
```ts
|
|
8
8
|
import { createHyperdriveDatabase } from '@rdlabo/workers-hono-kit/db';
|
|
@@ -15,6 +15,7 @@ const db = createHyperdriveDatabase({
|
|
|
15
15
|
});
|
|
16
16
|
|
|
17
17
|
const rows = await db.read<Item>('SELECT * FROM items WHERE id = ?', [id]);
|
|
18
|
+
const freshRows = await db.query<Item[]>('SELECT * FROM items WHERE id = ?', [id]);
|
|
18
19
|
await db.write((dz) => dz.insert(items).values(input));
|
|
19
20
|
await db.transaction((tx) => tx.insert(items).values(input));
|
|
20
21
|
```
|