OpenAPI Generator

The @ng-forge/openapi-generator reads your OpenAPI 3.x spec and produces ready-to-use FormConfig objects and TypeScript interfaces. No manual field wiring — your API contract drives your forms.

Key features:

  • Generates as const satisfies FormConfig for full type inference
  • Maps OpenAPI types, formats, and constraints to field types and validators
  • Interactive mode for resolving ambiguous field types (e.g. input vs textarea)
  • Watch mode for regeneration on spec changes
  • Persists decisions in a config file for reproducible builds

Installation

Run directly with npx:

npx @ng-forge/openapi-generator --spec ./openapi.yaml --output ./src/forms

Or install as a dev dependency:

Quick Start

Given a Petstore-like OpenAPI spec:

openapi: 3.0.3
info:
  title: Petstore
  version: 1.0.0
paths:
  /pets:
    post:
      operationId: createPet
      requestBody:
        content:
          application/json:
            schema:
              type: object
              required: [name]
              properties:
                name:
                  type: string
                  minLength: 1
                  maxLength: 100
                age:
                  type: integer
                  minimum: 0
                email:
                  type: string
                  format: email
                status:
                  type: string
                  enum: [available, pending, sold]

Run the generator:

npx @ng-forge/openapi-generator --spec ./petstore.yaml --output ./src/forms

This produces:

src/forms/forms/create-pet.form.ts

// @generated by @ng-forge/openapi-generator
import type { FormConfig } from '@ng-forge/dynamic-forms';

export const createPetFormConfig = {
  fields: [
    {
      key: 'name',
      type: 'input',
      label: 'Name',
      props: { type: 'text' },
      validators: [{ type: 'required' }, { type: 'minLength', value: 1 }, { type: 'maxLength', value: 100 }],
    },
    {
      key: 'age',
      type: 'input',
      label: 'Age',
      props: { type: 'number' },
      validators: [{ type: 'min', value: 0 }],
    },
    {
      key: 'email',
      type: 'input',
      label: 'Email',
      props: { type: 'email' },
      validators: [{ type: 'email' }],
    },
    {
      key: 'status',
      type: 'select',
      label: 'Status',
      options: [
        { label: 'Available', value: 'available' },
        { label: 'Pending', value: 'pending' },
        { label: 'Sold', value: 'sold' },
      ],
    },
  ],
} as const satisfies FormConfig;

src/forms/types/create-pet.types.ts

// @generated by @ng-forge/openapi-generator
export interface CreatePetFormValue {
  name: string;
  age?: number;
  email?: string;
  status?: 'available' | 'pending' | 'sold';
}

Both directories include barrel index.ts files for convenient imports.

CLI Reference

ng-forge-generator [options]
Flag Type Default Description
--spec string (required) Path to OpenAPI 3.x spec file (JSON or YAML)
--output string (required) Output directory for generated files
--interactive 'full' | 'none' 'full' 'full' prompts for ambiguous fields; 'none' uses defaults
--endpoints string Comma-separated endpoints: POST:/pets,GET:/pets/{id}
--read-only boolean false Generate GET endpoint forms with all fields disabled
--watch boolean false Watch spec file and regenerate on changes
--config string Directory containing .ng-forge-generator.json
--dry-run boolean false List files that would be generated without writing them
--skip-existing boolean false Skip files that already exist on disk
--verbose boolean false Show detailed output including field mapping decisions
--quiet boolean false Suppress info output; still shows success summary, warnings, and errors

OpenAPI to Field Type Mapping

The generator maps OpenAPI schema types and formats to @ng-forge/dynamic-forms field types:

OpenAPI Type Format Field Type Props
string email input { type: 'email' }
string uri / url input { type: 'url' }
string date / date-time datepicker
string password input { type: 'password' }
string (none) input { type: 'text' }
string + enum select
integer / number (any) input { type: 'number' }
boolean (any) checkbox
array + enum items multi-checkbox
array + object items array (container)
object (any) group (container)

Nullable Values

OpenAPI nullability is preserved end-to-end:

OpenAPI Spec Schema Example Emitted FieldDef
3.0 { type: 'string', nullable: true } nullable: true
3.1 { type: ['string', 'null'] } nullable: true
Either { nullable: true, default: null } nullable: true, value: null

See Configuration → Nullable values for runtime behavior and the read-side caveat.

Ambiguous Field Types

Some mappings have alternatives. In interactive mode, the CLI prompts you to choose:

Scope Default Alternative
Text input input (single-line) textarea (multi-line)
Single select select (dropdown) radio (radio buttons)
Numeric input (number) slider (range)
Boolean checkbox toggle (switch)

In non-interactive mode (--interactive none), the Default column is used automatically.

Validator Mapping

OpenAPI schema constraints map directly to @ng-forge/dynamic-forms validators:

OpenAPI Property Validator Value
Field in required array required
minLength minLength number
maxLength maxLength number
minimum min number
maximum max number
pattern pattern regex string
format: 'email' email

Schema Support

Schema Structure Support Notes
Plain properties Supported Maps each property to a field
allOf Supported Merges schemas, combines required arrays
oneOf + discriminator Supported Creates conditional groups with radio discriminator
anyOf Skipped Warning logged
if/then/else Skipped Warning logged
additionalProperties Skipped Warning logged
$ref Dereferenced Resolved at parse time via swagger-parser

Config File

The generator persists your choices in .ng-forge-generator.json:

{
  "spec": "./openapi.yaml",
  "output": "./src/forms",
  "endpoints": ["POST:/pets", "GET:/pets/{id}"],
  "decisions": {
    "description": "textarea",
    "age": "slider"
  },
  "readOnly": true
}

On subsequent runs, persisted decisions are reused — so interactive prompts only appear for new or changed fields.

Interactive vs Non-Interactive Mode

Interactive (--interactive full) — the default:

  1. Prompts you to select which endpoints to generate forms for (POST/PUT/PATCH are pre-selected, GET is unchecked)
  2. Prompts for each ambiguous field type choice
  3. Saves decisions to the config file

Non-interactive (--interactive none):

  • Uses all endpoints, or filters by --endpoints flag
  • Uses default field type choices for ambiguous fields
  • No prompts — suitable for CI/CD pipelines

Watch Mode

npx @ng-forge/openapi-generator \
  --spec ./openapi.yaml \
  --output ./src/forms \
  --watch

Watch mode monitors your spec file and regenerates forms on changes:

  • Uses a 500ms debounce to avoid thrashing
  • Runs in non-interactive mode (uses persisted decisions)
  • Press Ctrl+C to stop

Output Structure

Generated files follow this layout:

<output-dir>/
├── forms/
│   ├── create-pet.form.ts
│   ├── list-pets.form.ts
│   └── index.ts
└── types/
    ├── create-pet.types.ts
    ├── list-pets.types.ts
    └── index.ts

File names are derived from operationId if available, otherwise from the HTTP method and path (e.g. POST /pets/{id}/tagspost-pets-by-id-tags).

GET Endpoints

By default, GET endpoints are included with editable fields, just like POST/PUT/PATCH. Use the --read-only flag to generate GET endpoint fields with disabled: true:

npx @ng-forge/openapi-generator \
  --spec ./openapi.yaml \
  --output ./src/forms \
  --read-only

Top-level array responses are always skipped with a warning.

Programmatic API

The package exports its internals for programmatic use:

import {
  // Parsing
  parseOpenAPISpec,
  extractEndpoints,

  // Mapping
  mapSchemaToFieldType,
  mapSchemaToValidators,
  mapSchemaToFields,

  // Generation
  generateFormConfig,
  generateInterface,
  generateBarrel,
  writeGeneratedFiles,

  // Configuration
  loadConfig,
  saveConfig,

  // CLI
  run,
} from '@ng-forge/openapi-generator';

Use these to integrate form generation into custom build pipelines, Nx generators, or CI workflows.