-
Notifications
You must be signed in to change notification settings - Fork 23
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
zCustomFunction doesn't return a type when using runMutation/runQuery etc. #504
Comments
I'll take a look at this later on. But one note:
If the calling function is an action, you've always needed to use runQuery
/ runMutation since the action isn't in a transaction and doesn't have
access to the database (ctx.db doesn't exist).
But for mutations calling mutation/query, or a query calling a query, I'd
recommend pulling out the functions into helper functions instead of doing
`runQuery / runAction`.
Since it's just more overhead to do `runQuery`/`runMutation` as they start
a new subtransaction in a new isolated function (unless you need
subtransactions which is more rare)
https://docs.convex.dev/understanding/best-practices/#use-helper-functions-to-write-shared-code
|
Also, I just released `0.1.73` which updates the zod helpers and allows you
to specify `returns` validators which may help with the type inference when
TypeScript gives up
…On Tue, Mar 18, 2025 at 12:56 PM Ian Macartney ***@***.***> wrote:
I'll take a look at this later on. But one note:
If the calling function is an action, you've always needed to use runQuery
/ runMutation since the action isn't in a transaction and doesn't have
access to the database (ctx.db doesn't exist).
But for mutations calling mutation/query, or a query calling a query, I'd
recommend pulling out the functions into helper functions instead of doing
`runQuery / runAction`.
Since it's just more overhead to do `runQuery`/`runMutation` as they start
a new subtransaction in a new isolated function (unless you need
subtransactions which is more rare)
On Tue, Mar 18, 2025 at 3:17 AM Matteus Steinberg <
***@***.***> wrote:
> Since updating to Convex 1.20^ it has become "illegal" to execute Convex
> functions as a normal Typescript function ( await fooBar(ctx, { args }) )
> so the only correct approach is to run internal and external functions with
> the action helpers ( await runQuery(internal.foo.bar.fooBar, { args }) )
>
> When running runMutation(api/internal....) for a *zCustomFunction* it
> returns *any*, but when using the default built-in Convex function, the
> correct types get returned.
>
> Example of failing/missing type actions:
>
> */searchAgent/external.ts*
>
> import { action } from '../utils/customWrapper'
> import { internal } from '../_generated/api'
>
> export default action({
> args: {
> searchAgentId: zid('searchAgents')
> },
> handler: async (ctx, { searchAgentId }) => {
> const { runQuery } = ctx
> return await runQuery(internal.searchAgent.index.get, { searchAgentId })
> }
> })
>
> */searchAgent/index.ts*
>
> import { zid } from 'convex-helpers/server/zod'
> import { internalQuery } from '../utils/customWrapper'
>
> export const get = internalQuery({
> args: {
> searchAgentId: zid('searchAgents')
> },
> handler: async (ctx, { searchAgentId }) => {
> const { db } = ctx
>
> const searchAgent = await db.get(searchAgentId)
>
> return searchAgent
> }
> })
>
>
> */utils/customWrapper.ts*
>
> import { zCustomAction, zCustomMutation, zCustomQuery } from 'convex-helpers/server/zod'
> import { v } from 'convex/values'
> import {
> action as convexAction,
> internalAction as convexInternalAction,
> internalMutation as convexInternalMutation,
> internalQuery as convexInternalQuery,
> mutation as convexMutation,
> query as convexQuery
> } from '../_generated/server'
>
> export const mutation = zCustomMutation(convexMutation, {
> args: {
> posthogDistinctId: v.optional(v.string())
> },
> input: async (ctx, { posthogDistinctId }) => {
> return { ctx: { ...ctx, posthogDistinctId }, args: {} }
> }
> })
>
> export const query = zCustomQuery(convexQuery, {
> args: {
> posthogDistinctId: v.optional(v.string())
> },
> input: async (ctx, { posthogDistinctId }) => {
> return { ctx: { ...ctx, posthogDistinctId }, args: {} }
> }
> })
>
> export const action = zCustomAction(convexAction, {
> args: {
> posthogDistinctId: v.optional(v.string())
> },
> input: async (ctx, { posthogDistinctId }) => {
> return { ctx: { ...ctx, posthogDistinctId }, args: {} }
> }
> })
>
> export const internalMutation = zCustomMutation(convexInternalMutation, {
> args: {
> posthogDistinctId: v.optional(v.string())
> },
> input: async (ctx, { posthogDistinctId }) => {
> return { ctx: { ...ctx, posthogDistinctId }, args: {} }
> }
> })
>
> export const internalQuery = zCustomQuery(convexInternalQuery, {
> args: {
> posthogDistinctId: v.optional(v.string())
> },
> input: async (ctx, { posthogDistinctId }) => {
> return { ctx: { ...ctx, posthogDistinctId }, args: {} }
> }
> })
>
> export const internalAction = zCustomAction(convexInternalAction, {
> args: {
> posthogDistinctId: v.optional(v.string())
> },
> input: async (ctx, { posthogDistinctId }) => {
> return { ctx: { ...ctx, posthogDistinctId }, args: {} }
> }
> })
>
> —
> Reply to this email directly, view it on GitHub
> <#504>, or unsubscribe
> <https://github.com/notifications/unsubscribe-auth/AACZQW6JJQLNISVTIQWWNDL2U7XD7AVCNFSM6AAAAABZHXIN5GVHI2DSMVQWIX3LMV43ASLTON2WKOZSHEZDOOJTGAYTEMQ>
> .
> You are receiving this because you are subscribed to this thread.Message
> ID: ***@***.***>
> [image: MatteusSteinberg]*MatteusSteinberg* created an issue
> (get-convex/convex-helpers#504)
> <#504>
>
> Since updating to Convex 1.20^ it has become "illegal" to execute Convex
> functions as a normal Typescript function ( await fooBar(ctx, { args }) )
> so the only correct approach is to run internal and external functions with
> the action helpers ( await runQuery(internal.foo.bar.fooBar, { args }) )
>
> When running runMutation(api/internal....) for a *zCustomFunction* it
> returns *any*, but when using the default built-in Convex function, the
> correct types get returned.
>
> Example of failing/missing type actions:
>
> */searchAgent/external.ts*
>
> import { action } from '../utils/customWrapper'
> import { internal } from '../_generated/api'
>
> export default action({
> args: {
> searchAgentId: zid('searchAgents')
> },
> handler: async (ctx, { searchAgentId }) => {
> const { runQuery } = ctx
> return await runQuery(internal.searchAgent.index.get, { searchAgentId })
> }
> })
>
> */searchAgent/index.ts*
>
> import { zid } from 'convex-helpers/server/zod'
> import { internalQuery } from '../utils/customWrapper'
>
> export const get = internalQuery({
> args: {
> searchAgentId: zid('searchAgents')
> },
> handler: async (ctx, { searchAgentId }) => {
> const { db } = ctx
>
> const searchAgent = await db.get(searchAgentId)
>
> return searchAgent
> }
> })
>
>
> */utils/customWrapper.ts*
>
> import { zCustomAction, zCustomMutation, zCustomQuery } from 'convex-helpers/server/zod'
> import { v } from 'convex/values'
> import {
> action as convexAction,
> internalAction as convexInternalAction,
> internalMutation as convexInternalMutation,
> internalQuery as convexInternalQuery,
> mutation as convexMutation,
> query as convexQuery
> } from '../_generated/server'
>
> export const mutation = zCustomMutation(convexMutation, {
> args: {
> posthogDistinctId: v.optional(v.string())
> },
> input: async (ctx, { posthogDistinctId }) => {
> return { ctx: { ...ctx, posthogDistinctId }, args: {} }
> }
> })
>
> export const query = zCustomQuery(convexQuery, {
> args: {
> posthogDistinctId: v.optional(v.string())
> },
> input: async (ctx, { posthogDistinctId }) => {
> return { ctx: { ...ctx, posthogDistinctId }, args: {} }
> }
> })
>
> export const action = zCustomAction(convexAction, {
> args: {
> posthogDistinctId: v.optional(v.string())
> },
> input: async (ctx, { posthogDistinctId }) => {
> return { ctx: { ...ctx, posthogDistinctId }, args: {} }
> }
> })
>
> export const internalMutation = zCustomMutation(convexInternalMutation, {
> args: {
> posthogDistinctId: v.optional(v.string())
> },
> input: async (ctx, { posthogDistinctId }) => {
> return { ctx: { ...ctx, posthogDistinctId }, args: {} }
> }
> })
>
> export const internalQuery = zCustomQuery(convexInternalQuery, {
> args: {
> posthogDistinctId: v.optional(v.string())
> },
> input: async (ctx, { posthogDistinctId }) => {
> return { ctx: { ...ctx, posthogDistinctId }, args: {} }
> }
> })
>
> export const internalAction = zCustomAction(convexInternalAction, {
> args: {
> posthogDistinctId: v.optional(v.string())
> },
> input: async (ctx, { posthogDistinctId }) => {
> return { ctx: { ...ctx, posthogDistinctId }, args: {} }
> }
> })
>
> —
> Reply to this email directly, view it on GitHub
> <#504>, or unsubscribe
> <https://github.com/notifications/unsubscribe-auth/AACZQW6JJQLNISVTIQWWNDL2U7XD7AVCNFSM6AAAAABZHXIN5GVHI2DSMVQWIX3LMV43ASLTON2WKOZSHEZDOOJTGAYTEMQ>
> .
> You are receiving this because you are subscribed to this thread.Message
> ID: ***@***.***>
>
|
Any update on this? |
@MatteusSteinberg - it should be fixed in the latest version of |
@ianmacartney - I'm running v0.1.75 and the type is still not inferred |
Sorry to hear that, I can try to reproduce it again later this week, but it was working for me. If you made a repo with a minimal reproduction it'd be quicker to debug. Thanks! |
@MatteusSteinberg the issue you're seeing is due to circular type references, not due to the zod helpers. I copied your functions into a project and you don't get the issue if you take out this: return await runQuery(internal.searchAgent.index.get, { searchAgentId }) Unfortunately even though they're in different files, they all import from export default action({
args: {
searchAgentId: zid("searchAgents"),
},
handler: async (ctx, { searchAgentId }): Promise<Doc<"searchAgents"> | null> => {
const { runQuery } = ctx;
return await runQuery(internal.zodExample.get, { searchAgentId });
},
}); |
This tanks the developer experience of using the zod helpers if I have to run through my whole codebase and start annotating the return type for each function manually. Do you plan on having the issue fixed or will it be like this for the foreseeable future if I want to use the zod helpers? |
This is not specific to the zod helper, this is due to the global api
object. Does specifying a returns validator break the cycle for you? I
haven’t put energy into that but for people who want to do return
validation that could be another way to break the cycle.
I have some ideas for workarounds to segment the types but again it is not
specific to the zod helper, just to Convex overall.
You only need to annotate functions when their return type depends on other
functions
…On Wed, Mar 26, 2025 at 5:02 AM Matteus Steinberg ***@***.***> wrote:
This tanks the developer experience of using the zod helpers if I have to
run through my whole codebase and start annotating the return type for each
function manually.
Do you plan on having the issue fixed or will it be like this for the
foreseeable future if I want to use the zod helpers?
—
Reply to this email directly, view it on GitHub
<#504 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AACZQW2ITITZUBVCLHIHX732WKCKPAVCNFSM6AAAAABZHXIN5GVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDONJUGE3TAMJUGY>
.
You are receiving this because you modified the open/close state.Message
ID: ***@***.***>
[image: MatteusSteinberg]*MatteusSteinberg* left a comment
(get-convex/convex-helpers#504)
<#504 (comment)>
This tanks the developer experience of using the zod helpers if I have to
run through my whole codebase and start annotating the return type for each
function manually.
Do you plan on having the issue fixed or will it be like this for the
foreseeable future if I want to use the zod helpers?
—
Reply to this email directly, view it on GitHub
<#504 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AACZQW2ITITZUBVCLHIHX732WKCKPAVCNFSM6AAAAABZHXIN5GVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDONJUGE3TAMJUGY>
.
You are receiving this because you modified the open/close state.Message
ID: ***@***.***>
|
I thought it should work when you break it up between files, maybe my repro
wasn’t the best because the function defs I think were in the one of the
files.
…On Wed, Mar 26, 2025 at 11:04 AM Ian Macartney ***@***.***> wrote:
This is not specific to the zod helper, this is due to the global api
object. Does specifying a returns validator break the cycle for you? I
haven’t put energy into that but for people who want to do return
validation that could be another way to break the cycle.
I have some ideas for workarounds to segment the types but again it is not
specific to the zod helper, just to Convex overall.
You only need to annotate functions when their return type depends on
other functions
On Wed, Mar 26, 2025 at 5:02 AM Matteus Steinberg <
***@***.***> wrote:
> This tanks the developer experience of using the zod helpers if I have to
> run through my whole codebase and start annotating the return type for each
> function manually.
>
> Do you plan on having the issue fixed or will it be like this for the
> foreseeable future if I want to use the zod helpers?
>
> —
> Reply to this email directly, view it on GitHub
> <#504 (comment)>,
> or unsubscribe
> <https://github.com/notifications/unsubscribe-auth/AACZQW2ITITZUBVCLHIHX732WKCKPAVCNFSM6AAAAABZHXIN5GVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDONJUGE3TAMJUGY>
> .
> You are receiving this because you modified the open/close state.Message
> ID: ***@***.***>
> [image: MatteusSteinberg]*MatteusSteinberg* left a comment
> (get-convex/convex-helpers#504)
> <#504 (comment)>
>
> This tanks the developer experience of using the zod helpers if I have to
> run through my whole codebase and start annotating the return type for each
> function manually.
>
> Do you plan on having the issue fixed or will it be like this for the
> foreseeable future if I want to use the zod helpers?
>
> —
> Reply to this email directly, view it on GitHub
> <#504 (comment)>,
> or unsubscribe
> <https://github.com/notifications/unsubscribe-auth/AACZQW2ITITZUBVCLHIHX732WKCKPAVCNFSM6AAAAABZHXIN5GVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDONJUGE3TAMJUGY>
> .
> You are receiving this because you modified the open/close state.Message
> ID: ***@***.***>
>
|
I have split the code into multiple files and still have the same issue where no type is returned from the function. I've tried breaking up the dependencies across different files as suggested, setting a return validator, annotating a return type but I'm still experiencing the same problem with the type information not being properly propagated. Why should the issue be circular dependency? |
Here's some resources: Here's a repro with your code that I fixed by adding a return type: https://github.com/get-convex/convex-helpers/blob/repro/zod-return-type/convex/zod2.ts#L21 The cycle is
There are efforts to do some static codegen to break the cycle, and I have other ideas, but this is how it works currently. Sorry! |
Since updating to Convex 1.20^ it has become "illegal" to execute Convex functions as a normal Typescript function ( await fooBar(ctx, { args }) ) so the only correct approach is to run internal and external functions with the action helpers ( await runQuery(internal.foo.bar.fooBar, { args }) )
When running runMutation(api/internal....) for a zCustomFunction it returns
any
, but when using the default built-in Convex function, the correct types get returned.Example of failing/missing type actions:
/searchAgent/external.ts
/searchAgent/index.ts
/utils/customWrapper.ts
The text was updated successfully, but these errors were encountered: