npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

klasik

v2.5.2

Published

TypeScript code generator from OpenAPI/CRD/JSON Schema/Go structs - rebuilt from ground up with AST-based generation

Readme

Klasik

Generate TypeScript clients from OpenAPI specifications, Kubernetes CRDs, JSON Schema, and Go structs with full type safety and class-transformer support.

Perfect for:

  • 🚀 Kubernetes operators and controllers
  • 🔧 REST API clients with type safety
  • 📦 NestJS backend services
  • 🎯 Type-safe microservice communication
  • 🔄 Go-to-TypeScript code sharing

Features

Easy to Use - Single CLI command to generate types 🎯 Type-Safe - Full TypeScript support with strict typing 🔄 class-transformer - Automatic serialization/deserialization ✅ class-validator - Built-in validation decorators 📋 Ajv JSON Schema - Draft 2020-12 validation with deep nesting support 🛡️ Zod Schemas - Generate Zod validation schemas for runtime type safety 🎨 NestJS Ready - @ApiProperty decorators out of the box 📦 Multiple Formats - OpenAPI 3.x, Swagger 2.0 (auto-converted), Kubernetes CRDs, JSON Schema, Go structs 🌐 ESM Support - Modern JavaScript modules with .js extensions 🔗 External $refs - Automatic resolution of external schemas 🎭 Custom Templates - Mustache-based customization ⚙️ Flexible Output - Multiple export styles (namespace, direct, both) 🌐 HTTP Client Choice - Generate with Axios (default) or native Fetch API 🧪 Well Tested - Comprehensive test coverage (1073+ passing tests) 🚀 Production Ready - Used in real-world projects 📝 Full CLI - Rich command-line interface with 4 commands 🔐 Authentication - Custom headers including Bearer tokens

Installation

npm install -g klasik

# or use directly with npx
npx klasik <command>

Quick Start

CLI Usage

Generate from OpenAPI 3.x spec:

klasik generate --url https://api.example.com/openapi.json --output ./src/generated

Generate from Swagger 2.0 spec (auto-converted to OpenAPI 3.0):

klasik generate --url https://petstore.swagger.io/v2/swagger.json --output ./src/generated

Generate from Kubernetes CRDs:

klasik generate-crd \
  --url https://raw.githubusercontent.com/argoproj/argo-cd/master/manifests/crds/application-crd.yaml \
  --output ./src/generated \
  --class-validator \
  --nestjs-swagger

Generate from JSON Schema:

klasik generate-jsonschema \
  --url ./schemas/user.json \
  --output ./src/generated

Generate from Go structs (requires Go installed, auto-setup on first use):

klasik generate-go \
  --type "helm.sh/helm/v3/pkg/chart.Metadata" \
  --type "helm.sh/helm/v3/pkg/chart.Chart" \
  --output ./src/generated \
  --class-validator \
  --nestjs-swagger

Bare Mode

Generate models directly in the output directory without wrapper structure:

klasik generate-crd \
  --url ./my-crd.yaml \
  --output ./src/models \
  --bare

Output structure with --bare:

src/models/
├── index.ts       # Simple exports
├── application.ts
└── app-project.ts

Default structure (without --bare):

src/models/
├── models/
│   ├── index.ts
│   ├── application.ts
│   └── app-project.ts
├── package.json
└── tsconfig.json

Note: The --bare flag:

  • Only works with models-only mode (generate-crd, generate-jsonschema, or generate with --mode models-only)
  • Skips package.json and tsconfig.json generation
  • Ignores --export-style flag (uses simple direct exports)

Programmatic Usage

import { Generator, OpenAPIParser, SpecLoader } from 'klasik';

// Load and parse OpenAPI spec
const loader = new SpecLoader();
const spec = await loader.load({ url: 'https://api.example.com/openapi.json' });

const parser = new OpenAPIParser();
const ir = parser.parse(spec, { includeOperations: true });

// Generate TypeScript code
const generator = new Generator({
  outputDir: './generated',
  mode: 'full', // or 'models-only'
  nestJsSwagger: true,
  classValidator: true,
});

await generator.generate(ir);

Request and Response Validation

Klasik supports automatic validation of both API requests and responses using class-validator. When enabled, requests and responses are validated against the decorators generated by the --class-validator plugin.

Configuration

import { Configuration } from './generated/configuration';
import { TasksApi, NewTask } from './generated';
import axios from 'axios';

const config = new Configuration({
  basePath: 'https://api.example.com',
  enableResponseTransformation: true,     // Required for response validation
  enableResponseValidation: true,         // Enable response validation (default: false)
  enableRequestValidation: true,          // Enable request validation (default: false)
});

const api = new TasksApi(config, axios.create());

Request Validation

Request validation ensures request bodies are:

  1. Instances of the expected class
  2. Valid according to class-validator decorators
import { NewTask } from './generated/models';

// Create instance
const newTask = new NewTask();
newTask.title = 'My Task';
newTask.description = 'Task description';

// This will validate before sending
const response = await api.createTask(newTask);

Request validation will throw if:

  • Request body is not an instance: RequestNotInstanceError
  • Request body fails validation: ValidationError

Response Validation

Response validation ensures responses match the expected schema:

try {
  const response = await api.getTasks();
  // Response is validated
} catch (error) {
  if (error instanceof ValidationError) {
    console.error('Validation failed:', error.validationErrors);
  }
}

Custom Error Handling

Use callbacks for custom error handling:

const config = new Configuration({
  basePath: 'https://api.example.com',
  enableRequestValidation: true,
  onRequestValidationError: (errors, modelClass, instance) => {
    console.error(`Request validation failed for ${modelClass.name}:`);
    errors.forEach(err => {
      console.error(`  - ${err.property}: ${Object.values(err.constraints || {}).join(', ')}`);
    });
  },
  enableResponseValidation: true,
  onResponseValidationError: (errors, modelClass, instance) => {
    console.error(`Response validation failed for ${modelClass.name}:`);
    // Log to monitoring service
    Sentry.captureException(new Error('API validation failed'));
  }
});

Requirements

  • Models must be generated with --class-validator flag
  • For response validation: enableResponseTransformation must be true (default)
  • class-validator package must be installed

Example with CLI

# Generate models with validation decorators
klasik generate \
  --url https://api.example.com/openapi.json \
  --output ./src/generated \
  --class-validator

# Use in your code
import { Configuration, TasksApi, NewTask } from './generated';

const config = new Configuration({
  basePath: 'https://api.example.com',
  enableRequestValidation: true,
  enableResponseValidation: true
});

const api = new TasksApi(config);

// Request will be validated
const newTask = new NewTask();
newTask.title = 'My Task';
const created = await api.createTask(newTask);

// Response will be validated
const tasks = await api.listTasks();

For more details, see the Validation Guide.

Ajv JSON Schema Validation

Klasik can generate Ajv-based JSON Schema validation alongside or instead of class-validator decorators. This provides comprehensive JSON Schema Draft 2020-12 validation with deep nested object support and optimized performance.

Why Ajv Validation?

Advantages over class-validator:

  • Full JSON Schema compliance - Supports all Draft 2020-12 features
  • Deep nested validation - Automatically validates nested objects at all levels
  • Performance optimized - Schema compilation is cached per class
  • Standards-based - Uses industry-standard JSON Schema format
  • Independent - Works alongside or without class-validator

When to use:

  • Complex nested object structures (User → Address → Coordinates)
  • JSON Schema-first development workflows
  • Need for format validation (email, uuid, date-time, etc.)
  • Projects requiring JSON Schema compliance
  • High-performance validation scenarios

Basic Usage

Enable with the --use-ajv flag:

klasik generate \
  --url https://api.example.com/openapi.json \
  --output ./generated \
  --use-ajv

Generated Code Structure

Each generated class includes:

  1. static getSchema() - Returns the JSON Schema
  2. static validateWithJsonSchema(data) - Validates data against the schema
  3. Private cached validator - Optimized for performance

Example generated class:

import { Ajv } from "ajv";
import { addFormats } from "ajv-formats";
import { Expose } from "class-transformer";

export class User {
  /**
   * Get JSON Schema for User
   * @returns JSON Schema Draft 2020-12
   */
  static getSchema(): object {
    return {
      "$schema": "https://json-schema.org/draft/2020-12/schema",
      "type": "object",
      "properties": {
        "name": {
          "type": "string",
          "minLength": 1
        },
        "email": {
          "type": "string",
          "format": "email"
        },
        "address": {
          "type": "object"
        }
      },
      "additionalProperties": false,
      "required": ["name", "email"]
    };
  }

  private static _ajvInstance: Ajv | null = null;
  private static _compiledValidator: any = null;

  /** Get or create Ajv instance for User */
  private static getAjvInstance(): Ajv {
    if (!this._ajvInstance) {
      this._ajvInstance = new Ajv({ allErrors: true, strict: false });
      addFormats(this._ajvInstance);
    }
    return this._ajvInstance;
  }

  /** Get or create compiled validator for User (cached for performance) */
  private static getCompiledValidator(): any {
    if (!this._compiledValidator) {
      const ajv = this.getAjvInstance();
      const schema = this.getSchema();
      this._compiledValidator = ajv.compile(schema);
    }
    return this._compiledValidator;
  }

  /**
   * Validate data against JSON Schema with recursive nested validation
   * @param data - Data to validate
   * @returns Validation result with errors if any
   */
  static validateWithJsonSchema(data: unknown): { valid: boolean; errors: any[] } {
    const validate = this.getCompiledValidator();
    const valid = validate(data);

    // Collect errors
    const allErrors: any[] = validate.errors || [];

    // Recursively validate nested objects that have validateWithJsonSchema method
    if (valid && typeof data === "object" && data !== null) {
      for (const [key, value] of Object.entries(data)) {
        if (value && typeof value === "object") {
          const constructor = (value as any).constructor;
          if (constructor && typeof constructor.validateWithJsonSchema === "function") {
            const nestedResult = constructor.validateWithJsonSchema(value);
            if (!nestedResult.valid) {
              allErrors.push(...nestedResult.errors.map((e: any) => ({
                ...e,
                instancePath: `/${key}${e.instancePath || ""}`
              })));
            }
          }
        }
      }
    }

    return { valid: allErrors.length === 0, errors: allErrors };
  }

  @Expose()
  name: string;

  @Expose()
  email: string;

  @Expose()
  address?: Address;
}

Using Validation Methods

Valid data:

import { User } from './generated/models';

const userData = {
  name: 'John Doe',
  email: '[email protected]'
};

const result = User.validateWithJsonSchema(userData);

if (result.valid) {
  console.log('✅ Data is valid!');
} else {
  console.error('❌ Validation failed:', result.errors);
}

Invalid data:

const invalidData = {
  name: '',  // minLength: 1 violation
  email: 'not-an-email'  // format: email violation
};

const result = User.validateWithJsonSchema(invalidData);

console.log(result);
// {
//   valid: false,
//   errors: [
//     {
//       instancePath: '/name',
//       schemaPath: '#/properties/name/minLength',
//       keyword: 'minLength',
//       params: { limit: 1 },
//       message: 'must NOT have fewer than 1 characters'
//     },
//     {
//       instancePath: '/email',
//       schemaPath: '#/properties/email/format',
//       keyword: 'format',
//       params: { format: 'email' },
//       message: 'must match format "email"'
//     }
//   ]
// }

Deep Nested Validation

The validator automatically validates nested objects recursively:

import { User, Address } from './generated/models';

// Create nested structure
const address = new Address();
address.street = '';  // Invalid: minLength violation
address.city = 'Springfield';
address.zipCode = 'INVALID';  // Invalid: pattern violation

const user = {
  name: 'John Doe',
  email: '[email protected]',
  address  // Nested object
};

const result = User.validateWithJsonSchema(user);

console.log(result);
// {
//   valid: false,
//   errors: [
//     {
//       instancePath: '/address/street',
//       keyword: 'minLength',
//       message: 'must NOT have fewer than 1 characters'
//     },
//     {
//       instancePath: '/address/zipCode',
//       keyword: 'pattern',
//       message: 'must match pattern "^[0-9]{5}$"'
//     }
//   ]
// }

Note: Error paths include the full nested path (/address/street), making it easy to identify exactly where validation failed.

Supported Validations

All JSON Schema Draft 2020-12 validation keywords are supported:

String constraints:

  • minLength, maxLength
  • pattern (regex)
  • format (email, uuid, date-time, uri, etc.)

Numeric constraints:

  • minimum, maximum
  • exclusiveMinimum, exclusiveMaximum
  • multipleOf

Array constraints:

  • minItems, maxItems
  • uniqueItems

Object constraints:

  • required properties
  • additionalProperties

Other:

  • enum values
  • nullable types
  • Union types with anyOf

Performance Optimization

The generated code includes compilation caching for optimal performance:

// First validation: Schema is compiled and cached
User.validateWithJsonSchema(data1);  // Compile + Validate

// Subsequent validations: Uses cached compiled validator
User.validateWithJsonSchema(data2);  // Validate only (fast!)
User.validateWithJsonSchema(data3);  // Validate only (fast!)

Benefits:

  • Schema compiled once per class, not per validation
  • Significant performance improvement for repeated validations
  • Singleton Ajv instance shared across validations

Combining with class-validator

You can use both validation approaches together:

klasik generate \
  --url https://api.example.com/openapi.json \
  --output ./generated \
  --class-validator \
  --use-ajv

Generated class has both:

export class User {
  // class-validator decorators
  @IsString()
  @MinLength(1)
  @Expose()
  name: string;

  @IsEmail()
  @Expose()
  email: string;

  // PLUS Ajv validation methods
  static getSchema(): object { /* ... */ }
  static validateWithJsonSchema(data: unknown) { /* ... */ }
}

Use case: Runtime validation with class-validator in NestJS controllers, plus JSON Schema validation for external integrations.

Dependencies

When using --use-ajv, the following dependencies are automatically added to package.json:

{
  "dependencies": {
    "ajv": "^8.12.0",
    "ajv-formats": "^2.1.1"
  }
}

Install them in your project:

cd generated
npm install

Complete Example

# Generate models with Ajv validation
klasik generate-jsonschema \
  --url ./schemas/user.json \
  --output ./src/models \
  --use-ajv

# Use in your code
cat > example.ts << 'EOF'
import { User } from './src/models';

// Valid user
const validUser = {
  name: 'Alice Smith',
  email: '[email protected]',
  age: 30
};

const result1 = User.validateWithJsonSchema(validUser);
console.log('Valid:', result1.valid);  // true

// Invalid user
const invalidUser = {
  name: '',  // Too short
  email: 'invalid-email',
  age: -5  // Negative age
};

const result2 = User.validateWithJsonSchema(invalidUser);
console.log('Valid:', result2.valid);  // false
console.log('Errors:', result2.errors);
// [
//   { instancePath: '/name', message: 'must NOT have fewer than 1 characters' },
//   { instancePath: '/email', message: 'must match format "email"' },
//   { instancePath: '/age', message: 'must be >= 0' }
// ]
EOF

npx ts-node example.ts

Advanced: Accessing the JSON Schema

You can access the generated JSON Schema directly:

import { User } from './generated/models';

// Get the schema
const schema = User.getSchema();

console.log(JSON.stringify(schema, null, 2));
// {
//   "$schema": "https://json-schema.org/draft/2020-12/schema",
//   "type": "object",
//   "properties": {
//     "name": { "type": "string", "minLength": 1 },
//     "email": { "type": "string", "format": "email" }
//   },
//   "required": ["name", "email"],
//   "additionalProperties": false
// }

// Use with external JSON Schema validators
import Ajv from 'ajv';
const ajv = new Ajv();
const validate = ajv.compile(User.getSchema());
validate(data);

Use cases:

  • Generating OpenAPI documentation
  • Sharing schemas with other systems
  • Custom validation workflows
  • Schema introspection

Zod Schema Generation

Klasik can generate Zod validation schemas alongside your TypeScript models. Zod provides powerful runtime type validation with excellent TypeScript integration and a modern API.

Why Zod Validation?

Advantages:

  • TypeScript-first - Excellent type inference with z.infer<typeof Schema>
  • Modern API - Fluent, chainable validation methods
  • Lightweight - Small bundle size (~12kb gzipped)
  • Composable - Easy to extend and combine schemas
  • Parse, don't validate - Returns typed, validated data
  • No decorators - Works with plain objects, no class instances needed

When to use:

  • Frontend applications with React, Vue, or Svelte
  • API request/response validation
  • Form validation
  • Configuration file validation
  • Projects preferring functional over decorator-based approach

Basic Usage

Enable with the --use-zod flag:

klasik generate \
  --url https://api.example.com/openapi.json \
  --output ./generated \
  --use-zod

Generated Code Structure

For each model, Klasik generates a separate .zod.ts file:

models/
├── user.ts           # Class with decorators
├── user.zod.ts       # Zod schema
├── address.ts
├── address.zod.ts
├── index.ts          # Class exports
└── index.zod.ts      # Zod schema exports

Example generated Zod file (user.zod.ts):

import { z } from 'zod';

/**
 * User schema
 */
export const UserSchema = z.object({
  id: z.string().uuid(),
  name: z.string().min(1).max(100),
  email: z.string().email(),
  age: z.number().int().min(0).max(150).optional(),
  role: z.enum(['admin', 'user', 'guest']).optional(),
});

export type User = z.infer<typeof UserSchema>;

Using Zod Schemas

Validate data:

import { UserSchema } from './generated/models/user.zod';

const result = UserSchema.safeParse({
  id: '123e4567-e89b-12d3-a456-426614174000',
  name: 'John Doe',
  email: '[email protected]',
});

if (result.success) {
  console.log('Valid user:', result.data);
  // result.data is fully typed as User
} else {
  console.error('Validation errors:', result.error.issues);
}

Parse with error throwing:

try {
  const user = UserSchema.parse(data);
  // user is fully typed
} catch (error) {
  if (error instanceof z.ZodError) {
    console.error('Validation failed:', error.issues);
  }
}

Type Mapping

| OpenAPI/IR Type | Zod Output | |-----------------|------------| | string | z.string() | | number | z.number() | | integer | z.number().int() | | boolean | z.boolean() | | array | z.array(elementSchema) | | object | z.object({...}) | | enum | z.enum([...values]) | | union | z.union([...]) | | dictionary | z.record(z.string(), valueSchema) | | unknown | z.unknown() |

Format Validations

| Format | Zod Method | |--------|------------| | email | .email() | | url / uri | .url() | | uuid | .uuid() | | date-time | .datetime() | | date | .date() | | ipv4 | .ip({ version: 'v4' }) | | ipv6 | .ip({ version: 'v6' }) |

Constraint Validations

| Constraint | Zod Method | |------------|------------| | minLength | .min(length) | | maxLength | .max(length) | | pattern | .regex(pattern) | | minimum | .min(value) or .gte(value) | | maximum | .max(value) or .lte(value) | | exclusiveMinimum | .gt(value) | | exclusiveMaximum | .lt(value) | | minItems | .min(length) on array | | maxItems | .max(length) on array |

Optional and Nullable Handling

| Property | Zod Output | |----------|------------| | Optional (not required) | .optional() | | Nullable | .nullable() | | Both optional and nullable | .nullish() |

Reference Handling

Zod schemas import and use referenced types directly:

// address.zod.ts
import { z } from 'zod';

export const AddressSchema = z.object({
  street: z.string(),
  city: z.string(),
  zipCode: z.string(),
});

// user.zod.ts
import { z } from 'zod';
import { AddressSchema } from './address.zod';

export const UserSchema = z.object({
  name: z.string(),
  homeAddress: AddressSchema,
  workAddress: AddressSchema.optional(),
});

Combining with Class-based Models

You can use both class-based models and Zod schemas together:

klasik generate \
  --url https://api.example.com/openapi.json \
  --output ./generated \
  --class-validator \
  --use-zod

Use case: Use class-based models with decorators for NestJS backend validation, and Zod schemas for frontend form validation.

Dependencies

When using --use-zod, the following dependency is automatically added to package.json:

{
  "dependencies": {
    "zod": "^3.23.0"
  }
}

Install it in your project:

cd generated
npm install

Complete Example

# Generate models with Zod validation
klasik generate-jsonschema \
  --url ./schemas/user.json \
  --output ./src/models \
  --use-zod

# Use in your code
cat > example.ts << 'EOF'
import { UserSchema, User } from './src/models/user.zod';

// Valid user
const validResult = UserSchema.safeParse({
  name: 'Alice Smith',
  email: '[email protected]',
  age: 30
});

if (validResult.success) {
  const user: User = validResult.data;
  console.log('Valid user:', user.name);
}

// Invalid user
const invalidResult = UserSchema.safeParse({
  name: '',
  email: 'invalid-email',
  age: -5
});

if (!invalidResult.success) {
  console.log('Errors:', invalidResult.error.issues);
  // [
  //   { path: ['name'], message: 'String must contain at least 1 character(s)' },
  //   { path: ['email'], message: 'Invalid email' },
  //   { path: ['age'], message: 'Number must be greater than or equal to 0' }
  // ]
}
EOF

npx ts-node example.ts

CLI Commands

klasik generate

Generate TypeScript client from OpenAPI specification.

Supported formats:

  • OpenAPI 3.x (native support)
  • Swagger 2.0 (automatically converted to OpenAPI 3.0)

Swagger 2.0 Auto-Conversion:

Klasik automatically detects Swagger 2.0 specifications and converts them to OpenAPI 3.0 before generation. This is transparent - you don't need to do anything special:

# Works with Swagger 2.0 specs
klasik generate \
  --url https://petstore.swagger.io/v2/swagger.json \
  --output ./generated

# Output: "Converting Swagger 2.0 to OpenAPI 3.0..."

The conversion handles:

  • Path and operation migration
  • Parameter format changes
  • Security scheme updates
  • Response object structure
  • Schema definitions to components

Usage:

klasik generate [options]

Options:

| Option | Description | Default | |--------|-------------|---------| | -u, --url <url> | OpenAPI spec URL or file path (required) | - | | -o, --output <dir> | Output directory (required) | - | | -m, --mode <mode> | Generation mode: full or models-only | full | | --http-client <client> | HTTP client: axios or fetch | axios | | --resolve-refs | Resolve external $ref files | false | | --esm | Add .js extensions for ESM compatibility | false | | --skip-js-extensions | Skip .js extensions (for bundlers) | false | | --nestjs-swagger | Add @ApiProperty decorators | false | | --class-validator | Add class-validator decorators | false | | --use-ajv | Add Ajv JSON Schema validation methods | false | | --use-zod | Generate Zod validation schemas | false | | --header <header> | Custom header (repeatable) | - | | --timeout <ms> | Request timeout | 30000 | | --template <dir> | Custom template directory | - | | --keep-spec | Keep downloaded spec file(s) | false | | --export-style <style> | Export style: namespace, direct, both, none | namespace | | --clean | Remove output directory before generation | false |

Examples:

# Basic generation
klasik generate --url ./openapi.yaml --output ./generated

# Full client with API methods (default mode)
klasik generate \
  --url https://petstore.swagger.io/v2/swagger.json \
  --output ./src/api

# Models only (no API client)
klasik generate \
  --url https://api.example.com/openapi.json \
  --output ./src/models \
  --mode models-only

# With NestJS and validation support
klasik generate \
  --url https://api.example.com/spec.json \
  --output ./src/api \
  --nestjs-swagger \
  --class-validator

# With Ajv JSON Schema validation
klasik generate \
  --url https://api.example.com/spec.json \
  --output ./src/api \
  --use-ajv

# With both class-validator and Ajv
klasik generate \
  --url https://api.example.com/spec.json \
  --output ./src/api \
  --class-validator \
  --use-ajv

# With external refs and authentication
klasik generate \
  --url https://api.example.com/spec.json \
  --output ./generated \
  --resolve-refs \
  --header "Authorization: Bearer ${TOKEN}"

# ESM compatibility
klasik generate \
  --url ./openapi.yaml \
  --output ./generated \
  --esm

# Custom templates
klasik generate \
  --url ./openapi.yaml \
  --output ./generated \
  --template ./my-templates

# With native fetch instead of Axios
klasik generate \
  --url ./openapi.yaml \
  --output ./generated \
  --http-client fetch

klasik download

Download OpenAPI spec without generating code.

Usage:

klasik download [options]

Options:

| Option | Description | Default | |--------|-------------|---------| | -u, --url <url> | Remote spec URL (required) | - | | -o, --output <file> | Output file path (required) | - | | --header <header> | Custom header (repeatable) | - | | --resolve-refs | Resolve external $ref files | false | | --timeout <ms> | Request timeout | 30000 |

Examples:

# Download spec
klasik download \
  --url https://api.example.com/openapi.json \
  --output ./specs/api-spec.json

# With authentication
klasik download \
  --url https://api.example.com/openapi.json \
  --output ./specs/api-spec.json \
  --header "Authorization: Bearer ${TOKEN}"

# Download with all external references
klasik download \
  --url https://api.example.com/openapi.json \
  --output ./specs/api-spec.json \
  --resolve-refs

klasik generate-crd

Generate TypeScript models from Kubernetes CustomResourceDefinitions.

Usage:

klasik generate-crd [options]

Options:

| Option | Description | Default | |--------|-------------|---------| | -u, --url <url> | CRD URL or file path (repeatable) | - | | -o, --output <dir> | Output directory (required) | - | | -i, --include <schemas> | Include only specified schemas and dependencies | - | | --include-status | Generate status schemas | false | | --crd-kind-case <format> | Folder naming: pascal, snake, kebab | pascal | | --nestjs-swagger | Add @ApiProperty decorators | false | | --class-validator | Add class-validator decorators | false | | --use-ajv | Add Ajv JSON Schema validation methods | false | | --use-zod | Generate Zod validation schemas | false | | --esm | Add .js extensions for ESM | false | | --header <header> | Custom header (repeatable) | - | | --resolve-refs | Resolve external $ref files | false | | --template <dir> | Custom template directory | - | | --keep-spec | Keep downloaded spec file(s) | false | | --timeout <ms> | Request timeout | 30000 | | --clean | Remove output directory before generation | false |

Examples:

# Single CRD
klasik generate-crd \
  --url ./my-crd.yaml \
  --output ./src/types

# Multiple CRDs (automatic deduplication)
klasik generate-crd \
  --url https://raw.githubusercontent.com/argoproj/argo-cd/master/manifests/crds/application-crd.yaml \
  --url https://raw.githubusercontent.com/argoproj/argo-cd/master/manifests/crds/appproject-crd.yaml \
  --output ./src/generated \
  --crd-kind-case kebab \
  --nestjs-swagger \
  --class-validator

# With status schemas
klasik generate-crd \
  --url ./operator-crd.yaml \
  --output ./src/types \
  --include-status

# With authentication
klasik generate-crd \
  --url https://private-repo.com/crd.yaml \
  --output ./src/types \
  --header "Authorization: Bearer ${TOKEN}"

# Filter to specific schemas (Gateway + dependencies only)
klasik generate-crd \
  --url https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.2.0/standard-install.yaml \
  --output ./src/types \
  --include Gateway \
  --bare

# Filter to multiple schemas
klasik generate-crd \
  --url https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.2.0/standard-install.yaml \
  --output ./src/types \
  --include Gateway,HTTPRoute \
  --bare

klasik generate-jsonschema

Generate TypeScript models from JSON Schema files.

Usage:

klasik generate-jsonschema [options]

Options:

| Option | Description | Default | |--------|-------------|---------| | -u, --url <url> | JSON Schema URL or file path (repeatable) | - | | -o, --output <dir> | Output directory (required) | - | | --nestjs-swagger | Add @ApiProperty decorators | false | | --class-validator | Add class-validator decorators | false | | --use-ajv | Add Ajv JSON Schema validation methods | false | | --use-zod | Generate Zod validation schemas | false | | --esm | Add .js extensions for ESM | false | | --header <header> | Custom header (repeatable) | - | | --resolve-refs | Resolve external $ref files | false | | --template <dir> | Custom template directory | - | | --keep-spec | Keep intermediate OpenAPI specs | false | | --timeout <ms> | Request timeout | 30000 | | --clean | Remove output directory before generation | false |

Examples:

# From SchemaStore
klasik generate-jsonschema \
  --url https://json.schemastore.org/package.json \
  --output ./src/types

# Multiple schemas
klasik generate-jsonschema \
  --url https://json.schemastore.org/kustomization.json \
  --url https://json.schemastore.org/package.json \
  --output ./src/generated

# From local file
klasik generate-jsonschema \
  --url ./schemas/user.json \
  --output ./src/types \
  --nestjs-swagger \
  --class-validator

# With Ajv JSON Schema validation
klasik generate-jsonschema \
  --url ./schemas/user.json \
  --output ./src/types \
  --use-ajv

klasik generate-go

Generate TypeScript models from Go structs using runtime reflection.

Usage:

klasik generate-go [options]

Options:

| Option | Description | Default | |--------|-------------|---------| | -t, --type <type> | Go type path (package.Type) (repeatable, required) | - | | -o, --output <dir> | Output directory (required) | - | | --nestjs-swagger | Add @ApiProperty decorators | false | | --class-validator | Add class-validator decorators | false | | --use-ajv | Add Ajv JSON Schema validation methods | false | | --use-zod | Generate Zod validation schemas | false | | --esm | Add .js extensions for ESM | false | | --template <dir> | Custom template directory | - | | --export-style <style> | Export style: namespace, direct, both, none | namespace | | --bare | Generate models directly in output dir | false | | --go-tool-path <path> | Path to go-schema-gen binary | auto-detected | | --allow-additional-properties | Allow additional properties in JSON Schema | false | | --clean | Remove output directory before generation | false |

Prerequisites:

  • Go 1.21+ must be installed (first-time setup is automatic)
  • Types must be registered in tools/go-schema-gen/registry.go

Examples:

# Generate from Helm chart types
klasik generate-go \
  --type "helm.sh/helm/v3/pkg/chart.Metadata" \
  --type "helm.sh/helm/v3/pkg/chart.Chart" \
  --output ./src/helm-types \
  --nestjs-swagger \
  --class-validator

# Generate with Ajv validation
klasik generate-go \
  --type "helm.sh/helm/v3/pkg/chart.Metadata" \
  --output ./src/types \
  --use-ajv

# Generate for ESM
klasik generate-go \
  --type "helm.sh/helm/v3/pkg/chart.Chart" \
  --output ./src/types \
  --esm

How it works:

  1. Uses Go reflection via invopop/jsonschema library
  2. Generates JSON Schema from Go structs
  3. Feeds to existing JsonSchemaParser
  4. Generates TypeScript with full type safety

Adding new types:

Edit tools/go-schema-gen/registry.go:

import mypackage "github.com/org/package"

var typeRegistry = map[string]interface{}{
    "github.com/org/package.MyStruct": mypackage.MyStruct{},
}

Then rebuild: cd tools/go-schema-gen && ./build.sh

Kubernetes CRD Support

Klasik can generate TypeScript models directly from Kubernetes CustomResourceDefinitions (CRDs). Perfect for working with custom Kubernetes resources like ArgoCD Applications, Cert-Manager Certificates, or your own custom resources.

Features

  • Automatic schema extraction from openAPIV3Schema
  • Multiple URL support - Generate from multiple CRDs in one command
  • Automatic deduplication - Shared types (ObjectMeta, TypeMeta) generated once
  • Multi-document YAML - Process multiple CRDs from a single file
  • Nested type extraction - Complex objects become separate interfaces
  • Full decorator support - NestJS, class-validator, class-transformer
  • Status schemas - Optional with --include-status
  • Version support - Handles multiple CRD versions (v1alpha1, v1, etc.)

Schema Filtering

When working with large CRD collections (like Gateway API), you can filter to generate only specific schemas and their dependencies:

# Generate only Gateway and its dependencies (15 schemas instead of 99)
klasik generate-crd \
  --url https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.2.0/standard-install.yaml \
  --output ./src/types \
  --include Gateway \
  --bare

Multiple schemas:

# Comma-separated
klasik generate-crd -u crds.yaml -o ./out --include Gateway,HTTPRoute

# Repeatable flag
klasik generate-crd -u crds.yaml -o ./out -i Gateway -i HTTPRoute

How it works:

  1. Parses all CRDs and builds the full IR (intermediate representation)
  2. Finds specified schemas and traverses their dependencies (arrays, unions, dictionaries)
  3. Generates only the filtered set of schemas
  4. Handles circular references safely

Benefits:

  • Smaller output (only what you need)
  • Faster compilation
  • Cleaner imports

Automatic Deduplication

When generating from multiple CRDs, Klasik automatically deduplicates shared types by name:

klasik generate-crd \
  --url application-crd.yaml \    # Defines ObjectMeta
  --url appproject-crd.yaml \     # Also defines ObjectMeta
  --output ./generated

Result: Only ONE object-meta.ts file is generated. The last CRD's definition is used (controllable via URL order).

Generated Structure

src/generated/
└── models/
    ├── application.ts              # Main Application CRD
    ├── application-spec.ts         # Nested spec type
    ├── application-status.ts       # Status (with --include-status)
    ├── app-project.ts              # Main AppProject CRD
    ├── object-meta.ts              # SHARED (deduplicated)
    ├── type-meta.ts                # SHARED (deduplicated)
    ├── package.json
    ├── tsconfig.json
    └── index.ts                    # Barrel exports

Using Generated CRD Models

import { Application } from './generated/models';

const app = new Application();
app.apiVersion = 'argoproj.io/v1alpha1';
app.kind = 'Application';
app.metadata = {
  name: 'my-app',
  namespace: 'default'
};
app.spec = {
  project: 'default',
  source: {
    repoURL: 'https://github.com/example/repo',
    path: '.',
    targetRevision: 'HEAD'
  },
  destination: {
    server: 'https://kubernetes.default.svc',
    namespace: 'default'
  }
};

// Full TypeScript intellisense and type checking!

JSON Schema Support

Klasik can generate TypeScript models from JSON Schema files (like those from SchemaStore.org). Perfect for configuration files, data formats, and schema definitions.

Features

  • All definitions generated - Creates models for every definition
  • Multiple URL support - Merge models from multiple schemas
  • Standards compliant - Supports JSON Schema Draft 4 and Draft 7
  • Nested type extraction - Complex objects become separate interfaces
  • Full decorator support - NestJS, class-validator, class-transformer
  • Automatic naming - Schema title or filename becomes class name

Generated Structure

output/
├── models/
│   ├── package.ts
│   ├── person.ts
│   ├── repository.ts
│   └── index.ts
├── package.json
└── tsconfig.json

Using Generated JSON Schema Models

import { Package } from './generated/models';

const pkg: Package = {
  name: 'my-package',
  version: '1.0.0',
  description: 'My awesome package',
  author: {
    name: 'John Doe',
    email: '[email protected]'
  }
};

// Full TypeScript intellisense and type checking!

Go Struct Support

Klasik can generate TypeScript models from Go structs using runtime reflection. Perfect for sharing types between Go backends and TypeScript frontends, or for generating types from existing Go packages like Helm, Kubernetes, or your own custom packages.

Features

  • Runtime reflection - Uses Go's reflection API with invopop/jsonschema
  • Struct tag support - Respects json, yaml, jsonschema tags
  • Multiple types - Generate from multiple structs in one command
  • Automatic references - Nested types resolved with $ref
  • Full decorator support - NestJS, class-validator, class-transformer, Ajv
  • Production-ready - Tested with Helm chart types

Prerequisites

  • Go 1.21+ must be installed (download from go.dev)
  • Types must be registered in tools/go-schema-gen/registry.go

Note: The first time you use generate-go, Klasik will automatically:

  1. Check if Go is installed
  2. Download Go dependencies (go mod tidy)
  3. Build the Go schema generator tool

This only happens once - subsequent runs use the cached binary.

How It Works

  1. Go tool uses reflection to inspect struct at runtime
  2. Generates JSON Schema Draft 2020-12
  3. Feeds to existing JsonSchemaParser
  4. Generates TypeScript with full type safety

Architecture:

Go Struct → Reflection → JSON Schema → JsonSchemaParser → IR → TypeScript

Quick Start

First-time users: Just run the command! Klasik will automatically set up everything:

klasik generate-go \
  --type "helm.sh/helm/v3/pkg/chart.Metadata" \
  --output ./src/helm-types

# Output:
# Building Go schema generator (first time only)...
# Installing Go dependencies...
# Compiling Go tool...
# ✓ Go tool built successfully
# ✔ Generation complete!

Subsequent runs are instant (no rebuild needed):

klasik generate-go \
  --type "helm.sh/helm/v3/pkg/chart.Chart" \
  --output ./src/helm-types \
  --nestjs-swagger \
  --class-validator

Struct Tag Mapping

Go struct tags are automatically converted to JSON Schema:

| Go Tag | Effect | JSON Schema | |--------|--------|-------------| | json:"fieldName" | Property name | "properties": {"fieldName": {...}} | | json:",omitempty" | Optional field | Not in "required" array | | jsonschema:"required" | Force required | Added to "required" array | | jsonschema:"minLength=5" | Validation | "minLength": 5 | | jsonschema:"pattern=^[A-Z]" | Regex | "pattern": "^[A-Z]" | | jsonschema_description:"..." | Documentation | "description": "..." |

Example Go Struct:

type Metadata struct {
    Name        string `json:"name" jsonschema:"required,minLength=1"`
    Version     string `json:"version" jsonschema:"required"`
    Description string `json:"description,omitempty"`
}

Generated TypeScript:

export class Metadata {
  @ApiProperty()
  @IsString()
  name: string;

  @ApiProperty()
  @IsString()
  version: string;

  @ApiProperty({ required: false })
  @IsOptional()
  @IsString()
  description?: string;
}

Adding New Types

To add new Go packages/types:

  1. Edit tools/go-schema-gen/registry.go:
import (
    chart "helm.sh/helm/v3/pkg/chart"
    mypackage "github.com/org/package"
)

var typeRegistry = map[string]interface{}{
    // Existing types
    "helm.sh/helm/v3/pkg/chart.Metadata": chart.Metadata{},

    // Add your types
    "github.com/org/package.MyStruct": mypackage.MyStruct{},
}
  1. Update dependencies:
cd tools/go-schema-gen
go mod tidy
  1. Rebuild the tool:
./build.sh
  1. Use your new type:
klasik generate-go \
  --type "github.com/org/package.MyStruct" \
  --output ./src/types

Limitations

  • Type Registry Required - Types must be pre-registered (no dynamic loading)
  • Requires Recompilation - Adding new packages requires rebuilding the Go tool
  • Go Installation - Go must be installed on the system

Troubleshooting

"Go is not installed" error:

# Install Go from https://go.dev/dl/
# On macOS with Homebrew:
brew install go

# Verify installation:
go version

"Failed to build Go tool" error:

# Try manual build:
cd tools/go-schema-gen
./build.sh

# Or rebuild from scratch:
rm -rf ../../dist/bin/go-schema-gen
klasik generate-go --type "..." --output ...

Rebuild the Go tool manually:

cd tools/go-schema-gen
go mod tidy
./build.sh

Future Enhancements

Planned features for future releases:

  • Go Plugin support for dynamic loading
  • Auto-discovery of types in packages
  • validate tag support
  • Custom type mappings
  • Multi-platform pre-built binaries

NestJS Integration

Klasik integrates seamlessly with NestJS, generating models with @ApiProperty decorators for automatic Swagger/OpenAPI documentation and class-validator decorators for runtime validation.

Setup

Generate models with NestJS support:

klasik generate \
  --url https://api.example.com/openapi.json \
  --output ./src/generated \
  --mode models-only \
  --nestjs-swagger \
  --class-validator

Using in NestJS Controllers

import { Controller, Post, Body } from '@nestjs/common';
import { ApiTags, ApiOperation } from '@nestjs/swagger';
import { CreateUserDto } from './generated/models';

@ApiTags('users')
@Controller('users')
export class UsersController {
  @Post()
  @ApiOperation({ summary: 'Create user' })
  async create(@Body() createUserDto: CreateUserDto) {
    // Automatic validation via class-validator
    // Automatic Swagger docs via @ApiProperty
    return this.usersService.create(createUserDto);
  }
}

Generated Model Example

import { ApiProperty } from '@nestjs/swagger';
import { IsString, IsEmail, MinLength, IsOptional } from 'class-validator';
import { Expose } from 'class-transformer';

export class CreateUserDto {
  @Expose()
  @ApiProperty({
    type: String,
    description: 'User email address',
    required: true,
    format: 'email'
  })
  @IsEmail()
  email: string;

  @Expose()
  @ApiProperty({
    type: String,
    description: 'User password',
    required: true,
    minLength: 8
  })
  @IsString()
  @MinLength(8)
  password: string;

  @Expose()
  @ApiProperty({
    type: String,
    description: 'User display name',
    required: false
  })
  @IsString()
  @IsOptional()
  name?: string;
}

Advanced Topics

External Reference Resolution

When your OpenAPI spec uses external $ref, Klasik can automatically download and inline them:

klasik generate \
  --url ./api-spec.yaml \
  --output ./generated \
  --resolve-refs

What it does:

  1. Discovers all external $ref recursively
  2. Downloads referenced files (with auth if needed)
  3. Inlines them into the main spec
  4. Generates complete TypeScript types

Supported formats:

  • ✅ Relative paths: ./schemas/User.yaml
  • ✅ Absolute paths: /path/to/schema.yaml
  • ✅ Remote URLs: https://api.example.com/schemas/User.yaml
  • ✅ With fragments: ./User.yaml#/definitions/User
  • ✅ Nested refs (ref within ref)
  • ✅ Circular reference detection

Example spec with external refs:

# main-spec.yaml
components:
  schemas:
    User:
      $ref: './schemas/user.yaml'  # Local file
    Post:
      $ref: 'https://api.example.com/schemas/post.yaml'  # Remote URL
    Comment:
      $ref: './schemas/comment.yaml#/Comment'  # With fragment

With authentication:

klasik generate \
  --url https://private-api.com/spec.yaml \
  --resolve-refs \
  --header "Authorization: Bearer ${TOKEN}"

Authentication Headers

Access private API specs with custom headers:

# Bearer token
klasik generate \
  --url https://internal-api.company.com/spec.json \
  --header "Authorization: Bearer ${API_TOKEN}" \
  --output ./generated

# API key
klasik generate \
  --url https://api.example.com/spec.json \
  --header "X-API-Key: ${API_KEY}" \
  --output ./generated

# Multiple headers
klasik generate \
  --url https://api.example.com/spec.json \
  --header "Authorization: Bearer ${TOKEN}" \
  --header "X-Custom-Header: value" \
  --output ./generated

ESM Support

For Node.js ESM projects, use the --esm flag to add .js extensions to imports:

klasik generate \
  --url ./openapi.yaml \
  --output ./generated \
  --esm

Generated imports:

// With --esm
import { User } from './user.js';
import { Post } from './post.js';

// Without --esm
import { User } from './user';
import { Post } from './post';

For bundlers (webpack, vite, esbuild):

Use --skip-js-extensions to omit extensions:

klasik generate \
  --url ./openapi.yaml \
  --output ./generated \
  --esm \
  --skip-js-extensions

HTTP Client Options

Klasik supports two HTTP clients for generated API classes: Axios (default) and native Fetch API.

Axios (Default)

The default HTTP client uses Axios, which provides:

  • Automatic request/response transformation
  • Request cancellation
  • Interceptors for request/response manipulation
  • Wide browser and Node.js compatibility
klasik generate \
  --url ./openapi.yaml \
  --output ./generated
  # --http-client axios (default)

Generated code uses:

import { AxiosInstance, AxiosResponse } from 'axios';

export class UsersApi {
  constructor(configuration: Configuration, axios: AxiosInstance) {
    this.configuration = configuration;
    this.axios = axios;
  }

  async getUser(id: string): Promise<AxiosResponse<User>> {
    // Uses this.axios.request()
  }
}

Native Fetch

For projects that prefer native APIs or want to minimize dependencies, use the --http-client fetch option:

klasik generate \
  --url ./openapi.yaml \
  --output ./generated \
  --http-client fetch

Benefits:

  • No external HTTP dependencies (no Axios)
  • Smaller bundle size
  • Native browser API
  • Works in modern Node.js (18+), Deno, Bun, and edge runtimes
  • Built-in timeout support using AbortController

Generated code uses:

import { HttpResponse, RequestConfig, httpRequest } from './base';

export class UsersApi {
  constructor(configuration: Configuration) {
    this.configuration = configuration;
  }

  async getUser(id: string): Promise<HttpResponse<User>> {
    // Uses httpRequest() wrapper around native fetch
  }
}

HttpResponse interface:

interface HttpResponse<T> {
  data: T;
  status: number;
  statusText: string;
  headers: Record<string, string>;
}

Configuration options for fetch:

const config = new Configuration({
  basePath: 'https://api.example.com',
  headers: { 'Authorization': 'Bearer token' },
  timeout: 30000,           // Request timeout in ms
  credentials: 'include',   // Fetch credentials mode
  mode: 'cors',             // Fetch request mode
});

const api = new UsersApi(config);

Response handling:

  • JSON responses (application/json, *+json) are automatically parsed
  • Text responses (text/*) are returned as strings
  • Binary responses are returned as ArrayBuffer
  • Empty responses (204 No Content) are handled gracefully

Custom Templates

Klasik uses Mustache templates for code generation. You can provide custom templates:

klasik generate \
  --url ./openapi.yaml \
  --output ./generated \
  --template ./my-templates

Template structure:

my-templates/
├── model.mustache           # Model class template
├── api-class.mustache       # API class template (full mode)
├── configuration.mustache   # Configuration class template
└── index.mustache          # Barrel export template

Best Practices

1. Use Version Pinning

For production projects, pin to specific spec versions:

# ✅ Good: Specific commit hash
klasik generate-crd \
  --url https://raw.githubusercontent.com/argoproj/argo-cd/v2.8.0/manifests/crds/application-crd.yaml \
  --output ./src/types

# ❌ Bad: Using master/main branch
klasik generate-crd \
  --url https://raw.githubusercontent.com/argoproj/argo-cd/master/manifests/crds/application-crd.yaml \
  --output ./src/types

2. Keep Generated Code Separate

Store generated code in a dedicated directory:

src/
├── api/
│   └── generated/       # Generated code here
│       ├── models/
│       ├── apis/
│       └── index.ts
├── services/            # Your business logic
└── controllers/         # Your controllers

Add to .gitignore:

src/api/generated/

Regenerate in CI/CD:

# .github/workflows/ci.yml
- name: Generate API Client
  run: |
    npx klasik generate \
      --url https://api.example.com/openapi.json \
      --output ./src/api/generated \
      --nestjs-swagger \
      --class-validator

3. Validate After Generation

Always compile generated code to catch issues early:

klasik generate --url ./spec.yaml --output ./generated
cd ./generated
npm install
npm run build

4. Use Consistent Options

Create a script or Makefile for consistent generation:

// package.json
{
  "scripts": {
    "generate": "klasik generate --url https://api.example.com/openapi.json --output ./src/generated --nestjs-swagger --class-validator",
    "generate:crds": "klasik generate-crd --url https://example.com/crd.yaml --output ./src/k8s --include-status"
  }
}

Development

# Clone the repository
git clone https://github.com/yourusername/klasik-2.git
cd klasik-2

# Install dependencies
npm install

# Build
npm run build

# Run tests
npm test

# Run CLI locally
node dist/cli/index.js generate --url ./test-spec.yaml --output ./test-output

For more details, see CONTRIBUTING.md and ARCHITECTURE.md.

License

MIT

Credits

Built with:


Note: Klasik is a complete rebuild with AST-based code generation, replacing the string manipulation approach from v1. See ARCHITECTURE.md for technical details.