Sign In

@ng-forge/openapi-generator

Package Overview
Dependencies
Maintainers
1
Versions
39
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@ng-forge/openapi-generator

Generate @ng-forge/dynamic-forms configurations from OpenAPI specs

latest
Source
npmnpm
Version
1.1.0
Version published
Maintainers
1
Created
Source

@ng-forge/openapi-generator

Converts OpenAPI 3.x specifications into @ng-forge/dynamic-forms FormConfig objects and TypeScript interfaces.

CI npm version npm downloads License: MIT Discord

Quick Start

1. Write (or already have) an OpenAPI 3.x spec:

# openapi.yaml
openapi: '3.0.3'
info:
  title: My API
  version: '1.0'
paths:
  /users/register:
    post:
      operationId: registerUser
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [email, password]
              properties:
                email:
                  type: string
                  format: email
                password:
                  type: string
                  format: password
                  minLength: 8
                bio:
                  type: string
                  maxLength: 500

2. Run the generator:

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

3. Import and use in your Angular component:

import { DynamicForm } from '@ng-forge/dynamic-forms';
import { registerUserFormConfig } from './generated/forms';
import type { RegisterUserFormValue } from './generated/types';

@Component({
  imports: [DynamicForm],
  template: `<form [dynamic-form]="formConfig" (submitted)="onSubmit($event)"></form>`,
})
export class RegisterComponent {
  formConfig = registerUserFormConfig;

  onSubmit(value: RegisterUserFormValue) {
    this.http.post('/users/register', value).subscribe();
  }
}

The form needs a UI adapter registered once in your app providers, for example provideDynamicForm(...withMaterialFields()). See any @ng-forge/dynamic-forms-* adapter package for setup.

The generator produces a typed FormConfig and a matching TypeScript interface:

Generated register-user.form.ts
import type { FormConfig } from '@ng-forge/dynamic-forms';

export const registerUserFormConfig = {
  fields: [
    {
      key: 'email',
      type: 'input',
      label: 'Email',
      props: { type: 'email' },
      validators: [{ type: 'required' }, { type: 'email' }],
    },
    {
      key: 'password',
      type: 'input',
      label: 'Password',
      props: { type: 'password' },
      validators: [{ type: 'required' }, { type: 'minLength', value: 8 }],
    },
    {
      key: 'bio',
      type: 'textarea',
      label: 'Bio',
      validators: [{ type: 'maxLength', value: 500 }],
    },
  ],
} as const satisfies FormConfig;
Generated register-user.types.ts
export interface RegisterUserFormValue {
  email: string;
  password: string;
  bio?: string;
}

Installation

npm install @ng-forge/openapi-generator

CLI Usage

ng-forge-generator --spec openapi.yaml --output src/generated

Options

OptionDescription
--spec <path>Path to OpenAPI spec file (required)
--output <path>Output directory for generated files (required)
--interactive <mode>full (prompt for endpoints + ambiguous types) or none (auto-select). Default: full
--endpoints <list>Comma-separated endpoints, e.g. "POST:/users,PUT:/users/{id}"
--read-onlyGenerate GET endpoint forms with all fields disabled
--barrel-extension <ext>Extension in barrel re-exports. Accepts "" (default — no extension) or a dot-prefixed value like ".js" (for Node ESM). Pass "" to reset
--watchWatch spec file for changes and regenerate
--config <path>Directory for .ng-forge-generator.json config (defaults to --output)
--dry-runList files that would be generated without writing them
--skip-existingSkip files that already exist on disk
--verboseShow detailed output including field mapping decisions
--quietSuppress info output; still shows success summary, warnings, and errors

Interactive Modes

  • --interactive full (default): Prompts the user to select endpoints (all pre-checked) and resolve ambiguous field types. Automatically falls back to none in non-TTY environments (e.g., CI pipelines).
  • --interactive none: No prompts. Uses --endpoints flag, saved config, or auto-selects all endpoints. GET endpoints with top-level array responses are always skipped.

Examples

# Interactive mode (default) — prompts for endpoint selection and field types
ng-forge-generator --spec openapi.yaml --output src/generated

# Non-interactive for CI — auto-selects POST/PUT/PATCH
ng-forge-generator --spec openapi.yaml --output src/generated --interactive none

# Specific endpoints only
ng-forge-generator --spec openapi.yaml --output src/generated \
  --interactive none --endpoints "POST:/users,PUT:/users/{id}"

# Generate GET endpoints with disabled (read-only) fields
ng-forge-generator --spec openapi.yaml --output src/generated --interactive none --read-only

