Skip to content

OpenAPI Generator Specification — Advanced OpenAPI Spec Authoring for Code Generation

DodaTech Updated 2026-06-28 5 min read

In this tutorial, you will learn about OpenAPI Generator Specification. We cover key concepts, practical examples, and best practices to help you master this topic.

OpenAPI Generator specification focuses on writing OpenAPI specs that produce clean, idiomatic code across multiple target languages, covering schema design, polymorphism, discriminators, and spec portability.

What You'll Learn

  • Schema patterns that generate clean code
  • Discriminator-based polymorphism
  • oneOf, anyOf, and allOf composition
  • Nullable and optional field patterns
  • Spec portability across generators

Why It Matters

Not all OpenAPI specs generate good code. Specs written without code generation in mind produce verbose, confusing SDKs. DodaTech's spec review Process ensures every spec follows codegen-optimized patterns, reducing generated SDK size by 40% compared to naive specs.

Real-World Use

A team writes an OpenAPI spec with extensive use of oneOf for a polymorphic threat detection API. Initial generated SDKs were confusing to use. After applying discriminator patterns and proper schema composition, the generated client SDK became intuitive, with proper type hierarchies and IDE autocompletion.

flowchart TD
    A["OpenAPI Spec"] --> B{"Schema uses
discriminator?"} B -->|"Yes"| C["Generated: proper
type inheritance"] B -->|"No"| D["Generated: loose types,
manual casting needed"] A --> E{"Uses allOf for
composition?"} E -->|"Yes"| F["Generated: clean
composed models"] E -->|"No"| G["Generated: duplicate
properties"] A --> H{"Nullable marked
explicitly?"} H -->|"Yes"| I["Generated: Optional[T]
clean null handling"] H -->|"No"| J["Generated: ambiguous
null semantics"]

Code Examples

Example 1: Discriminator-Based Polymorphism

# OpenAPI spec with discriminator for polymorphic threats
components:
  schemas:
    Threat:
      type: object
      required:
        - id
        - type
        - name
        - severity
      properties:
        id:
          type: string
          format: uuid
        type:
          type: string
          enum: [malware, phishing, dos, data_breach]
        name:
          type: string
        severity:
          type: string
          enum: [low, medium, high, critical]
        description:
          type: string
      discriminator:
        propertyName: type
        mapping:
          malware: MalwareThreat
          phishing: PhishingThreat
          dos: DoSThreat
          data_breach: DataBreachThreat

    MalwareThreat:
      allOf:
        - $ref: '#/components/schemas/Threat'
        - type: object
          required:
            - malware_type
          properties:
            malware_type:
              type: string
              enum: [virus, worm, trojan, ransomware]
            signature:
              type: string
            infection_vector:
              type: string

    PhishingThreat:
      allOf:
        - $ref: '#/components/schemas/Threat'
        - type: object
          required:
            - target_url
          properties:
            target_url:
              type: string
              format: uri
            brand_impersonated:
              type: string
            email_subject:
              type: string

Example 2: oneOf for Union Types

components:
  schemas:
    Indicator:
      type: object
      required:
        - type
        - value
      properties:
        type:
          type: string
          enum: [ip, domain, hash, url]
        value:
          type: string
        confidence:
          type: number
          minimum: 0
          maximum: 100
        first_seen:
          type: string
          format: date-time

    IndicatorSearchResult:
      type: object
      properties:
        query:
          type: string
        results:
          type: array
          items:
            $ref: '#/components/schemas/Indicator'
        metadata:
          type: object
          properties:
            total:
              type: integer
            page:
              type: integer

    # oneOf for different response formats
    AnalysisResponse:
      oneOf:
        - $ref: '#/components/schemas/CompletedAnalysis'
        - $ref: '#/components/schemas/PendingAnalysis'
        - $ref: '#/components/schemas/FailedAnalysis'
      discriminator:
        propertyName: status
        mapping:
          completed: CompletedAnalysis
          pending: PendingAnalysis
          failed: FailedAnalysis

    CompletedAnalysis:
      type: object
      required: [status, result, score, summary]
      properties:
        status:
          type: string
          enum: [completed]
        result:
          type: string
        score:
          type: number
        summary:
          type: object
          properties:
            verdict:
              type: string
            severity:
              type: string
            affected_indicators:
              type: array
              items:
                type: string

    PendingAnalysis:
      type: object
      required: [status, estimated_completion]
      properties:
        status:
          type: string
          enum: [pending]
        estimated_completion:
          type: string
          format: date-time
        progress:
          type: integer
          minimum: 0
          maximum: 100

Example 3: Nullable and Optional Patterns

components:
  schemas:
    ThreatReport:
      type: object
      required:
        - id
        - title
        - created_at
        - severity
      properties:
        id:
          type: string
          format: uuid
          readOnly: true
        title:
          type: string
          maxLength: 500
        description:
          type: string
          nullable: true
          description: "Mark nullable to generate Optional[str] in Python"
        severity:
          type: string
          enum: [low, medium, high, critical]
        created_at:
          type: string
          format: date-time
          readOnly: true
        updated_at:
          type: string
          format: date-time
          nullable: true
          readOnly: true
        assigned_to:
          type: string
          nullable: true
          description: "null if unassigned"
        tags:
          type: array
          items:
            type: string
            maxLength: 50
          description: "Use default: [], not nullable, for empty list"
        related_threats:
          type: array
          items:
            $ref: '#/components/schemas/ThreatReference'
          nullable: true
        metadata:
          type: object
          additionalProperties:
            type: string
          nullable: true
        remediation_steps:
          type: array
          items:
            $ref: '#/components/schemas/RemediationStep'
          description: "Will be empty array if no steps, use Optional[List]"

Common Mistakes

1. Not Using Discriminators for Polymorphism

Without discriminators, generated code uses loose typing and manual Type Checking. Discriminators produce proper class hierarchies.

2. Overusing oneOf Without Discriminator

oneOf without a discriminator produces confusing generated code. Always provide a discriminator property.

3. Missing nullable for Optional Fields

Without nullable: true, clients must use sentinel values or undefined for absent optional fields.

4. Deeply Nested allOf

Deep composition (allOf > allOf > allOf) produces unreadable generated models. Keep composition at most 2 levels deep.

5. Not Marking Read-Only Fields

Fields set by the server (created_at, id) should have readOnly: true. Generators use this to exclude them from request bodies.

Practice Questions

  1. Why use discriminators in polymorphic schemas?
  2. What is the difference between oneOf, anyOf, and allOf?
  3. How does nullable affect generated code?
  4. Why keep allOf composition shallow?
  5. What does readOnly: true do in code generation?

Answers:

  1. Discriminators enable proper type hierarchies in generated code instead of loose unions requiring manual casting.
  2. oneOf = exactly one match; anyOf = one or more; allOf = all must match (composition/inheritance).
  3. nullable: true generates Optional[T] in typed languages and proper null handling in all languages.
  4. Deep allOf chains are hard to read and some generators flatten them incorrectly, producing duplicate properties.
  5. readOnly: true fields are excluded from request body generation but included in response models.

Challenge: Write an OpenAPI spec for a polymorphic alert system with discriminator-based types (CriticalAlert, WarningAlert, InfoAlert). Generate Python and TypeScript SDKs and verify proper type inheritance.

FAQ

What is the best way to handle enums?

: Define enums in the schema's enum array. Generators produce native enum types in supporting languages.

How do I handle dates vs date-times?

: Use format: date for dates and format: date-time for timestamps. Generators produce appropriate types (LocalDate vs Instant).

Can I use $ref everywhere?

: Yes, but avoid $ref at the property level when you need additional constraints. Wrap in an allOf.

What is schema inlining?

: Some generators inline referenced schemas, increasing code size. Use --additional-properties=avoidInlineModel=true.

How do I generate proper error types?

: Define a reusable Error schema and reference it in all 4xx/5xx responses. Include a code, message, and optional details field.

What's Next

Review {{< ilink "OpenAPI" "OpenAPI Generator Options" }} for spec-level generation configuration, and explore {{< ilink "OpenAPI" "OpenAPI Diff" }} for spec version management.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro