trpc-openapi
trpc-openapi copied to clipboard
Support for union or discriminatedUnion?
The following code produces a runtime error:
thing: publicProcedure
.meta({ openapi: { method: 'GET', path: '/api/thing' }})
.input( z.union([
z.object({ type: z.literal('first'), myVar: z.string() }),
z.object({ type: z.literal('another'), anotherVar: z.number() }),
z.object({ type: z.literal('third') }),
]))
.output( z.string() )
.query(({ ctx }) => "thing" ),
TRPCError: [query.thing] - Input parser must be a ZodObject
Is there any planned support for union or discriminatedUnion?
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
I looked into this. Looks like a non-trivial change since zod-to-json-schema
simply returns an anyOf
set whenever it sees a union with the openapi3
option.
To properly support OpenAPI3 style discriminators, one would have to either modify zod-to-json-schema
to support an openapi3 specific output (which would be a significant change for that package) or perform some pre/post processing in zodSchemaToOpenApiSchemaObject
.
I think the latter would be better for this library, specifically. However, I still need to investigate further what a preprocessing or post processing step would look like in practice. Open to ideas!
This would be a great feature. Unions are extremely useful for creating strict contracts and type narrowing. I definitely miss having this ability when working with openapi.
what happened this support? how can I tackle a discriminatedUnion without using it?
I wrote a hacky implementation to start a discussion, but realized that my needs were for nested discriminated unions, which are not supported in the OpenAPI 3 spec. I closed my PR, but someone feel free to take a look at the kernel of the PR and open a new one.
I don't understand the challenge since zod-to-json-schema
correctly parses unions? anyOf isn't what we want?
This is also triggered by z.array()
+1
I cannot use this library just because it lacks this feature. This is a necessary thing, but it seems like the library has not been functioning actively recently, could it be that it has died?
I wrote some logic to handle .input({ appId: z.union([z.number(), z.string()]) })
as originally (1) this would error about the union not being coercible (when technically everything within it is coercible), and additionally I (2) wrote some logic to ensure that the values within the input
that is produced are coerced into the correct type. I think this approach is fine with the zod-to-json-schema
anyOf
set mentioned above...?
Is there something that I'm misunderstanding about this requirement or is my situation a little different/simpler? I can make the PR if what I've done locally is somewhat beneficial.
+1
This issue could be hacked around like this
const unionSchema = z.discriminatedUnion('type', [
z.object({ type: z.literal('number'), number: z.number() }),
z.object({ type: z.literal('string'), string: z.string() }),
]);
const restSchema = z.object({
type: z.string(),
number: z.number().optional(),
string: z.string().optional(),
});
export const restRouter = router({
test: procedure
.meta({
openapi: {
method: 'GET',
path: '/test',
},
})
.input(restSchema)
.output(z.object({}))
.query(async ({ input }) => {
const validatedInput = await unionSchema.parseAsync(input);
return {};
}),
})
The code is still safe, just with a bit of duplication