@nwire/drizzle 0.10.0
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/LICENSE +21 -0
- package/README.md +69 -0
- package/dist/data-drizzle.d.ts +81 -0
- package/dist/data-drizzle.js +60 -0
- package/package.json +51 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Alex Gefter / 200apps Ltd.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# @nwire/drizzle
|
|
2
|
+
|
|
3
|
+
> Drizzle ORM provider — lifecycle-managed `db` client handlers import directly.
|
|
4
|
+
|
|
5
|
+
> See: [write paths (actor xor direct-DB)](https://nwire.dev/concepts/write-paths) — direct-DB is one of two write paths; do not mix it with `ctx.use(Actor, id)` in the same handler.
|
|
6
|
+
|
|
7
|
+
## What it does
|
|
8
|
+
|
|
9
|
+
Boots a Drizzle ORM client as a Nwire provider so handlers can use the _real_ Drizzle API (full end-to-end TypeScript inference) without DI ceremony. Pluggable health check + telemetry emit on every query. Pairs with [pglite](https://github.com/electric-sql/pglite) for zero-Docker tests.
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pnpm add @nwire/drizzle drizzle-orm
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Quick start
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
import { drizzle } from "drizzle-orm/node-postgres";
|
|
21
|
+
import { Pool } from "pg";
|
|
22
|
+
import { drizzleProvider } from "@nwire/drizzle";
|
|
23
|
+
import { defineApp, defineAction } from "@nwire/forge";
|
|
24
|
+
import * as schema from "./schema.js";
|
|
25
|
+
import { db } from "./db.js";
|
|
26
|
+
|
|
27
|
+
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
|
|
28
|
+
export const drizzleDb = drizzle(pool, { schema });
|
|
29
|
+
|
|
30
|
+
defineApp("my-app", {
|
|
31
|
+
modules: [...],
|
|
32
|
+
providers: [drizzleProvider({ db: drizzleDb, close: () => pool.end() })],
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
// In a handler — import db directly:
|
|
36
|
+
defineAction({
|
|
37
|
+
name: "users.create",
|
|
38
|
+
handler: async ({ input }) => {
|
|
39
|
+
const [user] = await db.insert(users).values(input).returning();
|
|
40
|
+
return user;
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## API surface
|
|
46
|
+
|
|
47
|
+
- `drizzleProvider({ db, close?, check? })` — provider definition; lifecycle-managed.
|
|
48
|
+
|
|
49
|
+
## See also
|
|
50
|
+
|
|
51
|
+
- [Plain CRUD with Drizzle](https://nwire.dev/recipes/data-drizzle) — wiring this provider into an app.
|
|
52
|
+
- [Drizzle migrations + seeds](https://nwire.dev/recipes/drizzle-migrations-seeds) — authoring migrations, boot-vs-deploy, seeds, tests, production checklist.
|
|
53
|
+
|
|
54
|
+
## When to use
|
|
55
|
+
|
|
56
|
+
When you want a typed SQL layer with end-to-end inference.Compose with `@nwire/auth-better-auth`
|
|
57
|
+
|
|
58
|
+
## Within nwire-app
|
|
59
|
+
|
|
60
|
+
For developers using this package as part of the Nwire stack — register it via `app.use(...)` or it auto-wires when you compose `createApp({ modules })`.
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
import { createApp } from "@nwire/forge";
|
|
64
|
+
|
|
65
|
+
const app = createApp({
|
|
66
|
+
/* ...config... */
|
|
67
|
+
});
|
|
68
|
+
// Adapter/plugin wiring happens here when applicable.
|
|
69
|
+
```
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@nwire/drizzle` — plugin that lifecycle-manages a Drizzle ORM
|
|
3
|
+
* client so application handlers can use the *real* Drizzle API directly.
|
|
4
|
+
*
|
|
5
|
+
* import { drizzlePlugin } from "@nwire/drizzle";
|
|
6
|
+
* import { drizzle } from "drizzle-orm/node-postgres";
|
|
7
|
+
* import { Pool } from "pg";
|
|
8
|
+
* import * as schema from "./schema";
|
|
9
|
+
*
|
|
10
|
+
* const pool = new Pool({ connectionString: process.env.DATABASE_URL });
|
|
11
|
+
* const db = drizzle(pool, { schema });
|
|
12
|
+
*
|
|
13
|
+
* defineApp("my-app", {
|
|
14
|
+
* modules: [...],
|
|
15
|
+
* plugins: [drizzlePlugin({ db, close: () => pool.end() })],
|
|
16
|
+
* });
|
|
17
|
+
*
|
|
18
|
+
* // In a handler — import db directly, no DI ceremony:
|
|
19
|
+
* import { db } from "../db";
|
|
20
|
+
*
|
|
21
|
+
* defineAction({
|
|
22
|
+
* name: "users.create",
|
|
23
|
+
* handler: async (input) => {
|
|
24
|
+
* const [user] = await db.insert(users).values(input).returning();
|
|
25
|
+
* return UserWasRegistered({ userId: user.id });
|
|
26
|
+
* },
|
|
27
|
+
* });
|
|
28
|
+
*
|
|
29
|
+
* Why "import db directly" instead of `ctx.resolve('db')`:
|
|
30
|
+
* - Drizzle's strength is end-to-end TypeScript inference. The schema
|
|
31
|
+
* types flow into the query builder. Going through DI erases that.
|
|
32
|
+
* - For tests / multi-tenancy where you need a different client, the
|
|
33
|
+
* same instance is also registered on the container under the
|
|
34
|
+
* plugin's name (default: `"db"`) — `ctx.resolve("db")` works.
|
|
35
|
+
* - This is the same recommendation Drizzle's own docs make.
|
|
36
|
+
*
|
|
37
|
+
* What the plugin gives you:
|
|
38
|
+
* 1. Lifecycle — graceful pool close during shutdown.
|
|
39
|
+
* 2. DI registration — same instance accessible via `ctx.resolve(name)`.
|
|
40
|
+
*
|
|
41
|
+
* Health checks belong on the endpoint, not the plugin. Use
|
|
42
|
+
* `endpoint({ probes: { checks: [{ name: "db", check: () => db.execute(...) }] } })`
|
|
43
|
+
* to wire a readiness probe — see the `station-mgmt-drizzle` recipe.
|
|
44
|
+
*/
|
|
45
|
+
import { type PluginDefinition } from "@nwire/app";
|
|
46
|
+
/**
|
|
47
|
+
* Minimum shape we need from a Drizzle client. Kept structural so we don't
|
|
48
|
+
* have to take a hard dep on a specific drizzle driver (node-postgres,
|
|
49
|
+
* mysql2, libsql, neon, planetscale, …). The user's `db` is whatever
|
|
50
|
+
* `drizzle(...)` returned.
|
|
51
|
+
*/
|
|
52
|
+
export type DrizzleClient = Record<string, any>;
|
|
53
|
+
export interface DrizzlePluginOptions<TDb extends DrizzleClient> {
|
|
54
|
+
/**
|
|
55
|
+
* The Drizzle client instance — what `drizzle(pool, { schema })` returns.
|
|
56
|
+
* Pass it in pre-configured; we don't construct it for you so the user
|
|
57
|
+
* keeps full control of driver choice + schema typing.
|
|
58
|
+
*/
|
|
59
|
+
readonly db: TDb;
|
|
60
|
+
/**
|
|
61
|
+
* Name the client is registered under on the container. Default: `"db"`.
|
|
62
|
+
* Use a different name when running multiple databases (e.g. `"primary"`,
|
|
63
|
+
* `"analytics"`).
|
|
64
|
+
*/
|
|
65
|
+
readonly name?: string;
|
|
66
|
+
/**
|
|
67
|
+
* Called during shutdown to close the underlying connection pool.
|
|
68
|
+
* Examples:
|
|
69
|
+
* - node-postgres: `() => pool.end()`
|
|
70
|
+
* - mysql2: `() => pool.end()`
|
|
71
|
+
* - better-sqlite3:`() => sqlite.close()`
|
|
72
|
+
* - libsql / neon / planetscale: usually a no-op (HTTP-backed)
|
|
73
|
+
* Optional — omit for drivers that don't need cleanup.
|
|
74
|
+
*/
|
|
75
|
+
readonly close?: () => Promise<void> | void;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Build a Nwire plugin that registers a Drizzle client on the container
|
|
79
|
+
* and tears down its connection pool on shutdown.
|
|
80
|
+
*/
|
|
81
|
+
export declare function drizzlePlugin<TDb extends DrizzleClient>(options: DrizzlePluginOptions<TDb>): PluginDefinition;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@nwire/drizzle` — plugin that lifecycle-manages a Drizzle ORM
|
|
3
|
+
* client so application handlers can use the *real* Drizzle API directly.
|
|
4
|
+
*
|
|
5
|
+
* import { drizzlePlugin } from "@nwire/drizzle";
|
|
6
|
+
* import { drizzle } from "drizzle-orm/node-postgres";
|
|
7
|
+
* import { Pool } from "pg";
|
|
8
|
+
* import * as schema from "./schema";
|
|
9
|
+
*
|
|
10
|
+
* const pool = new Pool({ connectionString: process.env.DATABASE_URL });
|
|
11
|
+
* const db = drizzle(pool, { schema });
|
|
12
|
+
*
|
|
13
|
+
* defineApp("my-app", {
|
|
14
|
+
* modules: [...],
|
|
15
|
+
* plugins: [drizzlePlugin({ db, close: () => pool.end() })],
|
|
16
|
+
* });
|
|
17
|
+
*
|
|
18
|
+
* // In a handler — import db directly, no DI ceremony:
|
|
19
|
+
* import { db } from "../db";
|
|
20
|
+
*
|
|
21
|
+
* defineAction({
|
|
22
|
+
* name: "users.create",
|
|
23
|
+
* handler: async (input) => {
|
|
24
|
+
* const [user] = await db.insert(users).values(input).returning();
|
|
25
|
+
* return UserWasRegistered({ userId: user.id });
|
|
26
|
+
* },
|
|
27
|
+
* });
|
|
28
|
+
*
|
|
29
|
+
* Why "import db directly" instead of `ctx.resolve('db')`:
|
|
30
|
+
* - Drizzle's strength is end-to-end TypeScript inference. The schema
|
|
31
|
+
* types flow into the query builder. Going through DI erases that.
|
|
32
|
+
* - For tests / multi-tenancy where you need a different client, the
|
|
33
|
+
* same instance is also registered on the container under the
|
|
34
|
+
* plugin's name (default: `"db"`) — `ctx.resolve("db")` works.
|
|
35
|
+
* - This is the same recommendation Drizzle's own docs make.
|
|
36
|
+
*
|
|
37
|
+
* What the plugin gives you:
|
|
38
|
+
* 1. Lifecycle — graceful pool close during shutdown.
|
|
39
|
+
* 2. DI registration — same instance accessible via `ctx.resolve(name)`.
|
|
40
|
+
*
|
|
41
|
+
* Health checks belong on the endpoint, not the plugin. Use
|
|
42
|
+
* `endpoint({ probes: { checks: [{ name: "db", check: () => db.execute(...) }] } })`
|
|
43
|
+
* to wire a readiness probe — see the `station-mgmt-drizzle` recipe.
|
|
44
|
+
*/
|
|
45
|
+
import { definePlugin } from "@nwire/app";
|
|
46
|
+
/**
|
|
47
|
+
* Build a Nwire plugin that registers a Drizzle client on the container
|
|
48
|
+
* and tears down its connection pool on shutdown.
|
|
49
|
+
*/
|
|
50
|
+
export function drizzlePlugin(options) {
|
|
51
|
+
const name = options.name ?? "db";
|
|
52
|
+
return definePlugin(`drizzle:${name}`, ({ bind, dispose }) => {
|
|
53
|
+
bind(name, options.db);
|
|
54
|
+
if (options.close) {
|
|
55
|
+
dispose(async () => {
|
|
56
|
+
await options.close();
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@nwire/drizzle",
|
|
3
|
+
"version": "0.10.0",
|
|
4
|
+
"description": "Nwire — Drizzle ORM adapter. Provider that registers a Drizzle client on the container, manages the connection pool lifecycle, and exposes optional readiness checks + telemetry. No Repository wrapper — handlers get the real Drizzle API.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"drizzle",
|
|
7
|
+
"mysql",
|
|
8
|
+
"nwire",
|
|
9
|
+
"orm",
|
|
10
|
+
"postgres",
|
|
11
|
+
"provider",
|
|
12
|
+
"sqlite"
|
|
13
|
+
],
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"README.md",
|
|
18
|
+
"LICENSE"
|
|
19
|
+
],
|
|
20
|
+
"type": "module",
|
|
21
|
+
"main": "./dist/data-drizzle.js",
|
|
22
|
+
"types": "./dist/data-drizzle.d.ts",
|
|
23
|
+
"exports": {
|
|
24
|
+
".": {
|
|
25
|
+
"import": "./dist/data-drizzle.js",
|
|
26
|
+
"types": "./dist/data-drizzle.d.ts"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"publishConfig": {
|
|
30
|
+
"access": "public"
|
|
31
|
+
},
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"@nwire/app": "0.10.0",
|
|
34
|
+
"@nwire/forge": "0.10.0"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@electric-sql/pglite": "^0.4.5",
|
|
38
|
+
"@types/node": "^22.19.9",
|
|
39
|
+
"drizzle-orm": "^0.45.2",
|
|
40
|
+
"typescript": "^5.9.3",
|
|
41
|
+
"vitest": "^4.1.6"
|
|
42
|
+
},
|
|
43
|
+
"peerDependencies": {
|
|
44
|
+
"drizzle-orm": ">=0.40.0"
|
|
45
|
+
},
|
|
46
|
+
"scripts": {
|
|
47
|
+
"build": "tsc && node ../../scripts/fix-dist-extensions.mjs dist",
|
|
48
|
+
"dev": "tsc --watch",
|
|
49
|
+
"typecheck": "tsc --noEmit"
|
|
50
|
+
}
|
|
51
|
+
}
|