fdarian/prisma-generator-drizzle

v1

fdarian opened this issue · 24 comments

💡 This post will be updated over time

Planned Features

These are the essential features necessary for querying and mutating purposes, providing a 1:1 feature mapping from Prisma so you can start write in Drizzle right away. Kindly point out if there are anything missing from list:

Models

  • Field types
    • Scalar
      • Unsupported
    • List
    • Relations (e.g. []) for Drizzle relational query
    • Enum
  • Constraints
    • ID
    • primaryKey
    • notNull (when no ? modifier)
  • Custom @db. field
  • Multi database schema

Helpers

Config

  • Multi .prisma file
  • Support for all providers
    • postgres
    • mysql
    • sqlite

Unplanned

V1 will focus exclusively on query and mutation operations. Although you can still utilize most of the drizzle-kit features, it is advisable to stick with Prisma for those. The following parts are currently not under consideration:

Following this 👀 Thanks for your work.

Amazing work! Looking forward to the SQLite support.

@octalpixel I've just added the SQLite adapter on v0.5.0, feel free to check out and let me know if there's a problem

@octalpixel I've just added the SQLite adapter on v0.5.0, feel free to check out and let me know if there's a problem

Thanks for the ping. Will check this out later today.

@octalpixel I've just added the SQLite adapter on v0.5.0, feel free to check out and let me know if there's a problem

Thanks for the ping. Will check this out later today.

Hey tried this now on a quick sample project with two tables and it works well! Thanks for this! Will do a bit more a bigger schema.

I know this a bit unrela

@octalpixel I've just added the SQLite adapter on v0.5.0, feel free to check out and let me know if there's a problem

Thanks for the ping. Will check this out later today.

Hey tried this now on a quick sample project with two tables and it works well! Thanks for this! Will do a bit more a bigger schema.

Ik this a bit unrelated but I tried this on React Native Project, and there is require cycle.
image

This because categories and transaction import each other for relationships.

How about an optional flag to define all the relationship in another file ?

I know this a bit unrela

@octalpixel I've just added the SQLite adapter on v0.5.0, feel free to check out and let me know if there's a problem

Thanks for the ping. Will check this out later today.

Hey tried this now on a quick sample project with two tables and it works well! Thanks for this! Will do a bit more a bigger schema.

Ik this a bit unrelated but I tried this on React Native Project, and there is require cycle. image

This because categories and transaction import each other for relationships.

How about an optional flag to define all the relationship in another file ?

Thanks for noticing! Just released the patch v0.5.2 where you can add relationalQuery = false

generator drizzle {
  provider        = "prisma-generator-drizzle"
  relationalQuery = false
}

A proper fix would be to simply separate the definition to a separate file later

I know this a bit unrela

@octalpixel I've just added the SQLite adapter on v0.5.0, feel free to check out and let me know if there's a problem

Thanks for the ping. Will check this out later today.

Hey tried this now on a quick sample project with two tables and it works well! Thanks for this! Will do a bit more a bigger schema.

Ik this a bit unrelated but I tried this on React Native Project, and there is require cycle. image
This because categories and transaction import each other for relationships.
How about an optional flag to define all the relationship in another file ?

Thanks for noticing! Just released the patch v0.5.2 where you can add relationalQuery = false

generator drizzle {
  provider        = "prisma-generator-drizzle"
  relationalQuery = false
}

A proper fix would be to simply separate the definition to a separate file later

Thanks! I believe this is one way of solving it. Also just looking at your patch at the moment and train of thought.Maybe not the best, but here is a thought dump

  • Have a flag to output everything to one file. (Fixes the require cycle for React Native projects)
  • Have a flag to output relationship to another file
  • Flag to only generate relationships (This can actually be a good starting point to learn about how to make relationships with Drizzle as well)

It would be great if this lib also properly handled @ignore and @@ignore. Basically don't pull these columns and/or tables into the generated schema(s).

It would be great if this lib also properly handled @ignore and @@ignore. Basically don't pull these columns and/or tables into the generated schema(s).

Yeah that seems simple.

So just wanted to report an experiment that I did. After trying out your lib, an idea sparked in my head. WHAT IF we introspect the database using drizzlekit to actually pull in the schema. I tried this with a local sqlite db, and db pushed the schema to it using prisma and introspected it with drizzlekit. This is what I got.

PS: Disregard the table names being title case :D

From introspect

export const transaction = sqliteTable("Transaction", {
	id: integer("id").primaryKey({ autoIncrement: true }).notNull(),
	amount: real("amount").notNull(),
	date: numeric("date").notNull(),
	description: text("description"),
	createdAt: numeric("createdAt").default(sql`(CURRENT_TIMESTAMP)`).notNull(),
	updatedAt: numeric("updatedAt").notNull(),
	transCategoryId: integer("transCategoryId").references(() => transCategory.id, { onDelete: "set null", onUpdate: "cascade" } ),
	categoryId: integer("categoryId").references(() => category.id, { onDelete: "set null", onUpdate: "cascade" } ),
});

export const transCategory = sqliteTable("TransCategory", {
	id: integer("id").primaryKey({ autoIncrement: true }).notNull(),
	name: text("name").notNull(),
},
(table) => {
	return {
		nameKey: uniqueIndex("TransCategory_name_key").on(table.name),
	}
});

From the generator

export const transactions = sqliteTable('Transaction', {
  id: integer('id', { mode: 'number' }).primaryKey(),
  amount: real('amount').notNull(),
  date: integer('date', { mode: 'timestamp' }).notNull(),
  description: text('description'),
  createdAt: integer('createdAt', { mode: 'timestamp' }).notNull(),
  updatedAt: integer('updatedAt', { mode: 'timestamp' }).notNull(),
  transCategoryId: integer('transCategoryId', { mode: 'number' }),
  categoryId: integer('categoryId', { mode: 'number' }),
});

export const transactionsRelations = relations(transactions, (helpers) => ({
  TransCategory: helpers.one(transCategories, {
    relationName: 'TransCategoryToTransaction',
    fields: [transactions.transCategoryId],
    references: [transCategories.id],
  }),
  Category: helpers.one(categories, {
    relationName: 'CategoryToTransaction',
    fields: [transactions.categoryId],
    references: [categories.id],
  }),
}));

Off scope for you: One thing I noticed that prisma doesn't play well with enum of sqlite (Probably something you can look into). If i remember correctly Sqlite enums are done using CHECKS in sql so in one way doesnt make sense ?

Also drizzle-team/drizzle-orm#636 seems to be solved with incorrect relationships. So I guess in theory the introspect should work ?

So just wanted to report an experiment that I did. After trying out your lib, an idea sparked in my head. WHAT IF we introspect the database using drizzlekit to actually pull in the schema. I tried this with a local sqlite db, and db pushed the schema to it using prisma and introspected it with drizzlekit. This is what I got.

PS: Disregard the table names being title case :D

From introspect

export const transaction = sqliteTable("Transaction", {
	id: integer("id").primaryKey({ autoIncrement: true }).notNull(),
	amount: real("amount").notNull(),
	date: numeric("date").notNull(),
	description: text("description"),
	createdAt: numeric("createdAt").default(sql`(CURRENT_TIMESTAMP)`).notNull(),
	updatedAt: numeric("updatedAt").notNull(),
	transCategoryId: integer("transCategoryId").references(() => transCategory.id, { onDelete: "set null", onUpdate: "cascade" } ),
	categoryId: integer("categoryId").references(() => category.id, { onDelete: "set null", onUpdate: "cascade" } ),
});

export const transCategory = sqliteTable("TransCategory", {
	id: integer("id").primaryKey({ autoIncrement: true }).notNull(),
	name: text("name").notNull(),
},
(table) => {
	return {
		nameKey: uniqueIndex("TransCategory_name_key").on(table.name),
	}
});

From the generator

export const transactions = sqliteTable('Transaction', {
  id: integer('id', { mode: 'number' }).primaryKey(),
  amount: real('amount').notNull(),
  date: integer('date', { mode: 'timestamp' }).notNull(),
  description: text('description'),
  createdAt: integer('createdAt', { mode: 'timestamp' }).notNull(),
  updatedAt: integer('updatedAt', { mode: 'timestamp' }).notNull(),
  transCategoryId: integer('transCategoryId', { mode: 'number' }),
  categoryId: integer('categoryId', { mode: 'number' }),
});

