Skip to content

OAS3 schema with no required and an allOf property produces wrongly-required fields #460

@mateusfccp-cos

Description

@mateusfccp-cos

Summary

For an OpenAPI 3 schema that has no required array AND uses allOf for at least one property, every non-default property in the generated Dart factory is marked required, even though the spec says none should be.

@nestjs/swagger emits allOf for properties typed as a nested DTO (e.g. @ApiPropertyOptional({ type: SomeTypedDto })), so this fires routinely on backends that follow that conventional NestJS pattern.

Version

swagger_parser: 1.43.1
OpenAPI version: 3.0.0
Output: Dart + freezed (json_serializer: freezed, use_freezed3: true)

Minimal reproducer

Spec fragment:

components:
  schemas:
    Bar:
      type: object
      properties:
        name: { type: string }
      required: [name]
    Foo:
      type: object
      properties:
        a: { type: string }
        b:
          allOf:
            - $ref: '#/components/schemas/Bar'

Foo has no required array — both a and b should be optional.

Actual generated output

@Freezed()
abstract class Foo with _$Foo {
  const factory Foo({
    required String a,
    required Bar b,
  }) = _Foo;
}

Expected

@Freezed()
abstract class Foo with _$Foo {
  const factory Foo({
    String? a,
    Bar? b,
  }) = _Foo;
}

Root cause

lib/src/parser/parser/open_api_parser.dart, in _findParametersAndImports:

  • Line 749: var hasAllOfKey = false;

  • Lines 752–758: when the schema has no required key, the parser scans properties and sets hasAllOfKey = true if any property has allOf.

  • Line 800: the final required flag is computed as

    isRequired || hasAllOfKey || hasDefaultKey,

    So the moment any property uses allOf, every property in the schema is treated as required.

Why this is wrong

allOf is a schema-composition mechanism, not a requiredness signal. A property whose value is { allOf: [ { $ref: '...' } ] } is the standard way to attach a description / example to a typed reference; it says nothing about whether the property is required on its parent object. That information lives only in the parent's required array, exactly as defined in OAS 3.1 §10.2.1 and OAS 3.0 §4.7.24.

A cross-generator sanity check: the same spec fed to swagger-typescript-api correctly generates a?: string; b?: Bar; for Foo. The TS generator only treats fields as required when they appear in required.

Suggested fix

Drop || hasAllOfKey from the expression on line 800. hasAllOfKey is computed only as an input to that expression and can be removed entirely (lines 749, 752–758).

hasDefaultKey is in a similar spot — a default value affects nullability/initialisation semantics, not whether the property is required at the schema level — but I haven't reviewed that path closely and it's not what this issue is about.

Workaround (for downstream users)

Until this is fixed upstream, a post-processor on the generated .dart files can rewrite required Type field,Type? field, inside the factory constructor of every schema that (a) has no required array and (b) has at least one allOf property.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions