Кеняка
Кеняка2w ago

object is not accepted undefined

I have an object in the convex parent Document database that accepts Id<"document"> | undefined, but when I put undefined nothing changes
No description
36 Replies
Кеняка
КенякаOP2w ago
but I change undefined to Id<"documents">(pass values ​​with this data type) everything works
const document = await ctx.db.patch(args.id, {
...rest,
parentDocument: rest.parentDocument !== undefined ? rest.parentDocument : undefined
})
const document = await ctx.db.patch(args.id, {
...rest,
parentDocument: rest.parentDocument !== undefined ? rest.parentDocument : undefined
})
ChatGPT advised me to do this (specify exactly the object and type), but now it doesn’t work for me in the opposite direction: if the type is Id<"document">
lee
lee2w ago
Can you show a bit more of the code? What you have here looks like it should work, although I'd like to see where rest comes from. And I'm not sure why you would do const document = await ctx.db.patch(...) since patch doesn't return anything
Кеняка
КенякаOP2w ago
update func in convex/document.ts
export const update = mutation({
args: {
id: v.id("documents"),
title: v.optional(v.string()),
content: v.optional(v.string()),
coverImage: v.optional(v.string()),
icon: v.optional(v.string()),
isPublished: v.optional(v.boolean()),
parentDocument: v.optional(v.id("documents")),
userId: v.string(),
lastEditor: v.string()
},
handler: async (ctx, args) => {
const identity = await ctx.auth.getUserIdentity()

if (!identity) {
throw new Error("Not authenticated")
}

const { id, ...rest } = args

const existingDocument = await ctx.db.get(args.id)

if (!existingDocument) {
throw new Error("Document not found")
}

if (existingDocument.userId !== args.userId) {
throw new Error("Unauthorized")
}

const document = await ctx.db.patch(args.id, {
...rest,
})

return document
}
})
export const update = mutation({
args: {
id: v.id("documents"),
title: v.optional(v.string()),
content: v.optional(v.string()),
coverImage: v.optional(v.string()),
icon: v.optional(v.string()),
isPublished: v.optional(v.boolean()),
parentDocument: v.optional(v.id("documents")),
userId: v.string(),
lastEditor: v.string()
},
handler: async (ctx, args) => {
const identity = await ctx.auth.getUserIdentity()

if (!identity) {
throw new Error("Not authenticated")
}

const { id, ...rest } = args

const existingDocument = await ctx.db.get(args.id)

if (!existingDocument) {
throw new Error("Document not found")
}

if (existingDocument.userId !== args.userId) {
throw new Error("Unauthorized")
}

const document = await ctx.db.patch(args.id, {
...rest,
})

return document
}
})
lee
lee2w ago
Oh yeah that's not going to work. You need ctx.db.patch(args.id, {...rest, parentDocument: rest.parentDocument}); to make sure the value is undefined and not missing
Кеняка
КенякаOP2w ago
I did this, and now when I try to assign some value on the other side (with undefined on Id<"...">) it doesn't work if it helps somehow: func where undefined (or Id<"...">) to Id<"...">
const handleDrop = (event: React.DragEvent<HTMLDivElement>) => {
event.preventDefault()
const draggedId = event.dataTransfer.getData("text/plain")

if (draggedId && id && draggedId != id) {
const promise = update({
id: draggedId as Id<"documents">,
parentDocument: id as Id<"documents">,
userId: orgId,
lastEditor: user?.username as string
})

toast.promise(promise, {
loading: "Перемещаем...",
success: "Заметка успешно перемещена!",
error: "Не удалось переместить заметку"
})
}
}
const handleDrop = (event: React.DragEvent<HTMLDivElement>) => {
event.preventDefault()
const draggedId = event.dataTransfer.getData("text/plain")

if (draggedId && id && draggedId != id) {
const promise = update({
id: draggedId as Id<"documents">,
parentDocument: id as Id<"documents">,
userId: orgId,
lastEditor: user?.username as string
})

toast.promise(promise, {
loading: "Перемещаем...",
success: "Заметка успешно перемещена!",
error: "Не удалось переместить заметку"
})
}
}
func where Id<"..."> to undefined
const handleDrop = (event: React.DragEvent<HTMLDivElement>) => {
event.preventDefault();
const draggedId = event.dataTransfer.getData("text/plain");

console.log(draggedId)

if (draggedId) {
const promise = update({
id: draggedId as Id<"documents">,
parentDocument: undefined,
userId: orgId,
lastEditor: user?.username as string
});

toast.promise(promise, {
loading: "Перемещаем...",
success: "Заметка успешно перемещена!",
error: "Не удалось переместить заметку"
})
}
};
const handleDrop = (event: React.DragEvent<HTMLDivElement>) => {
event.preventDefault();
const draggedId = event.dataTransfer.getData("text/plain");

console.log(draggedId)

if (draggedId) {
const promise = update({
id: draggedId as Id<"documents">,
parentDocument: undefined,
userId: orgId,
lastEditor: user?.username as string
});

toast.promise(promise, {
loading: "Перемещаем...",
success: "Заметка успешно перемещена!",
error: "Не удалось переместить заметку"
})
}
};
so how to fix this
lee
lee2w ago
Sorry I don't understand the question This thread may help https://discord.com/channels/1019350475847499849/1297837121461162006/1297837121461162006
Кеняка
КенякаOP2w ago
I have the same problem, I tried to do as you wrote, everything is the same. I also tried to separate the functions, one to set undefined, the other Id<...>, in the end nothing** In short, I don’t want to set the value to undefined
erquhart
erquhart2w ago
The arguments object can't carry undefined values to the backend, as it needs to be stringified first:
JSON.stringify({ foo: 'bar', baz: undefined })
'{"foo":"bar"}'
JSON.stringify({ foo: 'bar', baz: undefined })
'{"foo":"bar"}'
lee
lee2w ago
What is your goal? To remove the field or to leave it as-is?
Кеняка
КенякаOP2w ago
remove
erquhart
erquhart2w ago
I would recommend sending null instead of undefined. Then in your Convex mutation, if you have null, set it to undefined there. undefined is not a value that can be stringified in JSON.
Кеняка
КенякаOP2w ago
I'll try now
lee
lee2w ago
If you want to delete the field, you cannot pass the patch argument directly from the client. You would need to explicitly set undefined in the patch argument Btw this thread prompted me to describe the undefined behavior in docs, since i would rather link docs than old discord threads 🙂
erquhart
erquhart2w ago
I've wrestled with this a good bit, and had a stringifiable undefined token in use at one point, but undefined is just a weird value and it's hard to not run into bugs if you start messing with it this way. I dislike null as a js languge feature but I think it's the way to go for this sort of thing.
Кеняка
КенякаOP2w ago
i did like this by adding an additional null type, but now if i pass values ​​with type Id<"..."> then it does not change undefined to Id
parentDocument: v.optional(v.union(v.id("documents"), v.null()))
parentDocument: v.optional(v.union(v.id("documents"), v.null()))
const document = await ctx.db.patch(args.id, {
...rest,
parentDocument: rest.parentDocument === null ? undefined : rest.parentDocument
})
const document = await ctx.db.patch(args.id, {
...rest,
parentDocument: rest.parentDocument === null ? undefined : rest.parentDocument
})
I convey it like this
parentDocument: null
parentDocument: null
Кеняка
КенякаOP2w ago
No description
erquhart
erquhart2w ago
It does not change undefined to Id
What do you mean The patch you pasted will set parentDocument to undefined if null is received
Кеняка
КенякаOP2w ago
Well, my object takes two types (already three): Id<"documents"> | undefined | null after what I added, if I pass null, then everything is ok and it removes the values ​​​​in the database and puts undefined but if I pass Id<"documents"> then it does not change the value in the database .
erquhart
erquhart2w ago
oh, can you share the code that does that on the client I mean and a bit more of the mutation to show where the id is being received and rest is being created, etc client might be enough though
Кеняка
КенякаOP2w ago
const handleDrop = (event: React.DragEvent<HTMLDivElement>) => {
event.preventDefault();
const draggedId = event.dataTransfer.getData("text/plain");

if (draggedId) {
const promise = update({
id: draggedId as Id<"documents">,
userId: orgId,
parentDocument: null, // in other code it’s the same thing, only here it’s not null, but an id variable with type Id
lastEditor: user?.username as string
});

toast.promise(promise, {
loading: "Перемещаем...",
success: "Заметка успешно перемещена!",
error: "Не удалось переместить заметку"
})
}
};
const handleDrop = (event: React.DragEvent<HTMLDivElement>) => {
event.preventDefault();
const draggedId = event.dataTransfer.getData("text/plain");

if (draggedId) {
const promise = update({
id: draggedId as Id<"documents">,
userId: orgId,
parentDocument: null, // in other code it’s the same thing, only here it’s not null, but an id variable with type Id
lastEditor: user?.username as string
});

toast.promise(promise, {
loading: "Перемещаем...",
success: "Заметка успешно перемещена!",
error: "Не удалось переместить заметку"
})
}
};
rest in mutation
handler: async (ctx, args) => {
const identity = await ctx.auth.getUserIdentity()

if (!identity) {
throw new Error("Not authenticated")
}

const { id, ...rest } = args

const existingDocument = await ctx.db.get(args.id)

if (!existingDocument) {
throw new Error("Document not found")
}

if (existingDocument.userId !== args.userId) {
throw new Error("Unauthorized")
}

const document = await ctx.db.patch(args.id, {
...rest,
parentDocument: rest.parentDocument === null ? undefined : rest.parentDocument
})

return document
}
handler: async (ctx, args) => {
const identity = await ctx.auth.getUserIdentity()

if (!identity) {
throw new Error("Not authenticated")
}

const { id, ...rest } = args

const existingDocument = await ctx.db.get(args.id)

if (!existingDocument) {
throw new Error("Document not found")
}

if (existingDocument.userId !== args.userId) {
throw new Error("Unauthorized")
}

const document = await ctx.db.patch(args.id, {
...rest,
parentDocument: rest.parentDocument === null ? undefined : rest.parentDocument
})

return document
}
erquhart
erquhart2w ago
Just do some basic debugging, I'd start with logging the type of rest.parentDocument when you expect an id. If it's undefined, that means you're not sending a value from the client, so you can start logging from there to trace why it's not happening.
Кеняка
КенякаOP2w ago
I did a debug, all data and values ​​from the client are sent correctly
Кеняка
КенякаOP2w ago
No description
erquhart
erquhart2w ago
Can you run ctx.db.get() again after you patch and log the result I'm assuming when you say it's not written to the database, you're not relying on the value returned from the mutation (as patch returns undefined, so this function returns undefined)
Кеняка
КенякаOP2w ago
No description
erquhart
erquhart2w ago
i mean on the backend, inside your mutation
const document = await ctx.db.patch(args.id, {
...rest,
parentDocument: rest.parentDocument === null ? undefined : rest.parentDocument
})

console.log(await ctx.db.get(id))

return document
const document = await ctx.db.patch(args.id, {
...rest,
parentDocument: rest.parentDocument === null ? undefined : rest.parentDocument
})

console.log(await ctx.db.get(id))

return document
You're just looking to see if the log output there includes your parentDocument id.
Кеняка
КенякаOP2w ago
So, I did a debug and here you can see that the first time it does everything ok and sets the required value, but the second time it removes it completely
No description
Кеняка
КенякаOP2w ago
although this was noticeable on the site itself
erquhart
erquhart2w ago
Do you know where the second call is coming from
Кеняка
КенякаOP2w ago
perhaps, since I call this function in other files although I checked and made a separate function for this action, it apparently also ran twice I also call the component once in layout.tsx
erquhart
erquhart2w ago
yeah, you'll just need to figure out where exactly you're calling it twice, that isn't a bug in Convex or anything
Кеняка
КенякаOP2w ago
Ok, I got it, thanks for the help
erquhart
erquhart2w ago
I'd search for every use of api.document.update in your client code and log from each call site to narrow down where they're coming from.
lee
lee2w ago
sounds like you might want
if (rest.parentDocument === null) {
rest.parentDocument = undefined;
}
await ctx.db.patch(args.id, rest);
if (rest.parentDocument === null) {
rest.parentDocument = undefined;
}
await ctx.db.patch(args.id, rest);
which would have the behavior where {parentDocument: null} removes the parentDocument field, {} or {parentDocument: undefined} leaves it as-is, and {parentDocument: id} sets it to a string.
Кеняка
КенякаOP2w ago
In general, I figured out the problem. It consisted in the fact that I called the function twice, applying Drag&Drop directly to the parent and child elements simultaneously all this with nulls, no checks were needed. It was only necessary to apply drag&drop to another container But thank you anyway, you gave me a push in what direction I need to think
erquhart
erquhart2w ago
Nice, glad you got it figured out!

Did you find this page helpful?