getting the returning data after inserting record as string | undefined instead of string

Hey folks here is my drizzle schema
import { createId } from "@paralleldrive/cuid2";
import { relations } from "drizzle-orm";
import { boolean, index, pgTable, text, timestamp, unique } from "drizzle-orm/pg-core";

export const user = pgTable(
"user",
{
id: text("id")
.notNull()
.primaryKey()
.$defaultFn(() => createId()),
name: text("name").notNull(),
email: text("email").notNull(),
emailVerified: boolean("email_verified").notNull().default(false),
type: text("type", { enum: ["admin", "customer"] })
.notNull()
.default("customer"),

createdAt: timestamp("created_at").notNull().defaultNow(),
updatedAt: timestamp("updated_at")
.notNull()
.$defaultFn(() => new Date()),
},
(table) => [unique().on(table.email, table.type), index().on(table.email)],
);

export const password = pgTable("password", {
hash: text("hash").notNull().unique(),
userId: text("user_id")
.notNull()
.references(() => user.id, { onDelete: "cascade" }),
});
import { createId } from "@paralleldrive/cuid2";
import { relations } from "drizzle-orm";
import { boolean, index, pgTable, text, timestamp, unique } from "drizzle-orm/pg-core";

export const user = pgTable(
"user",
{
id: text("id")
.notNull()
.primaryKey()
.$defaultFn(() => createId()),
name: text("name").notNull(),
email: text("email").notNull(),
emailVerified: boolean("email_verified").notNull().default(false),
type: text("type", { enum: ["admin", "customer"] })
.notNull()
.default("customer"),

createdAt: timestamp("created_at").notNull().defaultNow(),
updatedAt: timestamp("updated_at")
.notNull()
.$defaultFn(() => new Date()),
},
(table) => [unique().on(table.email, table.type), index().on(table.email)],
);

export const password = pgTable("password", {
hash: text("hash").notNull().unique(),
userId: text("user_id")
.notNull()
.references(() => user.id, { onDelete: "cascade" }),
});
I don't why after inserting record in the database the returning data has undefined also instead of specific types like string on columns
No description
13 Replies
Ghost
Ghost2w ago
ofc it's undefined you clearly did this
No description
Ghost
Ghost2w ago
maybe do something like this: const [{ id: userId }]
Aditya Kirad
Aditya KiradOP2w ago
I had to I intentionally not did that in my other app I had the same code but there I didn't had this problem there here is schema I had in my that app https://mystb.in/6247b647461a3fd134 the only difference is there I was using sqlite and here postgres
No description
Ghost
Ghost2w ago
have you tried solution I gave you?
Aditya Kirad
Aditya KiradOP2w ago
tried that already it gives this error Property 'id' does not exist on type '{ id: string; } | undefined'.
Ghost
Ghost2w ago
maybe use returnIds ?
Aditya Kirad
Aditya KiradOP2w ago
postgres don't support that
Sillvva
Sillvva2w ago
TypeScript can't assume an array has a first value.
const test: number[] = [1,2,3];
const value = test[0];
// ^? const value: number | undefined
const test: number[] = [1,2,3];
const value = test[0];
// ^? const value: number | undefined
The same is true regardless of how you try to access it.
const [value] = [1,2,3] as number[];
// ^? const value: number | undefined
const [value] = [1,2,3] as number[];
// ^? const value: number | undefined
This is different from a tuple type.
const [value1, value2] = [1,2,3] as [number, ...number[]];
// ^? const value1: number
// const value2: number | undefined
const [value1, value2] = [1,2,3] as [number, ...number[]];
// ^? const value1: number
// const value2: number | undefined
If the second insert query relies on the first, then you should wrap it in an if statement.
const userId = // first insert query
if (userId) {
// second insert query
} else {
tx.rollback();
}
const userId = // first insert query
if (userId) {
// second insert query
} else {
tx.rollback();
}
@Aditya Kirad ping for notification
Aditya Kirad
Aditya KiradOP2w ago
Firstly, in all of you example you are wrong typeof of value of in all examples will be number instead of number | undefined I just create new quakka sheet and run these examples you can see result in the images attached and secondly did you not see any of my previous screenshot in both screenshot there are similar type of queries the only difference is in first screenshot I'm using postgres as database and while in second sqlite is being used
No description
No description
No description
No description
Aditya Kirad
Aditya KiradOP2w ago
while retrieving the data using select query and then getting the undefined also makes sense but getting the data back after inserting it in database and then getting undefined on type doesn't makes sense
Sillvva
Sillvva2w ago
I see. In your postgres app, do you have "noUncheckedIndexedAccess": true, in your tsconfig? That causes the behavior I described. I prefer having that property on in all my projects, because it catches potential runtime errors that TS wouldn't otherwise catch. Example with it off: https://www.typescriptlang.org/play/#code/MYewdgzgLgBGCuBbARgUwE4QFxyW9A2gLowC8MxA3AFDWiSxhm4oYQEAMRNA9DzAJgA9APxA Example with it on: https://www.typescriptlang.org/play/?noUncheckedIndexedAccess=true#code/MYewdgzgLgBGCuBbARgUwE4QFxyW9A2gLowC8MxA3AFDWiSxhm4oYQEAMRNA9DzAJgA9APxA
Aditya Kirad
Aditya KiradOP2w ago
thank you so much yes turning it of removed that error but as you said I will keep it turned on @Sillvva just one more help can you tell even after using the guard clause why the userId type is not narrowing down to string
const id = await db.transaction(async (tx) => {
const userId = await tx
.insert(user)
.values({ name, email })
.returning({ id: user.id })
.then((res) => res[0]?.id);
if(!userId) tx.rollback()
await tx.insert(password).values({ hash: await getPasswordHash(userPassword), userId });
return userId
});
const id = await db.transaction(async (tx) => {
const userId = await tx
.insert(user)
.values({ name, email })
.returning({ id: user.id })
.then((res) => res[0]?.id);
if(!userId) tx.rollback()
await tx.insert(password).values({ hash: await getPasswordHash(userPassword), userId });
return userId
});
it's still string | undefined
Sillvva
Sillvva2w ago
On this line, you should throw. tx.rollback() throws internally, but I guess TS doesn't realize that. Adding throw adds the type narrowing you're looking for. Otherwise, TS assumes the code is allowed to continue. And since userId hasn't been defined in the undefined case, it can still be undefined.
if(!userId) throw tx.rollback()
if(!userId) throw tx.rollback()
I believe it also rolls back if an error is thrown. That's all the rollback function does internally.
if(!userId) throw new Error("Failed to insert user");
if(!userId) throw new Error("Failed to insert user");

Did you find this page helpful?