idempotent
idempotent2y 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
61 Replies
idempotent
idempotentOP2y ago
Here is my schema.ts without it:
No description
erquhart
erquhart2y 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.
idempotent
idempotentOP2y 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
erquhart2y ago
In the error from your screenshot, user.stripeUserId is Value | undefined Do you have an error where user itself is union w/ undefined?
idempotent
idempotentOP2y 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.
idempotent
idempotentOP2y ago
When I remove the optional from the subscriptionId everything works fine:
No description
idempotent
idempotentOP2y ago
When I do not make the subscriptionid optional then no, but when I do:
No description
idempotent
idempotentOP2y 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
erquhart2y 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
idempotent
idempotentOP2y ago
No description
erquhart
erquhart2y ago
yeah that should work
idempotent
idempotentOP2y ago
with optional
No description
erquhart
erquhart2y ago
May have to wait for @Michal Srb to evaluate, not certain if this is a bug but it seems like it.
idempotent
idempotentOP2y ago
well If do it like in that screenshot, that makes everything else Value | undefined
ampp
ampp2y 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
erquhart2y ago
Not sure what you mean here, they're just defining a schema
ampp
ampp2y 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
erquhart2y 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 Srb2y ago
Looks like a bug
idempotent
idempotentOP2y ago
Even after changing my schema to following:
No description
idempotent
idempotentOP2y ago
the issue persists
No description
Michal Srb
Michal Srb2y 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?
idempotent
idempotentOP2y ago
No description
idempotent
idempotentOP2y 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 Srb2y ago
I'll have a look later today, does look like a bug in Ents
idempotent
idempotentOP2y 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 Srb2y ago
It shouldn't matter which syntax is used. Fields from either syntax can be used in indexes.
idempotent
idempotentOP2y ago
Did you figure this out? Are the issues on my side?
Michal Srb
Michal Srb2y 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).
idempotent
idempotentOP2y 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.
idempotent
idempotentOP2y ago
Here is a demo video:
Michal Srb
Michal Srb2y 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.
idempotent
idempotentOP2y ago
using Doc<"users"> (I have it imported as Document_, just to avoid confusion).
No description
idempotent
idempotentOP2y ago
And with using .doc() the same issue persists as before.
Michal Srb
Michal Srb2y ago
For the second point, you need to annotate where you have your runQuery in the action, not inside of the query.
idempotent
idempotentOP2y ago
Also tried still the same issue.
Michal Srb
Michal Srb2y ago
Could you put together a repo on GitHub that I can clone and replicate the error?
idempotent
idempotentOP2y 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
idempotent
idempotentOP2y ago
from this:
No description
idempotent
idempotentOP2y 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 Srb2y 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.
idempotent
idempotentOP2y 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 Srb2y ago
No worries, no rush
idempotent
idempotentOP2y 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 Srb2y ago
I got the invite, looking
idempotent
idempotentOP2y ago
Thank you very much.
Michal Srb
Michal Srb2y 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
idempotent
idempotentOP2y ago
Well, oh, it is a dev version. VS Code had this set for me as default.
No description
idempotent
idempotentOP2y ago
No description
idempotent
idempotentOP2y 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 Srb2y ago
Right, I forgot that, got it reproed
idempotent
idempotentOP2y 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 Srb2y 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
idempotent
idempotentOP2y 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 Srb2y 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.
idempotent
idempotentOP2y ago
Will take a look, thanks.
ampp
ampp2y ago
do you know if anyone is using the new "flat" setup with eslint.config.js / mjs ?
ampp
ampp2y 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 Srb2y ago
@ampp I’m not familiar with “flat” setup. Monorepos are indeed tricky atm.
idempotent
idempotentOP2y 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.
ballingt
ballingt2mo ago
@idempotent heads up that the latest convex alpha fixes this!

Did you find this page helpful?