drizzle-orm
drizzle-orm copied to clipboard
[Feature Request] Pass a function to `default()`
What version of drizzle-orm are you using?
0.22.0
Describe the Feature Request
I'm trying to port something from Prisma that uses a cuid type for the identifier column. Because the underlying database doesn't support cuid natively, Prisma implements it at the ORM level.
Of course it's not reasonable to ask drizzle to implement every possible function under the sun, but it would be nice if the default column type could take a user-defined function (e.g. from @paralleldrive/cuid2) to make this simple, something like:
import { pgTable, text } from 'drizzle-orm/pg-core'
import { createId } from '@paralleldrive/cuid2'
export const users = pgTable('users', {
id: text('id').default(() => createId()).primaryKey(),
name: text('name'),
})
Otherwise, the cuid isn't part of the schema, and instead needs to be added to every insert query, above the ORM layer.
It won't work like this, because the .default() function is only used in drizzle-kit and not in ORM's runtime. What you can do, however, is to define custom type: https://github.com/drizzle-team/drizzle-orm/blob/main/docs/custom-types.lite.md
@dankochetov Hmmm, I'm looking at the custom types and I have the following concerns / questions:
-
The
configfield in the type parameter forcustomTypeis only available as a parameter fordataType, not fortoDriver. What I would like to do is haveconfig: { idGenerator?: () => string }, which would allow passing an override with a fixed seed, which is useful in test environments so that tests have determinism. However, thisconfigis not available intoDriver, so I would need to change the entirecustomTypefor test environments, and therefore the entire schema, which is really not ideal....? -
The
toDriverfunction is defined as(value: T['data']) => T['driverData']not(value?: T['data']) => T['driverData']. Of course, if an identifier has already been established, then I want to re-use it. I only want to callcreateId()to generate a new ID if the row is being inserted for the first time, and the user has not provided a value. It doesn't seem like this matchestoDriver's type signature?
Thumbs up for this feature, I'd love to use my nanoid(prefix: string) util in this exact scenario.
export const user = pgTable('user', {
id: text('id').default(() => nanoid("usr")).primaryKey(),
...
})
To create ids like usr_1suf98fdJA.
I did manage getting it to work via a customType, but with a slight inconvenience:
const cuid = customType<{ data: string | undefined; notNull: true }>({
dataType() {
return 'varchar(191)' // match prisma
},
toDriver(value?: string) {
return value ?? createId()
},
})
Then use like id: cuid('id').primaryKey(), but the when you go to insert things it seems the query builder needs undefined id explicitly passed to the values in order to actually call the toDriver fn otherwise it won't if the id key is missing:
db.insert(users).values({
id: undefined,
username: input.username,
})
@garlandcrow Did you make any headway on this?\
The lack of this feature is the only thing stopping us from migrating from Prisma.
I'm not what has changed since but gave @garlandcrow's suggestion a go and its no longer working – this feature would be super useful!
Any update on this @dankochetov?
I'd also love some way to define some code that calculates a default for customTypes.
FWIW, after days of research, the best lexicographical id (MySQL needs a lexicographical ids) I found was a re-implementation of Firebase's PushID. But yeah, having the ability just to give any random function for id generation is even better!
Edit: Here the link to the re-implementation of PushID, it's a bit more than 30 LoC, https://gist.github.com/mikelehen/3596a30bd69384624c11
This would be a great addition!