Skip to content
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

Defining procedures with no input #5

Closed
dodas opened this issue Jun 7, 2022 · 10 comments
Closed

Defining procedures with no input #5

dodas opened this issue Jun 7, 2022 · 10 comments

Comments

@dodas
Copy link
Collaborator

dodas commented Jun 7, 2022

Defining a query/mutation with no input results in

TRPCError: [query.<queryName>] - Input parser expects a Zod validator

It's possible to workaround this by defining an empty input:

input: z.object({}),

However, that way an empty object is expected when calling the query via TRPC client.
It's a legit use-case to have queries/mutations with no input, right? Can we allow omitting input in such cases?

@jlalmes
Copy link
Owner

jlalmes commented Jun 7, 2022

Hi @dodas.

Yes you're right - the current workaround for no input is:

input: z.object({}),

Here's the reason for why Zod validators are required for both input and output parsers:

We could allow z.void() and/or z.undefined() if there is no input, nor path parameters? But to be honest, I think z.object({}) appears to be the best solution (this should probably be clearer in the docs).

@dodas
Copy link
Collaborator Author

dodas commented Jun 7, 2022

I currently have queries/mutations with no input and they work fine with TRPC Client, without having to pass an empty object explicitly.
My problem is that now with trpc-openapi, I will have to make a breaking change to our TRPC api by requesting an empty object to be passed in:

[{
    "code": "invalid_type",
    "expected": "object",
    "received": "undefined",
    "path": [],
    "message": "Required"
}]

Couldn't we fallback no input to z.object({}) internally in trpc-openapi?

@jlalmes
Copy link
Owner

jlalmes commented Jun 7, 2022

Couldn't we fallback no input to z.object({}) internally in trpc-openapi?

This is not possible.

I think this would be best solved by allowing this:

input: z.void(),

Now your tRPC API would continue working as intended and trpc-openapi will be aware that you're explicitly opting out of expecting any input for schema generation.

Thoughts?

@dodas
Copy link
Collaborator Author

dodas commented Jun 7, 2022

This is not possible.

Curious about this; care to explain why it wouldn't be possible, please?

Using z.void() sounds fine too, I just don't see much benefit being explicit about having no input.

@jlalmes
Copy link
Owner

jlalmes commented Jun 7, 2022

Curious about this; care to explain why it wouldn't be possible, please?

Zod validator (expects void)

.query('zodVoidInput', {
  input: z.void(),
  output: z.object({}),
  resolve: () => ({})
})
const inputParser = z.void();

Here inputParser instanceof ZodVoid therefore we can ignore input.

Custom func validator (expects string)

.query('customFnInput', {
  input: (raw: unknown) => {
    if (typeof raw !== 'string') {
      throw new TRPCError({
        code: 'BAD_REQUEST',
        message: 'Non string value',
      });
    }
    return raw;
  },
  output: z.object({}),
  resolve: () => ({})
})
const inputParser = (raw: unknown) => {
  if (typeof raw !== 'string') {
    throw new TRPCError({
      code: 'BAD_REQUEST',
      message: 'Non string value',
    });
  }
  return raw;
}

Here typeof inputParser === 'function' therefore we cannot generate an OpenAPI schema and must throw an error.

Omitted input

.query('omitInput', {
  output: z.object({}),
  resolve: () => ({})
})
const inputParser = (input: unknown) => {
  if (input != null) {
    throw new TRPCError({
      code: 'BAD_REQUEST',
      message: 'No input expected',
    });
  }
  return undefined;
}

As you can see typeof inputParser === 'function', we cannot differentiate between Custom func validator (above) and an omitted input, therefore we must throw an error in this case too.

@jlalmes
Copy link
Owner

jlalmes commented Jun 7, 2022

Couldn't we fallback no input to z.object({}) internally in trpc-openapi?

Procedures are constructed in trpc to have a fallback default inputParser function (not null/undefined), therefore this is not currently possible.

@jlalmes
Copy link
Owner

jlalmes commented Jun 7, 2022

Using z.void() sounds fine

I will recommend using z.void() in the docs, but z.undefined() and z.never() will be valid too.

@dodas
Copy link
Collaborator Author

dodas commented Jun 7, 2022

Ah I see, thanks.

Seems like this is a limitation of trpc and could be potentially solved if trpc used a constant fallback parser and exported it, instead of creating an anonymous fn, so we could compare references.

Anyway, I am totally fine with using z.void()/z.undefined()/z.never().

@jlalmes
Copy link
Owner

jlalmes commented Jun 7, 2022

We are actively working on tRPC v10 at the moment so I'll have a think & there may be a better solution after the next major!

@jlalmes
Copy link
Owner

jlalmes commented Jun 8, 2022

Hi @dodas.

Please upgrade to the newest release [email protected] so that you can use input: z.void(). Let me know if you have any issues.

Head's up that this release includes a breaking change - please see #11.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants