Giter Site home page Giter Site logo

eoin-obrien / prisma-extension-kysely Goto Github PK

View Code? Open in Web Editor NEW
194.0 2.0 5.0 529 KB

Drop down to raw SQL in Prisma without sacrificing type safety!

Home Page: https://www.npmjs.com/package/prisma-extension-kysely

License: MIT License

TypeScript 92.66% JavaScript 6.75% Shell 0.59%
kysely prisma prisma-extension query-builder sql type-safety typescript

prisma-extension-kysely's Introduction

Prisma Kysely Extension

npm version npm downloads GitHub license Node.js CI Node.js Package codecov Maintainability

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 and integrate them seamlessly with Prisma.

And the best part? You can use all of your favorite kysely plugins with prisma-extension-kysely too!

You don't have to take our word for it, though:

I have to say, this is BY FAR the most amazing community package I've seen in the Prisma ecosystem!

It makes it so much more convenient to drop down to raw SQL when needed without sacrificing DX โ€” best of both worlds! ๐Ÿš€

โ€” Nikolas Burk, DevRel @ Prisma

Features

  • Type-safe โ€” Write raw SQL queries in a type-safe manner with kysely
  • Seamless integration โ€” Use kysely queries with Prisma as if they were native
  • Autocompletion โ€” Get autocompletion for your queries in your IDE
  • Type inference โ€” Get type inference for your queries in your IDE

Get started

Click the Use this template button and provide details for your Client extension

Install the dependencies:

npm install prisma-extension-kysely kysely

Set up the excellent prisma-kysely library to automatically generate types for your database:

npm install -D prisma-kysely

Add prisma-kysely as a generator to your schema.prisma:

generator kysely {
  provider = "prisma-kysely"
}

Generate the types:

npx prisma generate

Extend your Prisma Client:

import kyselyExtension from "prisma-extension-kysely";
import type { DB } from "./prisma/generated/types";

const prisma = new PrismaClient().$extends(
  kyselyExtension({
    kysely: (driver) =>
      new Kysely<DB>({
        dialect: {
          // This is where the magic happens!
          createDriver: () => driver,
          // Don't forget to customize these to match your database!
          createAdapter: () => new PostgresAdapter(),
          createIntrospector: (db) => new PostgresIntrospector(db),
          createQueryCompiler: () => new PostgresQueryCompiler(),
        },
        plugins: [
          // Add your favorite plugins here!
        ],
      }),
  }),
);

It's that simple! Now you can write raw SQL queries with kysely and use them with Prisma:

// Replace this...
const result = prisma.$queryRaw`SELECT * FROM User WHERE id = ${id}`;

// With this!
const query = prisma.$kysely
  .selectFrom("User")
  .selectAll()
  .where("id", "=", id);

// Thanks to kysely's magic, everything is type-safe!
const result = await query.execute();

// You can also execute queries without fetching the results
await prisma.$kysely.deleteFrom("User").where("id", "=", id).execute();

Transactions

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:

await prisma.$transaction(async (tx) => {
  await tx.$kysely
    .insertInto("User")
    .values({ id: 1, name: "John Doe" })
    .execute();

  await tx.$kysely
    .insertInto("User")
    .values({ id: 2, name: "Jane Doe" })
    .execute();
});

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.

// Don't do this! Prefer prisma.$transaction instead.
await prisma.$kysely.transaction().execute(async (trx) => {});

Plugins

Do you love Kysely's plugins? So do we! You can use them with prisma-extension-kysely as well:

const prisma = new PrismaClient().$extends(
  kyselyExtension({
    kysely: (driver) =>
      new Kysely<DB>({
        dialect: {
          createDriver: () => driver,
          createAdapter: () => new PostgresAdapter(),
          createIntrospector: (db) => new PostgresIntrospector(db),
          createQueryCompiler: () => new PostgresQueryCompiler(),
        },
        // Use your favorite plugins!
        plugins: [new CamelCasePlugin()],
      }),
  }),
);

If you're using the CamelCasePlugin, don't forget to add the camelCase option to your Prisma schema too:

generator kysely {
  provider = "prisma-kysely"
  camelCase = true
}

Take a look at the camel case example to see it in action! Check out the Kysely documentation for more information about plugins.

Examples

Check out the examples directory for a sample project!

cd examples/basic
npm install
npx prisma db push
npm run dev

Learn more

License

This project is licensed under the MIT License - see the LICENSE file for details.

prisma-extension-kysely's People

Contributors

eoin-obrien avatar release-please[bot] avatar renovate[bot] avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

prisma-extension-kysely's Issues

Inserting/Updating enums | Auto-casting

Is your feature request related to a problem? Please describe.
Again, really amazing extension! Not sure if this is smth that should be handled by the integration, but one issue we are having quite frequently at the moment is the following: When inserting values that contain an enum (defined as an enum in the db schema), the typescript compiler says our insert is ok, but during execution, the operation will fail as the inserted enum in typescript does not get transformed to the db level enum type. Thus, we have to unwrap the inserted object/array just to (by knowing that smth is an enum) and cast it using sql``::enum.

Describe the solution you'd like
Is there any way that this could be already handled by the plugin/extension? Given that it knows what values are enums defined by Prisma, could the plugin just automatically add the enum cast?

This would save us hours in debugging and manual (and untyped!!) changes.

Additional context
Please find a screenshot attached of what we mean.

Screenshot 2024-03-01 at 15 09 40 Screenshot 2024-03-01 at 15 10 50

Type '(driver: Driver) => Kysely<DB>' is not assignable to type 'Kysely<unknown>'

Describe the bug
I copied the basic example and found that it does not work well with typescript
It throws the following error:
Type '(driver: Driver) => Kysely<DB>' is not assignable to type 'Kysely<unknown>'

To Reproduce

  1. You can create a basic express and prisma project (you can use npx try-prisma@latest and select rest-express)
  2. You install all the required dependencies (prisma-kysely, prisma-extension-kysely & kysely)
  3. Add the following code to schema.prisma file:
generator kysely {
  provider     = "prisma-kysely"
  output       = "../src/db"
  fileName     = "types.ts"
  enumFileName = "enums.ts"
}
  1. Run npx prisma generate
  2. Create a new file and can extend prisma there (like following):
import { PrismaClient } from "@prisma/client";
import {
  Driver,
  Kysely,
  SqliteAdapter,
  SqliteIntrospector,
  SqliteQueryCompiler,
} from "kysely";
import kyselyExtension from "prisma-extension-kysely";
import type { DB } from "./types";

const prisma = new PrismaClient().$extends(
  kyselyExtension({
    kysely: (driver: Driver) =>
      new Kysely<DB>({
        dialect: {
          createAdapter: () => new SqliteAdapter(),
          createDriver: () => driver,
          createIntrospector: (db) => new SqliteIntrospector(db),
          createQueryCompiler: () => new SqliteQueryCompiler(),
        },
      }),
  })
);

Expected behavior
The type error shouldn't be there

Screenshots
If applicable, add screenshots to help explain your problem.
image

Desktop (please complete the following information):

  • OS: macOS Sonoma 14.2.1 (23C71)

Additional context
here's the tsconfig.json if your reference:

{
  "compilerOptions": {
    "sourceMap": true,
    "outDir": "dist",
    "strict": true,
    "lib": ["esnext"],
    "esModuleInterop": true
  }
}

Issue with prisma.$kysely transaction

Hi,
I am not sure but after a few weeks using your extension, It don't seems that it support prisma transaction, is this the case ?
I am expecting that when I do an interactive transaction it use the transaction that prisma created but it doesn't seems to work...

prisma.$transaction(async (tx: PrismaTransaction) => {
    await tx.$kysely...
}

Is this not supported ?

CamelCasePlugin Support

Is your feature request related to a problem? Please describe.
Hi! First of all amazing plugin! Great work, we wanted to do the same and now are just using yours :)

Atm CamelCasePlugin does not correctly work. The returned items are still snake_case. I think it would be easily achievable to check if CamelCase Plugin is active and if yes, manually rewrap the result in the transformResult() function.

Normally the engine/driver would do it, but as thus this plugin is only used as query builder, it won't be done automatically.

