`extendZodWithOpenApi` Not Working as Expected in NestJS
Closed this issue · 6 comments
Description:
I am trying to integrate the library into my NestJS application. According to the docs, the `extendZodWithOpenApi function should be called once at the entry point of the application. However, when I followed the instructions, I get the following error:
TypeError: z.string(...).email(...).openapi is not a function
Steps to Reproduce
- Installed @asteasolutions/zod-to-openapi using pnpm.
- Attempted to call
extendZodWithOpenApi
inside the bootstrap function inmain.ts
. - Additionally created an
OnModuleInit
service to callextendZodWithOpenApi
the NestJS way.
Code Examples
main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { extendZodWithOpenApi } from '@asteasolutions/zod-to-openapi';
import { z } from 'zod';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
extendZodWithOpenApi(z); // Initial attempt to extend Zod
await app.listen(3000);
}
bootstrap();
ZodToOpenApiService.ts
import { Injectable, OnModuleInit } from '@nestjs/common';
import { z } from 'zod';
import { extendZodWithOpenApi } from '@asteasolutions/zod-to-openapi';
@Injectable()
export class ZodToOpenApiService implements OnModuleInit {
onModuleInit() {
console.log('ZodToOpenApiService initialized');
extendZodWithOpenApi(z);
}
}
ZodToOpenApiModule.ts
import { Global, Module } from '@nestjs/common';
import { ZodToOpenApiService } from './zod-to-openapi.service';
@Global()
@Module({
providers: [ZodToOpenApiService],
exports: [ZodToOpenApiService],
})
export class ZodToOpenApiModule {}
Observations
- Calling
extendZodWithOpenApi
inside the bootstrap function did not work. - Creating an OnModuleInit service to call
extendZodWithOpenApi
also did not work. - Directly calling
extendZodWithOpenApi
in the DTO file works, but the documentation suggests this should only be done once at the entry point. - Despite the compilation throwing a
TypeError,
the.openapi
method becomes available in autocompletion by TypeScript whenextendZodWithOpenApi
is called from anywhere.
Expected Behavior
The extendZodWithOpenApi
function should be called once and make the .openapi()
method available on Zod schemas throughout the application.
Actual Behavior
The .openapi()
method is not available on Zod schemas when extendZodWithOpenApi
is called in main.ts
or within an OnModuleInit
service.
Environment
- NestJS Version: 10.3.8
- Node Version: v22.0.0
- @asteasolutions/zod-to-openapi Version: 7.0.0
- Zod Version: 3.23.8
Hey @Armadillidiid there are two things that I could try and suggest:
- Try and call extendZodWithOpenApi before actually creating the app in the bootstrap function.
- If that doesn't work then a patter that we've used and so have many other users of the library it to create a file (for example
lib/zod.ts
) where you do:
import { z } from 'zod';
extendZodWithOpenApi(z);
export { z }
and then you can use that instance of zod anywhere across your app to make sure it has been properly extended
I had already tried the first option and it didn't work. However, having one instance of Zod as you've said solved the problem. Thank you.
I'm facing a similar issue.
I used Cloudflare Chanfana embedded in an Astro project. Chanfana itself already calls extendZodWithOpenApi(z)
(here) but it wasn't working for me.
So I called it mysql in some entry index.ts and that worked.
But suddenly it wasn't working anymore.. and I have no idea why. I moved the extendZodWithOpenApi(z)
to another file and it worked again (for now?)...
Using the above solution to create that zod
instance in a file and use that everywhere, doesn't cause that impact if you have a dependency in your project that imports zod as well...?
I was also using a wrapper like:
import { z } from 'zod';
import { extendZodWithOpenApi } from '@asteasolutions/zod-to-openapi';
extendZodWithOpenApi(z);
export { z };
and it worked until today... after adding some code using zo it now consistently fails with:
Cannot read properties of undefined (reading 'openapi')
.. so I'm completely blocked now unfortunately.
In the code I only import z from my wrapper.
I will make a minimal reproducible project later to share here.
@marceloverdijk the error you mentioned sounds like using z.<someSchema>().openapi()
failed - which means the generated schema was undefined which is quite odd. And doesn't really sound like something in the realm of our library. However you can create a reproducible project and we can take a look try and give an idea if there is anything related to our usage.
Hi @AGalabov thx for your feedback, I appreciate it.
I've create a minimal reproducible project here: https://github.com/marceloverdijk/astro-chanfana-zod-openapi
The project has 1 main scheme:
import { z } from '@/api/utils/zod';
import { BookTypeSchema, IsbnSchema } from '@/api/schemas';
// prettier-ignore
export const BookSchema = z
.object({
id: z.number().int().openapi({ description: 'The unique identifier of the book.', example: 1 }),
isbn: IsbnSchema.openapi({ description: 'The unique ISBN of the book.', example: '978-0-7475-3269-9' }),
title: z.string().openapi({ description: 'The title of the book.', example: 'Harry Potter and the Philosopher\'s Stone' }),
type: BookTypeSchema.openapi({ description: 'The type of the book.', example: 'FANTASY' }),
author: z.string().openapi({ description: 'The author of the book.', example: 'J. K. Rowling' }),
})
.openapi('Book', { description: 'Representation of a book.' });
I already noticed it goes wrong with re-using the IsbnSchema
and BookTypeSchema
schemas, but I cannot explain why.