I've just been debugging why my Zod wasn't working with the zodios-express library. And it looks like by default zodios-express removes any validation added by refine and superRefine callbacks.
I was wondering if this was intentional? And if it was, can it be documented please?
For me the fix was to enable the transform flag on the zodiosApp function options so that the original "request" schema is used without any modifications by the library.
This turned out to be a trivial fix, but it wasn't very intuitive why it worked this way in the library as I did need to dig into why my validation schema didn't work out of the box.
// schema.ts
import { z } from 'zod';
export const myRequestSchema = z
.object({
needsA: z.boolean(),
data: z.record(z.string(), z.string()),
})
.refine(({ needsA, data }) => {
return !needsA || Object.keys(data).includes('a');
}, "Property 'a' is expected within 'data' record when 'needsA' is enabled.");
and your Express app has an endpoint which uses it via zodiosApp (something like this):
// api.ts
import { myRequestSchema } from './schema';
import { z } from 'zod';
import { makeApi } from '@zodios/core';
import { zodiosApp } from '@zodios/express';
const app = zodiosApp(
makeApi([
{
method: 'post',
path: '/my-request',
response: z.any(),
alias: 'myRequest',
parameters: [
{
name: 'body',
type: 'Body',
schema: myRequestSchema,
},
],
},
])
);
app.post('/my-request', (req, res) => {
if (req.body.needsA) {
// Note: this error shouldn't happen, if caught by the validation logic in the Zod schema.
if (req.body.data.a == null) throw new Error('This has broken');
}
res.json({ message: 'All good' });
});
const PORT = 3001;
app.listen(PORT, () => {
console.log(`Listening on port ${PORT}...`);
});
then making a request which has a body of:
{
"needsA": true,
"data": {}
}
should fail validation. But it doesn't, unless options of {transform: true} are passed to zodiosApp.
Hi,
I've just been debugging why my Zod wasn't working with the zodios-express library. And it looks like by default zodios-express removes any validation added by
refine
andsuperRefine
callbacks.I was wondering if this was intentional? And if it was, can it be documented please?
For me the fix was to enable the
transform
flag on thezodiosApp
function options so that the original "request" schema is used without any modifications by the library.This turned out to be a trivial fix, but it wasn't very intuitive why it worked this way in the library as I did need to dig into why my validation schema didn't work out of the box.
Example:
Dependency versions:
zod = 3.23.8 @zodios/core = 10.9.6 @zodios/express = 10.6.1
refine
:zodiosApp
(something like this):should fail validation. But it doesn't, unless options of
{transform: true}
are passed tozodiosApp
.