prisma-extension-kysely 1.0.4 → 2.0.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/README.md +87 -16
- package/dist/connection.d.ts +11 -0
- package/dist/connection.js +47 -0
- package/dist/driver.d.ts +13 -0
- package/dist/driver.js +42 -0
- package/dist/index.d.ts +15 -21
- package/dist/index.js +45 -26
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -9,6 +9,16 @@
|
|
|
9
9
|
|
|
10
10
|
Writing and maintaining raw SQL queries for Prisma can be a tedious and error-prone task. The moment you need to write a query that is not supported out-of-the-box by Prisma, you lose all of that type-safety and autocompletion. This is where `prisma-extension-kysely` comes in! It allows you to easily write raw SQL queries in a type-safe manner with [`kysely`](https://kysely.dev/) and integrate them seamlessly with Prisma.
|
|
11
11
|
|
|
12
|
+
And the best part? You can use all of your favorite [`kysely`](https://kysely.dev/) plugins with `prisma-extension-kysely` too!
|
|
13
|
+
|
|
14
|
+
You don't have to take our word for it, though:
|
|
15
|
+
|
|
16
|
+
> I have to say, this is BY FAR the most amazing community package I've seen in the Prisma ecosystem!
|
|
17
|
+
>
|
|
18
|
+
> It makes it so much more convenient to drop down to raw SQL when needed without sacrificing DX — best of both worlds! 🚀
|
|
19
|
+
|
|
20
|
+
— [Nikolas Burk, DevRel @ Prisma](https://twitter.com/nikolasburk/status/1747901827960471699)
|
|
21
|
+
|
|
12
22
|
## Features
|
|
13
23
|
|
|
14
24
|
- **Type-safe** — Write raw SQL queries in a type-safe manner with `kysely`
|
|
@@ -52,17 +62,22 @@ Extend your Prisma Client:
|
|
|
52
62
|
import kyselyExtension from "prisma-extension-kysely";
|
|
53
63
|
import type { DB } from "./prisma/generated/types";
|
|
54
64
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
65
|
+
const prisma = new PrismaClient().$extends(
|
|
66
|
+
kyselyExtension({
|
|
67
|
+
kysely: (driver) =>
|
|
68
|
+
new Kysely<DB>({
|
|
69
|
+
dialect: {
|
|
70
|
+
// This is where the magic happens!
|
|
71
|
+
createDriver: () => driver,
|
|
72
|
+
// Don't forget to customize these to match your database!
|
|
73
|
+
createAdapter: () => new PostgresAdapter(),
|
|
74
|
+
createIntrospector: (db) => new PostgresIntrospector(db),
|
|
75
|
+
createQueryCompiler: () => new PostgresQueryCompiler(),
|
|
76
|
+
},
|
|
77
|
+
plugins: [new CamelCasePlugin()],
|
|
78
|
+
}),
|
|
79
|
+
}),
|
|
80
|
+
);
|
|
66
81
|
```
|
|
67
82
|
|
|
68
83
|
It's that simple! Now you can write raw SQL queries with `kysely` and use them with Prisma:
|
|
@@ -78,20 +93,76 @@ const query = prisma.$kysely
|
|
|
78
93
|
.where("id", "=", id);
|
|
79
94
|
|
|
80
95
|
// Thanks to kysely's magic, everything is type-safe!
|
|
81
|
-
const result = await
|
|
96
|
+
const result = await query.execute();
|
|
82
97
|
|
|
83
98
|
// You can also execute queries without fetching the results
|
|
84
|
-
await prisma.$
|
|
85
|
-
|
|
99
|
+
await prisma.$kysely.deleteFrom("User").where("id", "=", id).execute();
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Transactions
|
|
103
|
+
|
|
104
|
+
Prisma's interactive transactions are fully supported by `prisma-extension-kysely`! Just remeber to use `tx.$kysely` instead of `prisma.$kysely`, and you're good to go:
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
await prisma.$transaction(async (tx) => {
|
|
108
|
+
await tx.$kysely
|
|
109
|
+
.insertInto("User")
|
|
110
|
+
.values({ id: 1, name: "John Doe" })
|
|
111
|
+
.execute();
|
|
112
|
+
|
|
113
|
+
await tx.$kysely
|
|
114
|
+
.insertInto("User")
|
|
115
|
+
.values({ id: 2, name: "Jane Doe" })
|
|
116
|
+
.execute();
|
|
117
|
+
});
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Don't try to use Kysely's `transaction` method directly, though. It's not supported by `prisma-extension-kysely`, and it will throw an error if you try to use it.
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
// Don't do this! Prefer prisma.$transaction instead.
|
|
124
|
+
await prisma.$kysely.transaction().execute(async (trx) => {});
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Plugins
|
|
128
|
+
|
|
129
|
+
Do you love Kysely's plugins? So do we! You can use them with `prisma-extension-kysely` as well:
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
const prisma = new PrismaClient().$extends(
|
|
133
|
+
kyselyExtension({
|
|
134
|
+
kysely: (driver) =>
|
|
135
|
+
new Kysely<DB>({
|
|
136
|
+
dialect: {
|
|
137
|
+
createDriver: () => driver,
|
|
138
|
+
createAdapter: () => new PostgresAdapter(),
|
|
139
|
+
createIntrospector: (db) => new PostgresIntrospector(db),
|
|
140
|
+
createQueryCompiler: () => new PostgresQueryCompiler(),
|
|
141
|
+
},
|
|
142
|
+
// Use your favorite plugins!
|
|
143
|
+
plugins: [new CamelCasePlugin()],
|
|
144
|
+
}),
|
|
145
|
+
}),
|
|
86
146
|
);
|
|
87
147
|
```
|
|
88
148
|
|
|
149
|
+
If you're using the `CamelCasePlugin`, don't forget to add the `camelCase` option to your Prisma schema too:
|
|
150
|
+
|
|
151
|
+
```prisma
|
|
152
|
+
generator kysely {
|
|
153
|
+
provider = "prisma-kysely"
|
|
154
|
+
camelCase = true
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Take a look at [the camel case example](examples/camel-case/) to see it in action! Check out the [Kysely documentation](https://kysely.dev/) for more information about plugins.
|
|
159
|
+
|
|
89
160
|
## Examples
|
|
90
161
|
|
|
91
|
-
Check out the [
|
|
162
|
+
Check out the [examples](examples) directory for a sample project!
|
|
92
163
|
|
|
93
164
|
```shell
|
|
94
|
-
cd examples
|
|
165
|
+
cd examples/basic
|
|
95
166
|
npm install
|
|
96
167
|
npx prisma db push
|
|
97
168
|
npm run dev
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { PrismaClient } from "@prisma/client/extension";
|
|
2
|
+
import { CompiledQuery, DatabaseConnection, QueryResult } from "kysely";
|
|
3
|
+
/**
|
|
4
|
+
* A Kysely database connection that uses Prisma as the driver
|
|
5
|
+
*/
|
|
6
|
+
export declare class PrismaConnection implements DatabaseConnection {
|
|
7
|
+
private readonly prisma;
|
|
8
|
+
constructor(prisma: PrismaClient);
|
|
9
|
+
executeQuery<R>(compiledQuery: CompiledQuery<unknown>): Promise<QueryResult<R>>;
|
|
10
|
+
streamQuery<R>(_compiledQuery: CompiledQuery<unknown>, _chunkSize?: number | undefined): AsyncIterableIterator<QueryResult<R>>;
|
|
11
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.PrismaConnection = void 0;
|
|
13
|
+
const kysely_1 = require("kysely");
|
|
14
|
+
/**
|
|
15
|
+
* A Kysely database connection that uses Prisma as the driver
|
|
16
|
+
*/
|
|
17
|
+
class PrismaConnection {
|
|
18
|
+
constructor(prisma) {
|
|
19
|
+
this.prisma = prisma;
|
|
20
|
+
}
|
|
21
|
+
executeQuery(compiledQuery) {
|
|
22
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
23
|
+
const { sql, parameters, query } = compiledQuery;
|
|
24
|
+
// Delete, update and insert queries return the number of affected rows if no returning clause is specified
|
|
25
|
+
const supportsReturning = kysely_1.DeleteQueryNode.is(query) ||
|
|
26
|
+
kysely_1.UpdateQueryNode.is(query) ||
|
|
27
|
+
kysely_1.InsertQueryNode.is(query);
|
|
28
|
+
const shouldReturnAffectedRows = supportsReturning && !query.returning;
|
|
29
|
+
// Execute the query with $executeRawUnsafe to get the number of affected rows
|
|
30
|
+
if (shouldReturnAffectedRows) {
|
|
31
|
+
const numAffectedRows = BigInt(yield this.prisma.$executeRawUnsafe(sql, ...parameters));
|
|
32
|
+
return {
|
|
33
|
+
rows: [],
|
|
34
|
+
numAffectedRows: numAffectedRows,
|
|
35
|
+
numUpdatedOrDeletedRows: numAffectedRows,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
// Otherwise, execute it with $queryRawUnsafe to get the query results
|
|
39
|
+
const rows = yield this.prisma.$queryRawUnsafe(sql, ...parameters);
|
|
40
|
+
return { rows };
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
streamQuery(_compiledQuery, _chunkSize) {
|
|
44
|
+
throw new Error("prisma-extension-kysely does not support streaming queries");
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
exports.PrismaConnection = PrismaConnection;
|
package/dist/driver.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { PrismaClient } from "@prisma/client/extension";
|
|
2
|
+
import { DatabaseConnection, Driver, TransactionSettings } from "kysely";
|
|
3
|
+
export declare class PrismaDriver<T extends PrismaClient> implements Driver {
|
|
4
|
+
private readonly prisma;
|
|
5
|
+
constructor(prisma: T);
|
|
6
|
+
init(): Promise<void>;
|
|
7
|
+
acquireConnection(): Promise<DatabaseConnection>;
|
|
8
|
+
beginTransaction(_connection: DatabaseConnection, _settings: TransactionSettings): Promise<void>;
|
|
9
|
+
commitTransaction(_connection: DatabaseConnection): Promise<void>;
|
|
10
|
+
rollbackTransaction(_connection: DatabaseConnection): Promise<void>;
|
|
11
|
+
releaseConnection(_connection: DatabaseConnection): Promise<void>;
|
|
12
|
+
destroy(): Promise<void>;
|
|
13
|
+
}
|
package/dist/driver.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.PrismaDriver = void 0;
|
|
13
|
+
const connection_1 = require("./connection");
|
|
14
|
+
class PrismaDriver {
|
|
15
|
+
constructor(prisma) {
|
|
16
|
+
this.prisma = prisma;
|
|
17
|
+
}
|
|
18
|
+
init() {
|
|
19
|
+
return __awaiter(this, void 0, void 0, function* () { });
|
|
20
|
+
}
|
|
21
|
+
acquireConnection() {
|
|
22
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
23
|
+
return new connection_1.PrismaConnection(this.prisma);
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
beginTransaction(_connection, _settings) {
|
|
27
|
+
throw new Error("prisma-extension-kysely does not support transactions");
|
|
28
|
+
}
|
|
29
|
+
commitTransaction(_connection) {
|
|
30
|
+
throw new Error("prisma-extension-kysely does not support transactions");
|
|
31
|
+
}
|
|
32
|
+
rollbackTransaction(_connection) {
|
|
33
|
+
throw new Error("prisma-extension-kysely does not support transactions");
|
|
34
|
+
}
|
|
35
|
+
releaseConnection(_connection) {
|
|
36
|
+
return __awaiter(this, void 0, void 0, function* () { });
|
|
37
|
+
}
|
|
38
|
+
destroy() {
|
|
39
|
+
return __awaiter(this, void 0, void 0, function* () { });
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
exports.PrismaDriver = PrismaDriver;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import { Kysely } from "kysely";
|
|
2
|
+
import { PrismaDriver } from "./driver";
|
|
3
3
|
/**
|
|
4
4
|
* The configuration object for the Prisma Kysely extension
|
|
5
5
|
*/
|
|
@@ -7,28 +7,22 @@ export type PrismaKyselyExtensionArgs<Database> = {
|
|
|
7
7
|
/**
|
|
8
8
|
* The Kysely instance to provide to the Prisma client
|
|
9
9
|
*/
|
|
10
|
-
kysely: Kysely<Database>;
|
|
10
|
+
kysely: (driver: PrismaDriver<any>) => Kysely<Database>;
|
|
11
11
|
};
|
|
12
12
|
/**
|
|
13
13
|
* Define a Prisma extension that adds Kysely query builder methods to the Prisma client
|
|
14
14
|
* @param extensionArgs The extension configuration object
|
|
15
15
|
*/
|
|
16
|
-
declare const _default: <Database>(extensionArgs: PrismaKyselyExtensionArgs<Database>) => (client: any) =>
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
* Execute a Kysely query and return the number of rows affected
|
|
29
|
-
* @param query A Kysely select, insert, delete or update query builder
|
|
30
|
-
* @returns The number of rows affected
|
|
31
|
-
*/
|
|
32
|
-
$kyselyExecute(query: Compilable): PrismaPromise<number>;
|
|
33
|
-
}> & import("@prisma/client/runtime/library").DefaultArgs>;
|
|
16
|
+
declare const _default: <Database>(extensionArgs: PrismaKyselyExtensionArgs<Database>) => (client: any) => {
|
|
17
|
+
$extends: {
|
|
18
|
+
extArgs: {
|
|
19
|
+
result: {};
|
|
20
|
+
model: {};
|
|
21
|
+
query: {};
|
|
22
|
+
client: {
|
|
23
|
+
$kysely: () => Kysely<Database>;
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
};
|
|
34
28
|
export default _default;
|
package/dist/index.js
CHANGED
|
@@ -1,40 +1,59 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
2
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
|
|
12
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
13
|
+
const client_1 = require("@prisma/client");
|
|
14
|
+
const driver_1 = require("./driver");
|
|
4
15
|
/**
|
|
5
16
|
* Define a Prisma extension that adds Kysely query builder methods to the Prisma client
|
|
6
17
|
* @param extensionArgs The extension configuration object
|
|
7
18
|
*/
|
|
8
|
-
exports.default = (extensionArgs) =>
|
|
9
|
-
|
|
19
|
+
exports.default = (extensionArgs) => client_1.Prisma.defineExtension((client) => {
|
|
20
|
+
const driver = new driver_1.PrismaDriver(client);
|
|
21
|
+
const kysely = extensionArgs.kysely(driver);
|
|
22
|
+
const extendedClient = client.$extends({
|
|
10
23
|
name: "prisma-extension-kysely",
|
|
11
24
|
client: {
|
|
12
25
|
/**
|
|
13
26
|
* The Kysely instance used by the Prisma client
|
|
14
27
|
*/
|
|
15
|
-
$kysely:
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
//
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
$
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
28
|
+
$kysely: kysely,
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
// Wrap the $transaction method to attach a fresh Kysely instance to the transaction client
|
|
32
|
+
const kyselyTransaction = (target) => (...args) => {
|
|
33
|
+
if (typeof args[0] === "function") {
|
|
34
|
+
// If the first argument is a function, add a fresh Kysely instance to the transaction client
|
|
35
|
+
const [fn, options] = args;
|
|
36
|
+
return target.$transaction((tx) => __awaiter(void 0, void 0, void 0, function* () {
|
|
37
|
+
// The Kysely instance should call the transaction client, not the original client
|
|
38
|
+
const driver = new driver_1.PrismaDriver(tx);
|
|
39
|
+
const kysely = extensionArgs.kysely(driver);
|
|
40
|
+
tx.$kysely = kysely;
|
|
41
|
+
return fn(tx);
|
|
42
|
+
}), options);
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
// Otherwise, just call the original $transaction method
|
|
46
|
+
return target.$transaction(...args);
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
// Attach the wrapped $transaction method to the extended client using a proxy
|
|
50
|
+
const extendedClientProxy = new Proxy(extendedClient, {
|
|
51
|
+
get: (target, prop, receiver) => {
|
|
52
|
+
if (prop === "$transaction") {
|
|
53
|
+
return kyselyTransaction(target);
|
|
54
|
+
}
|
|
55
|
+
return Reflect.get(target, prop, receiver);
|
|
38
56
|
},
|
|
39
57
|
});
|
|
58
|
+
return extendedClientProxy;
|
|
40
59
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "prisma-extension-kysely",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"author": {
|
|
5
5
|
"name": "Eoin O'Brien",
|
|
6
6
|
"url": "https://eoin.ai",
|
|
@@ -33,8 +33,8 @@
|
|
|
33
33
|
"scripts": {
|
|
34
34
|
"pretest": "npm run build && prisma db push",
|
|
35
35
|
"test": "jest",
|
|
36
|
-
"test:watch": "
|
|
37
|
-
"test:coverage": "
|
|
36
|
+
"test:watch": "npm run test -- --watch",
|
|
37
|
+
"test:coverage": "npm run test -- --coverage",
|
|
38
38
|
"clean": "rm -rf ./dist",
|
|
39
39
|
"build": "tsc",
|
|
40
40
|
"prepack": "npm run clean && npm run build",
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
"jest": "^29.7.0",
|
|
69
69
|
"jest-mock-extended": "^3.0.5",
|
|
70
70
|
"kysely": "^0.27.0",
|
|
71
|
-
"prettier": "3.
|
|
71
|
+
"prettier": "3.2.4",
|
|
72
72
|
"prisma": "latest",
|
|
73
73
|
"prisma-kysely": "^1.7.1",
|
|
74
74
|
"ts-jest": "^29.1.1",
|