Thank you and greetings from Germany!
Paul

Transaction closes after running prisma query

Not too sure what's going on here, but it seems that the transaction closes when running a prisma query sequentially with using prisma's transaction client for kysely. For example:

return await prisma.$transaction(async (tx) => {
          const { teamId } = await tx.inventory.findFirstOrThrow({
            where: {
              id: inventoryId,
            },
            select: { teamId: true },
          })
         // throws an error saying the transaction has closed
         const ambulancesInTeam = await tx.$kysely
            .selectFrom('Inventory')
            .leftJoin('Team', 'Team.id', 'Inventory.teamId')
            ...
})

Specifically, the following error specifies that the queryRaw invocation by kysely is performed on a closed transaction:

TRPCClientError: 
Invalid `prisma.$queryRawUnsafe()` invocation:
Transaction API error: Transaction already closed: A query cannot be executed on a committed transaction.
    at TRPCClientError.from (webpack-internal:///./node_modules/@trpc/client/dist/TRPCClientError-38f9a32a.mjs:33:20)
    at eval (webpack-internal:///./node_modules/@trpc/client/dist/links/httpLink.mjs:45:105)

Add typing for transaction and logs

Hi,

First of all, thanks a lot for your package which will help a lot integration between prisma and kysely.
I'm currently trying your package but I'm struggling with two things, here my configuration:

export const prisma = new PrismaClient().$extends(
	kyselyExtension({
		kysely: (driver) =>
			new Kysely<DB>({
				dialect: {
					// This is where the magic happens!
					createDriver: () => driver,
					// Don't forget to customize these to match your database!
					createAdapter: () => new PostgresAdapter(),
					createIntrospector: (db) => new PostgresIntrospector(db),
					createQueryCompiler: () => new PostgresQueryCompiler(),
				},
				plugins: [
					// Add your favorite plugins here!
				],
			}),
	})
)
export type PrismaTransaction = Parameters<Parameters<PrismaClient["$transaction"]>[0]>[0]
prisma.$on("query" as never, (e: any) => {
	if (process.env.NODE_ENV !== "production") {
		console.log(e)
	}
})

First of all, I need to have the typing of the transaction so I can use typing everywhere. Is such a thing possible with this extension ? Other issue is the $on that is normally provided with PrismaClient, how to integrate it into this extension so I can log query ?

Thanks for your help !

Dependency Dashboard

This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.

Open

These updates have all been created already. Click a checkbox below to force a retry/rebase of any.

Ignored or Blocked

These are blocked by an existing closed PR and will not be recreated unless you click a checkbox below.

Detected dependencies

github-actions
.github/workflows/ci.yml
  • actions/checkout v4
  • actions/setup-node v4
  • codecov/codecov-action v4
  • actions/checkout v4
  • actions/setup-node v4
  • actions/checkout v4
  • actions/setup-node v4
  • actions/checkout v4
  • actions/setup-node v4
.github/workflows/npm-publish.yml
  • actions/checkout v4
  • actions/setup-node v4
  • actions/checkout v4
  • actions/setup-node v4
npm
package.json
  • @commitlint/cli ^18.4.3
  • @commitlint/config-conventional ^18.4.3
  • @commitlint/cz-commitlint ^18.4.3
  • @types/jest ^29.5.10
  • @typescript-eslint/eslint-plugin ^8.0.0
  • @typescript-eslint/parser ^8.0.0
  • commitizen ^4.3.0
  • eslint ^8.54.0
  • eslint-config-prettier ^9.0.0
  • eslint-plugin-require-extensions ^0.1.3
  • husky ^9.0.0
  • inquirer ^8.2.6
  • jest ^29.7.0
  • jest-mock-extended ^3.0.5
  • kysely ^0.27.0
  • prettier 3.3.3
  • prisma-kysely ^1.7.1
  • ts-jest ^29.1.1
  • tsconfig-to-dual-package ^1.2.0
  • typescript ^5.0.0

  • Check this box to trigger a request for Renovate to run again on this repository

Type error in NodeJS 20, when lib: ["es2023"]

Describe the bug
I'm using NodeJS 20 and the following tsconfig.json for my expressjs application, Prisma Extension Kysely throws Type Error:
Type 'typeof import("PATH_TO_MY_PROJECT/node_modules/prisma-extension-kysely/dist/index")' has no call signatures.

To Reproduce
Steps to reproduce the behavior:

  1. You can create a basic express and prisma project (you can use npx try-prisma@latest and select rest-express)
  2. You install all the required dependencies (prisma-kysely, prisma-extension-kysely & kysely)
  3. Add the following code to schema.prisma file:
generator kysely {
  provider     = "prisma-kysely"
  output       = "../src/db"
  fileName     = "types.ts"
  enumFileName = "enums.ts"
}
  1. Run npx prisma generate
  2. Create a new file and can extend prisma there (like following):
import kyselyExtension from "prisma-extension-kysely";
import type { DB } from "./types.js";  // <--- the '.js' extension cuz Relative import paths need explicit file extensions in ECMAScript imports when --moduleResolution is node16
import { PrismaClient } from "@prisma/client";
import {
  Kysely,
  SqliteAdapter,
  SqliteIntrospector,
  SqliteQueryCompiler,
  Driver,
} from "kysely";

export const prisma = new PrismaClient().$extends(
  kyselyExtension({ // <--- ERROR: This expression is not callable.
    kysely: (driver: Driver) =>
      new Kysely<DB>({
        dialect: {
          createDriver: () => driver,
          createAdapter: () => new SqliteAdapter(),
          createIntrospector: (db) => new SqliteIntrospector(db),
          createQueryCompiler: () => new SqliteQueryCompiler(),
        },
        plugins: [],
      }),
  })
);

Expected behavior
No Type Errors ๐Ÿ˜„

Desktop (please complete the following information):

  • OS: macOS Sonoma 14.2.1 (23C71)

Additional context
tsconfig.json:

{
  "compilerOptions": {
    "lib": ["es2023"],
    "module": "node16",
    "target": "es2022",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "moduleResolution": "node16",
    "sourceMap": true,
    "allowSyntheticDefaultImports": true,
    "noImplicitThis": true,
    "allowJs": true,
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "outDir": "dist/"
  },
  "exclude": ["node_modules"],
  "include": ["src/**/*"],
  "ts-node": {
    "esm": true,
    "experimentalSpecifierResolution": "node"
  }
}

Types Mismatch for postgres scalar lists

Describe the bug

nullable columns reported as non nullable

To Reproduce

clone this example repo and

  • docker compose up to start postgres
  • npm install && npx prisma migrate dev && npx prisma db seed
  • npm run dev to start the next app

Expected behavior

Expected Generated Types for scalar lists such as text[] integer[] json[] to be nullable

Model Definition

...

generator kysely {
  provider = "prisma-kysely"
}

...

model TestModel {
  id Int @id @default(autoincrement())

  list1 String[]
  list2 Int[]
  list3 Json[]
}

Postgres schema inspected after running prisma migrations
image

Versions:

  • @prisma/client: ^5.12.1
  • kysely: ^0.27.3
  • next: 14.2.2
  • prisma-extension-kysely: ^2.1.0

Additional context

Noticed this bug when my website crashed in prod, I was able to reproduce this bug pretty easily and I don't think its a prisma/prisma-keysely version issue.

I have two api's that I'm calling which do almost the same query but one is through prisma the other one is through prisma-kysely

prisma.testModel.findFirst();
prisma.$kysely
    .selectFrom("TestModel")
    .selectAll()
    .limit(1)
    .execute();

and here's what the result looks like:

  • prisma select
{
  "id": 1,
  "list1": [],
  "list2": [],
  "list3": []
}
  • prisma-kysely select
{
  "id": 1,
  "list1": null,
  "list2": null,
  "list3": null
}

and here's what the reported types look like:

  • prisma select
image
  • prisma-kysely select
image

You can find the api endpoints at /api/test and /api/test-kysely in my example repo. please follow the setup instructions mentioned in the "To Reproduce" section.

If someone can point me in the right direction I will be more than happy to dig into this and hopefully create a PR to solve this in the near future

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.