export const transactionsRelations = relations(transactions, (helpers) => ({
  TransCategory: helpers.one(transCategories, {
    relationName: 'TransCategoryToTransaction',
    fields: [transactions.transCategoryId],
    references: [transCategories.id],
  }),
  Category: helpers.one(categories, {
    relationName: 'CategoryToTransaction',
    fields: [transactions.categoryId],
    references: [categories.id],
  }),
}));

Off scope for you: One thing I noticed that prisma doesn't play well with enum of sqlite (Probably something you can look into). If i remember correctly Sqlite enums are done using CHECKS in sql so in one way doesnt make sense ?

Also drizzle-team/drizzle-orm#636 seems to be solved with incorrect relationships. So I guess in theory the introspect should work ?

That's actually also my initial hack a while ago, and it is a good one! (depends on the stability of db pull tho)

However the downside for this solution is that it requires an active database connection, meaning it requires another setup and it became more complex when managing with multiple branches. Therefore since Prisma already have their DMMF helper, as well as other packages like the nexus, zod, typegraphql, pothos, etc.. uses this technique to map Prisma with their features, it'd be safer use the proven pipeline like the others.

Off scope for you: One thing I noticed that prisma doesn't play well with enum of sqlite (Probably something you can look into). If i remember correctly Sqlite enums are done using CHECKS in sql so in one way doesnt make sense ?

Fyi I'm not really familiar for this. Currently the goal of this library is to provide all features that Prisma has — excluding unsupported features like the ENUMs in sqlite. Once Prisma has the support, we could reliably map the feature to drizzle.

So if you'd like to use one that drizzle has already supported, I think you could generate and commit the result. However it requires you to maintain the modified schema everytime the script generates the schema, I think this could be a feature that we could look in the future.

Also I think this is where the db pull solution comes in, it's more of a subsidiary rather than complementary.

So just wanted to report an experiment that I did. After trying out your lib, an idea sparked in my head. WHAT IF we introspect the database using drizzlekit to actually pull in the schema. I tried this with a local sqlite db, and db pushed the schema to it using prisma and introspected it with drizzlekit. This is what I got.
PS: Disregard the table names being title case :D
From introspect

export const transaction = sqliteTable("Transaction", {
	id: integer("id").primaryKey({ autoIncrement: true }).notNull(),
	amount: real("amount").notNull(),
	date: numeric("date").notNull(),
	description: text("description"),
	createdAt: numeric("createdAt").default(sql`(CURRENT_TIMESTAMP)`).notNull(),
	updatedAt: numeric("updatedAt").notNull(),
	transCategoryId: integer("transCategoryId").references(() => transCategory.id, { onDelete: "set null", onUpdate: "cascade" } ),
	categoryId: integer("categoryId").references(() => category.id, { onDelete: "set null", onUpdate: "cascade" } ),
});

export const transCategory = sqliteTable("TransCategory", {
	id: integer("id").primaryKey({ autoIncrement: true }).notNull(),
	name: text("name").notNull(),
},
(table) => {
	return {
		nameKey: uniqueIndex("TransCategory_name_key").on(table.name),
	}
});

From the generator

export const transactions = sqliteTable('Transaction', {
  id: integer('id', { mode: 'number' }).primaryKey(),
  amount: real('amount').notNull(),
  date: integer('date', { mode: 'timestamp' }).notNull(),
  description: text('description'),
  createdAt: integer('createdAt', { mode: 'timestamp' }).notNull(),
  updatedAt: integer('updatedAt', { mode: 'timestamp' }).notNull(),
  transCategoryId: integer('transCategoryId', { mode: 'number' }),
  categoryId: integer('categoryId', { mode: 'number' }),
});

export const transactionsRelations = relations(transactions, (helpers) => ({
  TransCategory: helpers.one(transCategories, {
    relationName: 'TransCategoryToTransaction',
    fields: [transactions.transCategoryId],
    references: [transCategories.id],
  }),
  Category: helpers.one(categories, {
    relationName: 'CategoryToTransaction',
    fields: [transactions.categoryId],
    references: [categories.id],
  }),
}));

Off scope for you: One thing I noticed that prisma doesn't play well with enum of sqlite (Probably something you can look into). If i remember correctly Sqlite enums are done using CHECKS in sql so in one way doesnt make sense ?
Also drizzle-team/drizzle-orm#636 seems to be solved with incorrect relationships. So I guess in theory the introspect should work ?

That's actually also my initial hack a while ago, and it is a good one! (depends on the stability of db pull tho)

However the downside for this solution is that it requires an active database connection, meaning it requires another setup and it became more complex when managing with multiple branches. Therefore since Prisma already have their DMMF helper, as well as other packages like the nexus, zod, typegraphql, pothos, etc.. uses this technique to map Prisma with their features, it'd be safer use the proven pipeline like the others.

Off scope for you: One thing I noticed that prisma doesn't play well with enum of sqlite (Probably something you can look into). If i remember correctly Sqlite enums are done using CHECKS in sql so in one way doesnt make sense ?

Fyi I'm not really familiar for this. Currently the goal of this library is to provide all features that Prisma has — excluding unsupported features like the ENUMs in sqlite. Once Prisma has the support, we could reliably map the feature to drizzle.

So if you'd like to use one that drizzle has already supported, I think you could generate and commit the result. However it requires you to maintain the modified schema everytime the script generates the schema, I think this could be a feature that we could look in the future.

Also I think this is where the db pull solution comes in, it's more of a subsidiary rather than complementary.

Definitely! Thanks for your insights on this! I empathize with the fact that extra setup is needed for a case like such. My thought process if purely SQLite based. Thats where your libs value prop lies for me. Really love what Prisma Schema has to offer for the DX when setting up the database. Looking to use Prisma as the schema generator, more sort of like where some devs use Prisma + Kysely.

Definitely! Thanks for your insights on this! I empathize with the fact that extra setup is needed for a case like such. My thought process if purely SQLite based. Thats where your libs value prop lies for me. Really love what Prisma Schema has to offer for the DX when setting up the database. Looking to use Prisma as the schema generator, more sort of like where some devs use Prisma + Kysely.

Thanks also for the suggestion! keep it coming

Definitely! Thanks for your insights on this! I empathize with the fact that extra setup is needed for a case like such. My thought process if purely SQLite based. Thats where your libs value prop lies for me. Really love what Prisma Schema has to offer for the DX when setting up the database. Looking to use Prisma as the schema generator, more sort of like where some devs use Prisma + Kysely.

Thanks also for the suggestion! keep it coming

Also! I forgot to mention you have another value prop. You generate the relationships as well where the introspect does not drizzle-team/drizzle-orm#3358

Hi! Are default values currently in development? I'd want to use the package, but unfortunately default values of identifiers aren't set properly, so typescript wants me to set ID on every insert, kinda annoying!

Hi! Are default values currently in development? I'd want to use the package, but unfortunately default values of identifiers aren't set properly, so typescript wants me to set ID on every insert, kinda annoying!

On it #22

Hey @farreldarian with the inclusion of defaults support, could you push a new version?

I'm planning to address #18 and add bytes support for the 0.6.0, but I've just published a canary release If you'd like to try out

Hi, this works great with general default values, but unfourtenately this didn't solve the id issues.
Ids are generated like this based on my postgres db:
id String @id @default(dbgenerated("uuid_generate_v4()")) @db.Uuid
Current implementation doesn't really work in this case, only with autoincrement. Is there something you could do? @farreldarian

Hi, this works great with general default values, but unfourtenately this didn't solve the id issues. Ids are generated like this based on my postgres db: id String @id @default(dbgenerated("uuid_generate_v4()")) @db.Uuid Current implementation doesn't really work in this case, only with autoincrement. Is there something you could do? @farreldarian

Defining dbgenerated explicitly will work, I'm working on the PR right know. But I couldn't find in the prisma docs defining the mapping for uuid() function in SQL for each provider.

In this case I recommend using the drizzle.default directive to generate via javascript, similar to what Prisma does with cuid().

UPDATE
It's all generated by the prisma engine, including uuid().

Looks great! Could you provide a canary version for now to test it out? Thanks!

Looks great! Could you provide a canary version for now to test it out? Thanks!

Just released v0.6.0 🚀

Nice work ! keep the updates...