samchon / nestia

NestJS Helper Libraries + TypeScript OpenAPI generator
https://nestia.io/
MIT License
1.79k stars 92 forks source link

Question: Using Native TypeScript Types vs. Zod/Prisma Types with Nestia Swagger Docs #748

Closed Lotfi-Arif closed 8 months ago

Lotfi-Arif commented 8 months ago

Question

Hello, I'm currently working on a NestJS project that utilizes Prisma Client for ORM and Zod for data validation. I've been trying to integrate Nestia for Swagger documentation generation, but I'm facing challenges with Nestia's ability to detect and use types generated by Prisma Client and schemas inferred by Zod. I was wondering if there's a recommended approach or existing solution to integrate these effectively with Nestia's Swagger documentation generation.

Detailed Explanation

In my project, models are defined using Prisma Client and validated with Zod. However, the Swagger documentation generated by Nestia doesn't accurately reflect these types. When I switch to native TypeScript types defined manually, Nestia generates the documentation correctly.

I'm particularly interested in:

  1. Any best practices or patterns that are recommended for ensuring that Nestia correctly interprets and integrates Prisma Client generated types and Zod inferred schemas in the Swagger documentation.
  2. If there are known limitations or challenges with this integration, and any potential workarounds that might exist.
  3. Any plans for future updates or enhancements in Nestia that will improve this integration.

Context

This issue is significant for our project, as we rely on Prisma Client for ORM and Zod for validation. While native TypeScript types are a workaround, they don't offer the same level of convenience and functionality as the generated types from Prisma and Zod. We're seeking a more streamlined approach that allows for seamless integration.

Additional Information

Any insights, advice, or resources on this matter would be greatly appreciated. If more information or specific examples from our codebase are needed to better understand the issue, I'd be happy to provide them.

Thank you for your time and support in addressing this query.

samchon commented 8 months ago

image

As you know, the 1st principle of typia and nestia is not to writing any extra schema like intro page.

It's not a matter to find the best way by yourself, but I don't want to do it, prohibiting the 1st principle \o/.

By the way, if you want to utilize the original type of Prisma, then just use it directly. It is the pure TypeScript type. Also, even when using the zod schema, you can still utilize it through Infer feature of the zod, because it is also the pure TypeScript type. Furthermore, if you turn on the clone mode of SDK generator, you can clone pure interface DTO types from them.

https://nestia.io/docs/sdk/sdk/#configuration

Lotfi-Arif commented 8 months ago

Yes i understand the principles of nestia which is why i am using it but i did not expect for prisma and zod schema types not to work, Here is some specific details on what i am going through:

Details

I am using Prisma for ORM, and Zod for data validation. I've run into a problem where Nestia fails to recognize and use types when they are either generated by Prisma or inferred from Zod schemas for the Swagger documentation. However, it works correctly when I define the types manually in TypeScript.

Working Scenario (Manual TypeScript Types)

When I define a type manually in TypeScript, Nestia successfully generates the Swagger documentation:

export type User = {
  id: number;
  email: string;
  password: string;
  name: string | null;
  createdAt: Date;
  updatedAt: Date;
};

@TypedRoute.Post()
create(@TypedBody() createUserDto: Prisma.UserCreateInput): Promise<User> {
  return this.userService.create(createUserDto);
}

Non-Working Scenario (Zod Inferred Types)

When using Zod to define a schema and then inferring the TypeScript type from it, Nestia fails to generate the Swagger documentation:

import * as z from 'zod';

export const userSchema = z.object({
  id: z.number(),
  email: z.string(),
  password: z.string(),
  name: z.string().nullable(),
  createdAt: z.date(),
  updatedAt: z.date(),
});

export type User = z.infer<typeof userSchema>;

@TypedRoute.Post()
create(@TypedBody() createUserDto: Prisma.UserCreateInput): Promise<User> {
  return this.userService.create(createUserDto);
}

Non-Working Scenario (Prisma Generated Types)

Similarly, when using types generated by Prisma, Nestia also fails to generate the Swagger documentation:

import { Prisma, User } from '@prisma/client';

// Example Prisma-generated User Model:
export type User = $Result.DefaultSelection<Prisma.$UserPayload>
// ...other properties...

@TypedRoute.Post()
create(@TypedBody() createUserDto: Prisma.UserCreateInput): Promise<User> {
  return this.userService.create(createUserDto);
}

The error report i am getting from nestia:

Analyzing source codes
-----------------------------------------------------------
 Nestia Error Report
-----------------------------------------------------------
src/user/user.controller.ts - UserController.create()
  - implicit (unnamed) return type.
src/user/user.controller.ts - UserController.update()
  - implicit (unnamed) return type.
src/user/user.controller.ts - UserController.remove()
  - implicit (unnamed) return type.
Warning: run-commands command "npx nestia swagger" exited with non-zero status code

Steps to Reproduce

  1. Set Up NestJS with Nestia, Prisma, and Zod: Create a NestJS project, integrating Nestia for Swagger documentation, Prisma for ORM, and Zod for validation.

  2. Define Models and Validation: Define data models using Prisma and validation schemas using Zod. Infer TypeScript types from Zod schemas.

  3. Implement Controllers with Different Type Definitions: Implement the controllers using:

    • Manually defined TypeScript types.
    • Types inferred from Zod schemas.
    • Types generated by Prisma.
  4. Generate Swagger Documentation: Run the Nestia Swagger generation command.

  5. Observe the Difference in Behavior: Note that Nestia successfully generates documentation for manually defined types but fails for types from Zod or Prisma.

samchon commented 8 months ago

Make me a reproducible repo, then I will test.

Lotfi-Arif commented 8 months ago

I can give you a repo now which i am working on to learn nestia: https://github.com/Lotfi-Arif/TradeTrove

samchon commented 8 months ago

It is just a trick, change like below, then it works.

As type X = Y statement means an alias type definition, your type User = z.infer<typeof userSchema> statement means that always copying and pasting the z.infer<typeof userSchema> type statement everywhere the type User being utilized. If you change it to a new interface statement, the problem being solved.

The z.infer is also an alias type of implicit type of TMP. So it is blocked.

The reason why blocking the implicit type is for the SDK purpose (client developer needs explicit type definition)

export interface User extends z.infer<typeof userSchema> {}
Lotfi-Arif commented 8 months ago

Oh i am guessing it is the same case for prisma generated types. I think i understand the solution, Thanks for the help!