cornpopguy
cornpopguy9mo ago

Convex ents v.optional() makes user Value | undefined

Hi guys, when I add .field('subscriptionId', v.optional(v.string())) to my schema all values suddenly become Value | undefined, but when there are no optional fields no errors arise. How could this be fixed?
No description
60 Replies
cornpopguy
cornpopguyOP9mo ago
Here is my schema.ts without it:
No description
erquhart
erquhart9mo ago
If a field is optional, it's type will be a union with undefined because the value may actually be undefined. The errors are a feature, the compiler is telling you have to make sure the value is defined in those places. For example, stripeApi.customers.retrieve() requires a string, and will probably throw an error if you pass it undefined. There are many ways to conditionally check for undefined to fix these errors, depends on your use case and the situations in which this field would be undefined. So, eg., if you have a form that should only ever be viewable by a user that has a stripe id, you can query the user and then throw right away if they don't have a stripe id. The throw should never occur since the user shouldn't be able to get to the form without a stripe id anyway, and Typescript can then deduce that, after the conditional throw, the user definitely has a defined stripe id.
cornpopguy
cornpopguyOP9mo ago
Hmm, but I am talking about all of the values of the user become Value | undefined shouldn't only the subscriptionId itself be Value | undefined??
erquhart
erquhart9mo ago
In the error from your screenshot, user.stripeUserId is Value | undefined Do you have an error where user itself is union w/ undefined?
cornpopguy
cornpopguyOP9mo ago
Yes thats what I am talking about when I make the subscription id optional .field('subscriptionId', v.optional(v.string())) like so everything else becomes Value | undefined even the stripeUserId which is not optional.
cornpopguy
cornpopguyOP9mo ago
When I remove the optional from the subscriptionId everything works fine:
No description
cornpopguy
cornpopguyOP9mo ago
When I do not make the subscriptionid optional then no, but when I do:
No description
cornpopguy
cornpopguyOP9mo ago
Uhh, sorry If I did not provide enough info. Is this supposed to happen? Shouldn't only the optional field be | undefined??? I am talking about if I make the subscriptionId field optional all other fields like stripeId which are not optional become Value | undefined .
erquhart
erquhart9mo ago
aahh I see what you're saying now Can you share your schema with optional field set Is it just { optional: true } on that one field and no other changes? I haven't used Ents much, and would need to look more into the docs to confirm, but you may need to use optional: true instead of v.optional() hmm actually that looks like it's edge specific
cornpopguy
cornpopguyOP9mo ago
No description
erquhart
erquhart9mo ago
yeah that should work
cornpopguy
cornpopguyOP9mo ago
with optional
No description
erquhart
erquhart9mo ago
May have to wait for @Michal Srb to evaluate, not certain if this is a bug but it seems like it.
cornpopguy
cornpopguyOP9mo ago
well If do it like in that screenshot, that makes everything else Value | undefined
ampp
ampp9mo ago
Fields are not made to be used like that because they are all indexed, i don't see why you would want to make all those fields indexed, i only use them for something i want to be unique.
erquhart
erquhart9mo ago
Not sure what you mean here, they're just defining a schema
ampp
ampp9mo ago
In every example ive seen there is quite a bit of mixing and matching. so i understand field to not be a full replacement, otherwise it would be used as such. from the saas-starter template. users: defineEnt({ firstName: v.optional(v.string()), lastName: v.optional(v.string()), fullName: v.string(), pictureUrl: v.optional(v.string()), }) .field("email", v.string(), { unique: true }) .field("tokenIdentifier", v.string(), { unique: true }) .edges("members", { ref: true, deletion: "soft" }) .deletion("soft"),
erquhart
erquhart9mo ago
Ah I see, didn't realize you could define fields for ents without .field(), or that .field() creates an index. That said, I still wouldn't expect the behavior OP is seeing.
Michal Srb
Michal Srb9mo ago
Looks like a bug
cornpopguy
cornpopguyOP9mo ago
Even after changing my schema to following:
No description
cornpopguy
cornpopguyOP9mo ago
the issue persists
No description
Michal Srb
Michal Srb9mo ago
Oh, I think you have a bug in your query? Can you share the full code of getUserByExetrnalIdOrThrow, and make sure it has no type errors?
cornpopguy
cornpopguyOP9mo ago
No description
cornpopguy
cornpopguyOP9mo ago
export const getUserByExternalIdOrThrow = internalQuery({
args: {
clerkUserId: v.string(),
},
handler: async (context, arguments_) => {
const { clerkUserId } = arguments_;
const user = await context.table('users').get('clerkUserId', clerkUserId);

if (!user) {
throw new ConvexError('User not found.');
}

return user;
},
});
export const getUserByExternalIdOrThrow = internalQuery({
args: {
clerkUserId: v.string(),
},
handler: async (context, arguments_) => {
const { clerkUserId } = arguments_;
const user = await context.table('users').get('clerkUserId', clerkUserId);

if (!user) {
throw new ConvexError('User not found.');
}

return user;
},
});
Michal Srb
Michal Srb9mo ago
I'll have a look later today, does look like a bug in Ents
cornpopguy
cornpopguyOP9mo ago
Okay, thank you very much! Also is it recommended to mix and match like this or use .field() for everything? Like if I do not make my field unique its not indexable.
Michal Srb
Michal Srb9mo ago
It shouldn't matter which syntax is used. Fields from either syntax can be used in indexes.
cornpopguy
cornpopguyOP9mo ago
Did you figure this out? Are the issues on my side?
Michal Srb
Michal Srb9mo ago
I think this must be caused by some usage of the field that you're making optional. Can you look at every place where you're using the field, and see if making it optional doesn't break anything? Are you using it in your custom query or mutation definition? Even the original error made sense, the function requires string, but you were passing in a string | undefined, so that should error (although I'm not sure why the error message says Value | undefined).
cornpopguy
cornpopguyOP9mo ago
But in the original error I was passing the stripeUserId which is not optional, only the subscriptionId is. I was using the subscriptionId nowhere yet, by just simply adding it it made all other fields undefined.
cornpopguy
cornpopguyOP9mo ago
Here is a demo video:
Michal Srb
Michal Srb9mo ago
Can you try two things: 1. In getUserByExternalIdOrThrow change the last line to return user.doc(); (what happens now, do you still have the same issue?) 2. at the getUserByExternalIdOrThrow callsite, change const user = to const user: Doc<"users"> = (what happens now, do you still have the same issue?) Even if either resolves the issue, if you could share a repo with the reproduction, that would be super hepful as I would love to know why adding the field changes things.
cornpopguy
cornpopguyOP9mo ago
using Doc<"users"> (I have it imported as Document_, just to avoid confusion).
No description
cornpopguy
cornpopguyOP9mo ago
And with using .doc() the same issue persists as before.
Michal Srb
Michal Srb9mo ago
For the second point, you need to annotate where you have your runQuery in the action, not inside of the query.
cornpopguy
cornpopguyOP9mo ago
Also tried still the same issue.
Michal Srb
Michal Srb9mo ago
Could you put together a repo on GitHub that I can clone and replicate the error?
cornpopguy
cornpopguyOP9mo ago
Sure, also just a quick question, I just recently updated to convex-ents 0.7.3 from convex-ents 0.7.2. And I had to remake my upsert function to look like this:
No description
cornpopguy
cornpopguyOP9mo ago
from this:
No description
cornpopguy
cornpopguyOP9mo ago
Is this expected in the update? I need to use .getX() to be able to use .patch(), .delete() etc.. on the user in the new update. I will make the repro but I just want to clarify if this is intended in the update? I am unsure which version to use in the repro, please tell me if this is an expected behavior.
Michal Srb
Michal Srb9mo ago
That's probably broken in the update, but shouldn't affect your issue. Feel free to use the latest and I will look at both issues.
cornpopguy
cornpopguyOP9mo ago
Uh, if that is not expected then I will probaly share the repo which uses 0.7.2. I just have to make a few modifications to it. Sorry for taking so long.
Michal Srb
Michal Srb9mo ago
No worries, no rush
cornpopguy
cornpopguyOP9mo ago
Is it okay if I share slightly modified version of my full repo only with you? Okay, so Imade a heavly stripped down version of the app which has nearly the same functionality and is in english. Here is the repro repo: https://github.com/kanepop/private-repro-convex I am pretty new to convex so if you want if find something to be improved or use better practices feel free to tell me. I sent you an invite did you check it out?
Michal Srb
Michal Srb9mo ago
I got the invite, looking
cornpopguy
cornpopguyOP9mo ago
Thank you very much.
Michal Srb
Michal Srb9mo ago
I cloned the repo and ran npm i. Then I ran npx tsc and I got no error. I also have no error in VS Code. Which TypeScript version are you using? You can click on the {} button as the screenshot shows. You must have 5.4.x at least. (If you update VS Code it will come with 5.4.x)
No description
cornpopguy
cornpopguyOP9mo ago
Well, oh, it is a dev version. VS Code had this set for me as default.
No description
cornpopguy
cornpopguyOP9mo ago
No description
cornpopguy
cornpopguyOP9mo ago
Even after switching to 5.4.5 it persists. Also sorry but I forgot to add this to the schema: .field('subscriptionId', v.optional(v.string())),. If you add this field it starts happening I will make a commit with the field added to the schema.
Michal Srb
Michal Srb9mo ago
Right, I forgot that, got it reproed
cornpopguy
cornpopguyOP9mo ago
I made the commit. Also feel free to ping me when you find the issue. And again thank you very much.
Michal Srb
Michal Srb9mo ago
Ok, so part of the problem is exactOptionalPropertyTypes in your convex/tsconfig.json. I'm super curious where you got your tsconfig and eslint setup from. It's trying to be super super strict? Yup, I think removing that setting fixes things and you can use convex-ents@0.7.4
cornpopguy
cornpopguyOP9mo ago
I used these configs with a friend when we were working on project a while ago. Oh, a such stupid mistake haha, I should have not blindly used those config. I am sorry for using up your time on stupid issues. And again thank you.
Michal Srb
Michal Srb9mo ago
We spent some time optimizing these templates: https://github.com/get-convex/templates/tree/mike/redesign/template-nextjs-clerk-shadcn So they have pretty good eslintrc and tsconfig setups (not overly strict - but you can make it stricter by deleting the overrides in the eslintrc as hinted).
GitHub
templates/template-nextjs-clerk-shadcn at mike/redesign · get-conve...
Contribute to get-convex/templates development by creating an account on GitHub.
cornpopguy
cornpopguyOP9mo ago
Will take a look, thanks.
ampp
ampp8mo ago
do you know if anyone is using the new "flat" setup with eslint.config.js / mjs ?
ampp
ampp8mo ago
We are just having a nightmare trying to get eslint to work on the convex folder. i have double checked, triple checked. debugged, vscode shows this error. Error: Could not find config file. in the eslint output but things like eslint -debug are fine. probably like this: https://github.com/eslint/eslint/issues/17784 . i feel like mono-repos are just designed for punishment.
GitHub
Bug: Error: Could not find config file. · Issue #17784 · eslint/esl...
Environment Node version: v16.20.0 npm version: v9.6.6 Local ESLint version: Not found Global ESLint version: Not found Operating System: darwin 23.0.0 What parser are you using? Other What did you...
Michal Srb
Michal Srb8mo ago
@ampp I’m not familiar with “flat” setup. Monorepos are indeed tricky atm.
cornpopguy
cornpopguyOP8mo ago
ESLint's new config system, Part 2: Introduction to flat config - E...
A pluggable and configurable linter tool for identifying and reporting on patterns in JavaScript. Maintain your code quality with ease.

Did you find this page helpful?