drizzle-orm icon indicating copy to clipboard operation
drizzle-orm copied to clipboard

[FEATURE]: return a object instead of a array if insert value is a object not array

Open Innei opened this issue 2 years ago • 18 comments

Describe what you want

Hi, I want to create a row, so I code this.

const model = await this.db.drizzle
      .insert(schema.user)
      .values({
        authCode,
        ...userDto,
        password: hashSync(userDto.password, 10),
      })
      .returning()

return model[0]

I added a .returning to get the new model. But I got model[], to I should destruct it first. model[0] is the data I want. But I insert just a single object, so the expected result should be an object, which can save one step of writing.

This is the expected behaviour.

const model = await this.db.drizzle
      .insert(schema.user)
      .values({
        authCode,
        ...userDto,
        password: hashSync(userDto.password, 10),
      })
      .returning()

return model

Innei avatar Sep 16 '23 06:09 Innei

I truly hope they develop this feature; constantly having to destructure like this can be quite frustrating.

const [model]= await this.db.drizzle .insert(schema.user) .values({ authCode, ...userDto, password: hashSync(userDto.password, 10), }) .returning()

  const model = await this.db.drizzle
  .insert(schema.user)
  .values({
    authCode,
    ...userDto,
    password: hashSync(userDto.password, 10),
  })
  .returning().first()
  
  how about this

Sapeiidw avatar Sep 27 '23 00:09 Sapeiidw

i find this very useful!

senzujuju avatar Oct 05 '23 05:10 senzujuju

Just destructure on response:

const [ model ] = await this.db.drizzle
      .insert(schema.user)
      .values({
        authCode,
        ...userDto,
        password: hashSync(userDto.password, 10),
      })
      .returning()

return model

spence avatar Nov 06 '23 08:11 spence

Are we guaranteed that there is a first element in the returned array if the call to returning is successful?

gabrielgrover avatar Jan 15 '24 18:01 gabrielgrover

what about ?

const  model  = await this.db.drizzle
      .insert(schema.user)
      .values({
        authCode,
        ...userDto,
        password: hashSync(userDto.password, 10),
      })
        .returning()
        .then((res) => res[0] ?? null)

return model

jhonsfran avatar Feb 19 '24 16:02 jhonsfran

the problem is that we have to do modal.id ?? "" while returning, coz the modal is not a garenteed response, even thought it should be

swarajbachu avatar Feb 23 '24 07:02 swarajbachu

the problem is that we have to do modal.id ?? "" while returning, coz the modal is not a garenteed response, even thought it should be

This sounds like a really nasty bug if this is the case and should be rewarded with its own ticket. How are you supposed to handle it when the row exists but the lib thinks it's not there and no error is thrown? Select it again? Delete it and retry?

matjaeck avatar Apr 25 '24 12:04 matjaeck

will throw in here that "then"-ing or destructuring the response isn't ideal when you are reusing your "repository layer" for batch calls. the moment you return something other than the drizzle sql object, it can no longer be used inside db.batch([])

parisholley avatar May 26 '24 17:05 parisholley

I think the proposal also applies to select , especially when we do the count

danaoairuike avatar Jun 03 '24 03:06 danaoairuike

Other downside of the way it's working is that even if the promise resolves correctly, typescript can't infer correctly the type since Array[i] could be undefined. Would be awesome to have this feature.

romoguill avatar Jun 20 '24 15:06 romoguill

I also hope for some built-in solution.

In the meantime, by combining with Lodash's first(), we get a nicer syntax over .then((res) => res[0] ?? null):

import { first } from "lodash-es"

db.insert(schema.user).values(data).returning().then(first)

asfktz avatar Jun 28 '24 19:06 asfktz

Can someone please confirm if this is indeed an issue? Currently, the typings indicate that this cannot occur.

try {
    const [res] = await db.insert(users).values({
        ...validated.data
    }).returning();
} catch (err) {
    // ...
}

According to the typings in "drizzle-orm": "^0.31.1", the destructured object res is not typed to include undefined or null.

matjaeck avatar Jul 02 '24 09:07 matjaeck

Can someone please confirm if this is indeed an issue? Currently, the typings indicate that this cannot occur.

try {
    const [res] = await db.insert(users).values({
        ...validated.data
    }).returning();
} catch (err) {
    // ...
}

According to the typings in "drizzle-orm": "^0.31.1", the destructured object res is not typed to include undefined or null.

I'm not sure if I understood correctly, but it's not a matter of "res" typing. The promise resolves to an array of the entity or the filtered fields passed in returning(). When destructuring, typescript can't know if the array[0] is defined. Of course noUncheckedIndexedAccess must be enabled to see this problem.

romoguill avatar Jul 02 '24 14:07 romoguill

That's an obvious DX improvement!

alpharder avatar Jul 08 '24 11:07 alpharder

that would be nice

mu6m avatar Jul 25 '24 18:07 mu6m

Oh no, was hoping thsi would have gotten some traction

neoighodaro avatar Jul 27 '24 07:07 neoighodaro

I would love this as well

Jdruwe avatar Aug 05 '24 13:08 Jdruwe

upvote!!

saychool avatar Aug 09 '24 10:08 saychool

+1

vic1707 avatar Aug 24 '24 11:08 vic1707

+1

Michael-Reich avatar Oct 23 '24 14:10 Michael-Reich

+1

wh5938316 avatar Nov 07 '24 07:11 wh5938316

ye thats a nice improvement

gituser-rs avatar Nov 07 '24 13:11 gituser-rs

this would be great.

anthonyalayo avatar Nov 12 '24 05:11 anthonyalayo

There’s a potential issue if you use .onConflictDoNothing(). If a conflict is found, it returns an empty array because nothing was inserted. So, in some cases, it might actually be empty.

jansedlon avatar Dec 02 '24 13:12 jansedlon

+1

rochajulian avatar Jan 20 '25 00:01 rochajulian

It would also be nice if we can get this functionality on queries that select, including the useLiveQuery hook. For example, if I have a todo app and I have a screen that only displays one todo list, I might use the useLiveQuery hook to get the data for the list and then display it on the UI. But since the data returned from the query is always an array of objects, I have to use data[0] throughout the screen to access the todo list's values. It's a bit inconvenient to have to do that all the time...

rochajulian avatar Jan 20 '25 00:01 rochajulian

I also hope for some built-in solution.

In the meantime, by combining with Lodash's first(), we get a nicer syntax over .then((res) => res[0] ?? null):

import { first } from "lodash-es"

db.insert(schema.user).values(data).returning().then(first)

For those who want a quick copy paste and not install yet another library:

export function first<T>(array: T[]): T | undefined {
	return array[0];
}

multiplehats avatar Jan 23 '25 22:01 multiplehats

For typescript just do

const newUser = await drizzle
  .insert(user)
  .values({...}).then([u] => u!)

In this case newUser will have type User and not User | undefined

Be cautious when you have .onConflictDoNothing() - then this will not work correctly...

valerii15298 avatar May 28 '25 13:05 valerii15298