samchon / nestia

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

Providing a schema component name through a comment notation #823

Closed RobbyUitbeijerse closed 5 months ago

RobbyUitbeijerse commented 5 months ago

Hey there! Great work on this, we're having a lot of fun nestia compared to our current solution (zod based dtos and swagger)

We're currently running into something that I couldn't yet find in other issues/the docs.

Let's say we have the following response model typed. I'm importing a model from prisma and using an intersection to add another prisma model to it.

import type { Developer, DeveloperRole } from '@prisma/client';

export interface ListDevelopersResponse {
  data: (DeveloperRole & {
    developer: Developer;
  })[];
}

this will lead to the following swagger schema output, mind the $ref property here. Note that technically, all the properties are there, so everything is being processed as expected - but the component names aren't very nice looking 😁

  "components": {
    "schemas": {
      "ListDevelopersResponse": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DeveloperRoledeveloperidstringnamestringnullemailstringcreatedAtDatelastSeenAtDatenullexternalIdstringnull"
            }
          }
        },
        "nullable": false,
        "required": [
          "data"
        ]
      },
      "DeveloperRoledeveloperidstringnamestringnullemailstringcreatedAtDatelastSeenAtDatenullexternalIdstringnull": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "developerId": {
            "type": "string"
          },
          "gameId": {
            "type": "string"
          },

If I change our response model to the following, the output is great:

type Developer = {
  email: string;
};

type DeveloperRole = {
  id: string;
  gameId: string;
  developerId: string;
  developer: Developer;
};

export interface ListDevelopersResponse {
  data: DeveloperRole[];
}

I could see there being a challenge here with inferring the intersection. Is there something we are currently missing here? I was wondering if for these scenarios, enforcing a schema component name through a comment notation could save the day, something like:

export interface ListDevelopersResponse {
  /**
   * @component DeveloperRoleItem
   */
  data: (DeveloperRole & {
    developer: Developer;
  })[];
}

If I'm missing something big here that causes the weird looking component name being generated, let me know!

samchon commented 5 months ago

What about defining the actual type DeveloperRoleItem?

export type DeveloperRoleItem = ArrDeveloperRole & { developer: Developer; };
RobbyUitbeijerse commented 5 months ago

Hey there! While that does solve the immediate issue, I do feel it's a bit ambiguous to "require" extra boilerplate to get to sensible output by default.

Wouldn't it make more sense to generate schema components for the entire response model at once if there's something in the schema component tree can't be figured out?

For example - this:

export type ListDevelopersResponse = {
  data: (DeveloperRole & {
    developer: Developer;
  })[];
}

Resulting in something like this, rather than a concatenated string representation of all properties and their types: (screenshot is from our current set-up using @nestjs/swagger)

Screenshot 2024-03-05 at 12 00 31

happy to hear your thoughts on this one

samchon commented 5 months ago

The type name comes from TypeScript compiler API, so I cannot determine it as I want \o/.