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 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
- // Don't forget to customize this to match your database!
56
- const kysely = new Kysely<DB>({
57
- dialect: {
58
- createAdapter: () => new PostgresAdapter(),
59
- createDriver: () => new DummyDriver(),
60
- createIntrospector: (db) => new PostgresIntrospector(db),
61
- createQueryCompiler: () => new PostgresQueryCompiler(),
62
- },
63
- });
64
-
65
- const prisma = new PrismaClient().$extends(kyselyExtension({ kysely }));
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 prisma.$kyselyQuery(query);
96
+ const result = await query.execute();
82
97
 
83
98
  // You can also execute queries without fetching the results
84
- await prisma.$kyselyExecute(
85
- prisma.$kysely.deleteFrom("User").where("id", "=", id),
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 [example](example) directory for a sample project!
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;
@@ -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 type { PrismaPromise } from "@prisma/client/runtime/library";
2
- import type { Compilable, Kysely, Simplify } from "kysely";
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) => import("@prisma/client/extension").PrismaClientExtends<import("@prisma/client/runtime/library").InternalArgs<{}, {}, {}, {
17
- /**
18
- * The Kysely instance used by the Prisma client
19
- */
20
- $kysely: Kysely<Database>;
21
- /**
22
- * Execute a Kysely query and return the result
23
- * @param query A Kysely select, insert, delete or update query builder
24
- * @returns The result of the query
25
- */
26
- $kyselyQuery<T>(query: Compilable<T>): Promise<Simplify<T>[]>;
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
- const extension_1 = require("@prisma/client/extension");
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) => extension_1.Prisma.defineExtension((client) => {
9
- return client.$extends({
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: extensionArgs.kysely,
16
- /**
17
- * Execute a Kysely query and return the result
18
- * @param query A Kysely select, insert, delete or update query builder
19
- * @returns The result of the query
20
- */
21
- $kyselyQuery(query) {
22
- const { sql, parameters } = query.compile();
23
- const ctx = extension_1.Prisma.getExtensionContext(this);
24
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
25
- return ctx.$queryRawUnsafe(sql, ...parameters);
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) {
33
- const { sql, parameters } = query.compile();
34
- const ctx = extension_1.Prisma.getExtensionContext(this);
35
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
36
- return ctx.$executeRawUnsafe(sql, ...parameters);
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": "1.0.4",
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": "jest --watch",
37
- "test:coverage": "jest --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.1.1",
71
+ "prettier": "3.2.4",
72
72
  "prisma": "latest",
73
73
  "prisma-kysely": "^1.7.1",
74
74
  "ts-jest": "^29.1.1",