# Node ESM / moduleResolution nodenext — emit .js extensions in barrel files
ng-forge-generator --spec openapi.yaml --output src/generated --barrel-extension .js

# Preview without writing
ng-forge-generator --spec openapi.yaml --output src/generated --dry-run

# Watch for changes
ng-forge-generator --spec openapi.yaml --output src/generated --watch

# Verbose output for debugging field mapping
ng-forge-generator --spec openapi.yaml --output src/generated --interactive none --verbose

# Quiet mode for CI pipelines
ng-forge-generator --spec openapi.yaml --output src/generated --interactive none --quiet

Field Type Mapping

OpenAPI TypeFormat/ConstraintForm Field TypeAmbiguous?
string--input (text)Yes: textarea
stringemailinput (email)No
stringpasswordinput (password)No
stringuri / urlinput (url)No
stringdate / date-timedatepickerNo
stringtimeinput (time)No
string + enum--selectYes: radio
string + maxLength <= 100--input (text)No
string + maxLength > 200--textareaNo
integer / number--input (number)Yes: slider
boolean--checkboxYes: toggle
array + enum items--multi-checkboxNo
array + object items--array (template)No
array + primitive items--array (single template)No
object--groupNo

Name-based Heuristics

  • Fields ending with description, notes, comment, bio, body, content, summary, message, text are resolved to textarea (overrides ambiguous text input).
  • Fields ending with phone, tel, telephone, mobile, fax, cell are resolved to input type tel.

Validator Mapping

OpenAPI ConstraintForm Validator
requiredrequired
minLengthminLength
maxLengthmaxLength
minimummin
maximummax
patternpattern
format: emailemail
minItems (array)minLength
maxItems (array)maxLength

Custom Extensions

  • x-ng-forge-type: Override the generated field type entirely (e.g., x-ng-forge-type: color-picker).
  • x-enum-labels: Custom human-readable labels for enum values. Supports array format ['Label 1', 'Label 2'] or object format { value1: 'Label 1', value2: 'Label 2' }.

Advanced Schema Handling

allOf (Schema Composition)

The generator merges all schemas in an allOf array into a single flat set of properties. This is commonly used for inheritance-style schemas:

NewPet:
  allOf:
    - $ref: '#/components/schemas/Pet' # base properties
    - type: object
      properties:
        ownerEmail: # additional properties
          type: string
          format: email

The generated form includes all properties from Pet plus ownerEmail, as if they were declared in a single schema.

oneOf + discriminator (Polymorphic Forms)

When a schema uses oneOf with a discriminator, the generator creates a conditional form:

Payment:
  oneOf:
    - $ref: '#/components/schemas/CreditCardPayment'
    - $ref: '#/components/schemas/BankTransferPayment'
  discriminator:
    propertyName: paymentMethod
    mapping:
      credit_card: '#/components/schemas/CreditCardPayment'
      bank_transfer: '#/components/schemas/BankTransferPayment'

This produces:

  • A radio field for the discriminator property (paymentMethod)
  • Conditional field groups for each variant, shown/hidden via logic based on the discriminator value
  • Distinct variant interfaces (e.g., PaymentCreditCard, PaymentBankTransfer) combined as a union type

When a property references a discriminator schema (e.g., method: { $ref: '#/components/schemas/Payment' }), the generator wraps the discriminator fields inside a group field for that property.

GET Endpoint Behavior

  • Default: GET endpoints are included with editable fields, just like POST/PUT/PATCH.
  • With --read-only: GET endpoint fields are generated with disabled: true, and array fields have no add/remove buttons.
  • Top-level array responses are always skipped with a warning.

Watch Mode

The --watch flag starts a file watcher on the spec file. When the spec changes:

  • The generator re-runs automatically using saved config (non-interactive)
  • If new endpoints are detected that weren't in the original selection, a message is logged — re-run without --watch to select them
  • Changes are debounced (500ms) to avoid redundant regenerations during rapid edits
  • Press Ctrl+C to stop watching

Watch mode is useful during API-first development workflows where the spec is being actively edited.

Configuration Persistence

A .ng-forge-generator.json config file is saved in the output directory (or the --config directory). It stores:

  • Selected endpoints — which METHOD:/path pairs to generate
  • Field type decisions — ambiguous field type choices (e.g., "registerUser.acceptTerms": "checkbox")
  • Read-only flag — whether GET endpoint fields are disabled
  • Barrel extension — extension used in generated index.ts re-exports (empty by default)

This enables reproducible non-interactive re-runs. On subsequent runs, the generator reuses saved decisions without prompting.

Output Structure

