nestjs / swagger

OpenAPI (Swagger) module for Nest framework (node.js) :earth_americas:
https://nestjs.com
MIT License
1.69k stars 475 forks source link

Check schema types in the ApiResponse for matching the endpoint’s ReturnType #2552

Closed isqua closed 1 year ago

isqua commented 1 year ago

Is there an existing issue that is already proposing this?

Is your feature request related to a problem? Please describe it

Now I can declare a schema in the @ApiResponse decorator, but return data that does not correspond to it at all. E.g.:

import { Controller, Get } from '@nestjs/common';
import { ApiResponse } from '@nestjs/swagger';

// Schema is declaring an object with properties id and count
const ReadDemoSchema = {
    type: 'object',
    properties: {
        id: { type: 'string' },
        count: { type: 'number' },
    },
    required: ['id', 'count']
};

@Controller({ path: '/demo' })
export class DemoController {
    @Get('/')
    @ApiResponse({ schema: ReadDemoSchema })
    public async read() {
        // Endpoint returns an array of items
        return [
            { name: 'foo', total: 4 },
            { name: 'bar', total: 2 },
        ];
    }
}

Describe the solution you'd like

It would be great if the schema could declare a type of data, and there would be type checking, that the returned data corresponds this type. E.g. in ajv JSONSchemaType is generic, so I could describe a schema for a specific type:

import { Controller, Get } from '@nestjs/common';
import { ApiResponse } from '@nestjs/swagger';
import type { JSONSchemaType } from 'ajv';

// My response type
type ReadDemoDTO = {
    id: string;
    count: number;
}

// Schema for the response type
const ReadDemoSchema: JSONSchemaType<ReadDemoDTO> = {
    type: 'object',
    properties: {
        id: { type: 'string' },
        count: { type: 'number' },
    },
    required: ['id', 'count']
};

@Controller({ path: '/demo' })
export class DemoController {
    @Get('/')
    // Using the schema for swagger
    @ApiResponse({ schema: ReadDemoSchema })
    //                     ^ I would like to get a type error here
    public async read() {
        //                   because the response does not correspond the ReadDemoSchema
        return [
            { name: 'foo', total: 4 },
            { name: 'bar', total: 2 },
        ];
    }
}

I’d like to get some TS Error about mismatching, e.g.:

Unable to resolve signature of method decorator when called as an expression.
  Argument of type 'TypedPropertyDescriptor<() => Promise<{ name: string; total: number; }[]>>' is not assignable to parameter of type 'TypedPropertyDescriptor<ApiControllerMethod<ReadDemoDTO, any[]>>'.
    Types of property 'value' are incompatible.
      Type '() => Promise<{ name: string; total: number; }[]>' is not assignable to type 'ApiControllerMethod<ReadDemoDTO, any[]>'.

Teachability, documentation, adoption, migration strategy

This issue can be solved by making JSONSchema type and ApiResponse decorator generic. I’ve made some type wrappers at my project to solve the issue locally:

import { ApiResponse as SwaggerApiResponse } from '@nestjs/swagger';

import type { JSONSchemaType } from 'ajv';
import type { ApiResponseSchemaHost as SwaggerApiResponseSchemaHost } from '@nestjs/swagger';

export interface ApiResponseSchemaHost<T> extends Omit<SwaggerApiResponseSchemaHost, 'schema'> {
    schema: JSONSchemaType<T>;
}

export type ApiControllerMethod<TResult, TArgs extends any[] = any[]> = (...args: TArgs) => Promise<TResult>;

type Decorator<T> = (
    target: Object,
    propertyKey: string | symbol,
    descriptor: TypedPropertyDescriptor<ApiControllerMethod<T>>,
) => TypedPropertyDescriptor<ApiControllerMethod<T>> | void;

export const ApiResponse = <T>(opts: ApiResponseSchemaHost<T>): Decorator<T> => {
    return SwaggerApiResponse(opts);
};

I’m using JSONSchemaType from ajv, but it can be solved by any generic JSON Schema types.

What is the motivation / use case for changing the behavior?

Of course, first of all I change the logic in a controller, cover the endpoint behaviour with tests and so on. And I forget to change corresponding schema, which affects the Swagger docs.

kamilmysliwiec commented 1 year ago

Thanks for your suggestion!

There are no plans to implement it in the foreseeable future.

If you think your request could live outside Nest's scope, we'd encourage you to collaborate with the community on publishing it as an open source package.