Repository: nestjs/swagger
Pull request #3400 (2025-04-14 09:41:23)

feat(schema): add duplicate DTO detection in schema exploration
## PR Checklist
Please check if your PR fulfills the following requirements:

- [x] The commit message follows our guidelines: https://github.com/nestjs/nest/blob/master/CONTRIBUTING.md
- [x] Tests for the changes have been added (for bug fixes / features)
- [ ] Docs have been added / updated (for bug fixes / features)


## PR Type
What kind of change does this PR introduce?

<!-- Please check the one that applies to this PR using "x". -->
- [x] Bugfix
- [ ] Feature
- [ ] Code style update (formatting, local variables)
- [ ] Refactoring (no functional changes, no api changes)
- [ ] Build related changes
- [ ] CI related changes
- [ ] Other... Please describe:

## What is the current behavior?
<!-- Please describe the current behavior that you are modifying, or link to a relevant issue. -->

Issue Number: https://github.com/nestjs/swagger/issues/3398


## What is the new behavior?

Whenever a dto with the same name and different schema is detected, an error would be thrown.


## Does this PR introduce a breaking change?
- [x] Yes
- [ ] No

<!-- If this PR contains a breaking change, please describe the impact and migration path for existing applications below. -->

Projects with duplicate DTO names with different schemas will have to adjust the duplicate dtos.



Patch:
diff --git a/lib/services/schema-object-factory.ts b/lib/services/schema-object-factory.ts
index 821c726f..eac18bf5 100644
--- a/lib/services/schema-object-factory.ts
+++ b/lib/services/schema-object-factory.ts
@@ -1,7 +1,8 @@
-import { Type } from '@nestjs/common';
+import { Logger, Type } from '@nestjs/common';
 import { isUndefined } from '@nestjs/common/utils/shared.utils';
 import {
   flatten,
+  isEqual,
   isFunction,
   isString,
   keyBy,
@@ -310,6 +311,15 @@ export class SchemaObjectFactory {
     if (typeDefinitionRequiredFields.length > 0) {
       typeDefinition['required'] = typeDefinitionRequiredFields;
     }
+
+    if (schemas[schemaName] && !isEqual(schemas[schemaName], typeDefinition)) {
+      Logger.error(
+        `Duplicate DTO detected: "${schemaName}" is defined multiple times with different schemas.\n` +
+          `Consider using unique class names or applying @ApiExtraModels() decorator with custom schema names.\n` +
+          `Note: This will throw an error in the next major version.`
+      );
+    }
+
     schemas[schemaName] = typeDefinition;
 
     return schemaName;


Test patch:
diff --git a/test/services/schema-object-factory.spec.ts b/test/services/schema-object-factory.spec.ts
index f4c1f689..e86ad9e4 100644
--- a/test/services/schema-object-factory.spec.ts
+++ b/test/services/schema-object-factory.spec.ts
@@ -1,4 +1,5 @@
 import { ApiExtension, ApiProperty, ApiSchema } from '../../lib/decorators';
+import { Logger } from '@nestjs/common';
 import {
   BaseParameterObject,
   SchemasObject
@@ -176,6 +177,70 @@ describe('SchemaObjectFactory', () => {
       });
     });
 
+    it('should log an error when detecting duplicate DTOs with different schemas', () => {
+      const loggerErrorSpy = jest.spyOn(Logger, 'error').mockImplementation();
+      const schemas: Record<string, SchemasObject> = {};
+
+      class DuplicateDTO {
+        @ApiProperty()
+        property1: string;
+      }
+
+      schemaObjectFactory.exploreModelSchema(DuplicateDTO, schemas);
+
+      class DuplicateDTOWithDifferentSchema {
+        @ApiProperty()
+        property2: string;
+      }
+
+      Object.defineProperty(DuplicateDTOWithDifferentSchema, 'name', {
+        value: 'DuplicateDTO'
+      });
+
+      schemaObjectFactory.exploreModelSchema(
+        DuplicateDTOWithDifferentSchema,
+        schemas
+      );
+
+      expect(loggerErrorSpy).toHaveBeenCalledWith(
+        `Duplicate DTO detected: "DuplicateDTO" is defined multiple times with different schemas.\n` +
+          `Consider using unique class names or applying @ApiExtraModels() decorator with custom schema names.\n` +
+          `Note: This will throw an error in the next major version.`
+      );
+
+      loggerErrorSpy.mockRestore();
+    });
+
+    it('should not throw an error or log error when detecting duplicate DTOs with the same schemas', () => {
+      const loggerErrorSpy = jest.spyOn(Logger, 'error').mockImplementation();
+      const schemas: Record<string, SchemasObject> = {};
+
+      class DuplicateDTO {
+        @ApiProperty()
+        property1: string;
+      }
+
+      schemaObjectFactory.exploreModelSchema(DuplicateDTO, schemas);
+
+      class DuplicateDTOWithSameSchema {
+        @ApiProperty()
+        property1: string;
+      }
+
+      Object.defineProperty(DuplicateDTOWithSameSchema, 'name', {
+        value: 'DuplicateDTO'
+      });
+
+      schemaObjectFactory.exploreModelSchema(
+        DuplicateDTOWithSameSchema,
+        schemas
+      );
+
+      expect(loggerErrorSpy).not.toHaveBeenCalled();
+
+      loggerErrorSpy.mockRestore();
+    });
+
     it('should create openapi schema', () => {
       const schemas: Record<string, SchemasObject> = {};
       const schemaKey = schemaObjectFactory.exploreModelSchema(