<output>/
├── forms/
│   ├── <endpoint>.form.ts    # FormConfig objects
│   └── index.ts              # Barrel exports
├── types/
│   ├── <endpoint>.types.ts   # TypeScript interfaces
│   └── index.ts              # Barrel exports
└── .ng-forge-generator.json  # Config

File Naming

  • With operationId: kebab-cased operationId (e.g., createPet -> create-pet.form.ts)
  • Without operationId: method + path (e.g., POST /users/register -> post-users-register.form.ts)

Barrel Exports

By default, generated index.ts barrel files re-export modules without a file extension:

// forms/index.ts
export * from './create-pet.form';
export * from './update-pet.form';

This matches the typical Angular app setup (moduleResolution: bundler). For Node ESM consumers running with moduleResolution: node16 or nodenext, pass --barrel-extension .js to emit fully-qualified ESM imports.

The selected extension persists in .ng-forge-generator.json (only when non-empty) so future runs keep your choice. To clear a previously-saved .js and return to the default, pass --barrel-extension "" or delete the barrelExtension key from the config file.

Migrating from pre-1.x: Earlier versions emitted .js suffixes by default. Upgrading will change generated barrel output for users who had not explicitly configured this — pass --barrel-extension .js on your next run to preserve the old behavior.

Programmatic API

The package exports all core functions for programmatic use:

Parsing

// Parse and dereference an OpenAPI 3.x spec file
parseOpenAPISpec(specPath: string): Promise<OpenAPISpec>

// Extract GET/POST/PUT/PATCH endpoints with their schemas
extractEndpoints(spec: OpenAPISpec): EndpointInfo[]

// Walk a schema, merging allOf and resolving discriminators
walkSchema(schema: SchemaObject, requiredFields?: string[]): WalkedSchema

Mapping

// Map a single schema property to a form field type
mapSchemaToFieldType(schema: SchemaObject): FieldTypeResult

// Map schema constraints to form validators
mapSchemaToValidators(schema: SchemaObject, required: boolean): ValidatorConfig[]

// Map an entire schema to an array of FieldConfig objects
mapSchemaToFields(schema: SchemaObject, requiredFields: string[], options?: MappingOptions): MappingResult

Generation

// Generate a TypeScript FormConfig source string from fields
generateFormConfig(fields: FieldConfig[], options: FormConfigGeneratorOptions): string

// Generate a TypeScript interface source string from a schema
generateInterface(schema: SchemaObject, options: InterfaceGeneratorOptions): string

// Generate an index.ts barrel file. `options.extension` controls the re-export
// suffix (default: '' — no extension; use '.js' for Node ESM/nodenext).
generateBarrel(fileNames: string[], options?: BarrelOptions): string

I/O

// Write generated files to disk (with change detection and skip-existing support)
writeGeneratedFiles(outputDir: string, files: GeneratedFile[], options?: WriteOptions): Promise<WriteResult>

// Load/save the .ng-forge-generator.json config file
loadConfig(dir: string): Promise<GeneratorConfig | null>
saveConfig(dir: string, config: GeneratorConfig): Promise<void>

Troubleshooting

Why did my field become input instead of textarea?

Plain string fields without a format are ambiguous — they could be a short text input or a long textarea. The generator resolves this by:

  • maxLength constraint: <= 100 → input, > 200 → textarea, between 100-200 → ambiguous
  • Name heuristic: fields ending with description, notes, bio, message, etc. → textarea
  • Interactive prompt (if --interactive full): you choose
  • Default (if --interactive none): falls back to input

To force a specific type, use the x-ng-forge-type extension on the property in your spec.

Why are my GET endpoints being skipped?

GET endpoints with top-level array responses are always skipped since they don't map to a single form. For all other GET endpoints, forms are generated by default with editable fields.

To make GET endpoint fields read-only, use --read-only.

Which request body content types are supported?

application/json, multipart/form-data, and application/x-www-form-urlencoded, in that preference order when an endpoint declares more than one.

Binary file properties (type: string, format: binary, including arrays of them) are skipped with a warning since there is no file upload field type yet. An endpoint whose body contains only binary properties produces no form and is skipped entirely.

Why does the generator say "Swagger 2.0" is not supported?

Only OpenAPI 3.x specs are supported. Convert Swagger 2.0 specs using converter.swagger.io or the swagger2openapi npm package.

How do I re-run without being prompted again?

The generator saves your choices to .ng-forge-generator.json. Subsequent runs with --interactive none reuse those decisions automatically. To reset, delete the config file.

Exit Codes

CodeMeaning
0Success
1Error (parse failure, no endpoints found/selected, invalid options)

Keywords

angular

FAQs

Package last updated on 22 Jul 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts