Trying to implement 404 but getting validator error
Hello all 👋 , in my app the ID for a resource ("campaign") is taken from the url, most the time things works as expected. However, if the user try to input the url and have typo or hit a deleted resource, In stead of seeing a 404 or my error page, the app crashes with this error. I tried adding .catch to the db.get call but it seems that the error is coming from the validator. is there is away to return null when campaign with the id provided does not exist?
8 Replies
You can use
v.string()
in your validator if you're not sure the string will be a valid ID and check in the Convex function with ctx.db.normalizeId
https://docs.convex.dev/database/document-ids#serializing-ids, then return null
if it's notDocument IDs | Convex Developer Hub
Create complex, relational data models using IDs.
Or you can catch this useQuery error with an errorboundary and show a 404
Do you know it will always be a string? You can accept more values if not (and then TypeScript will remind you to check for them in the body of your query)
Hey @ballingt , so I just changed the validator to be string and did type casting and that seems to work find. Does adding the ctx.db.normalizeId add any important benefit over the current implementation?
Mostly no it pretty much does the same thing as what you've written. It will let you know if that ID is actually from the table you think it is, and it might be easier to read
the code you could do surprising things if someone passed e.g. an
Id<"user">
insteadwhat! 😳 really ?, so if say a userId was passed to the bellow line, we are not always going to get null?
const campaign = await ctx.db.get(args.id as Id<"campaigns">).catch(() => null);
Yep, there'd be a User record stored in the campaign variable.
as Id<"campaigns">
is just a TypeScript thing, it doesn't exist at runtime
as
is always a way to promise something to just TypeScript, it never does anything at runtime. Here the promise/lie we're telling TypeScript is "just pretend that args.id
is a Id<"campaigns">
, I promise it will be." But if the code doesn't actually check that, it might not be true.
That's the reason to use normalizeId
here, that's the way to ask Convex if something is a real Id and whether it's from the table you think it is.That's also the benefit of using
args
validators rather than just doing:
As you saw earlier, it's doing runtime validationStumbled across this thread while trying to work out how to handle an array of IDs, which could belong to more than one table. Taking inspiration from the RLS code in
convex-helpers
(linked in another thread) I wrote a little helper function to help me determine which table an ID belongs to.
Sharing here, in case it's useful to anyone else
Can be used like this: