klasik
v2.5.2
Published
TypeScript code generator from OpenAPI/CRD/JSON Schema/Go structs - rebuilt from ground up with AST-based generation
Maintainers
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/generatedGenerate from Swagger 2.0 spec (auto-converted to OpenAPI 3.0):
klasik generate --url https://petstore.swagger.io/v2/swagger.json --output ./src/generatedGenerate 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-swaggerGenerate from JSON Schema:
klasik generate-jsonschema \
--url ./schemas/user.json \
--output ./src/generatedGenerate 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-swaggerBare Mode
Generate models directly in the output directory without wrapper structure:
klasik generate-crd \
--url ./my-crd.yaml \
--output ./src/models \
--bareOutput structure with --bare:
src/models/
├── index.ts # Simple exports
├── application.ts
└── app-project.tsDefault structure (without --bare):
src/models/
├── models/
│ ├── index.ts
│ ├── application.ts
│ └── app-project.ts
├── package.json
└── tsconfig.jsonNote: 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:
- Instances of the expected class
- 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-validatorflag - For response validation:
enableResponseTransformationmust betrue(default) class-validatorpackage 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-ajvGenerated Code Structure
Each generated class includes:
static getSchema()- Returns the JSON Schemastatic validateWithJsonSchema(data)- Validates data against the schema- 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,maxLengthpattern(regex)format(email, uuid, date-time, uri, etc.)
Numeric constraints:
minimum,maximumexclusiveMinimum,exclusiveMaximummultipleOf
Array constraints:
minItems,maxItemsuniqueItems
Object constraints:
requiredpropertiesadditionalProperties
Other:
enumvaluesnullabletypes- 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-ajvGenerated 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 installComplete 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.tsAdvanced: 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-zodGenerated 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 exportsExample 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-zodUse 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 installComplete 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.tsCLI 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 fetchklasik 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-refsklasik 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 \
--bareklasik 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-ajvklasik 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 \
--esmHow it works:
- Uses Go reflection via
invopop/jsonschemalibrary - Generates JSON Schema from Go structs
- Feeds to existing JsonSchemaParser
- 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 \
--bareMultiple 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 HTTPRouteHow it works:
- Parses all CRDs and builds the full IR (intermediate representation)
- Finds specified schemas and traverses their dependencies (arrays, unions, dictionaries)
- Generates only the filtered set of schemas
- 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 ./generatedResult: 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 exportsUsing 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.jsonUsing 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,jsonschematags - ✅ 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:
- Check if Go is installed
- Download Go dependencies (
go mod tidy) - Build the Go schema generator tool
This only happens once - subsequent runs use the cached binary.
How It Works
- Go tool uses reflection to inspect struct at runtime
- Generates JSON Schema Draft 2020-12
- Feeds to existing JsonSchemaParser
- Generates TypeScript with full type safety
Architecture:
Go Struct → Reflection → JSON Schema → JsonSchemaParser → IR → TypeScriptQuick 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-validatorStruct 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:
- 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{},
}- Update dependencies:
cd tools/go-schema-gen
go mod tidy- Rebuild the tool:
./build.sh- Use your new type:
klasik generate-go \
--type "github.com/org/package.MyStruct" \
--output ./src/typesLimitations
- 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.shFuture Enhancements
Planned features for future releases:
- Go Plugin support for dynamic loading
- Auto-discovery of types in packages
validatetag 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-validatorUsing 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-refsWhat it does:
- Discovers all external
$refrecursively - Downloads referenced files (with auth if needed)
- Inlines them into the main spec
- 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 fragmentWith 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 ./generatedESM Support
For Node.js ESM projects, use the --esm flag to add .js extensions to imports:
klasik generate \
--url ./openapi.yaml \
--output ./generated \
--esmGenerated 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-extensionsHTTP 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 fetchBenefits:
- 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-templatesTemplate 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 templateBest 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/types2. 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 controllersAdd 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-validator3. Validate After Generation
Always compile generated code to catch issues early:
klasik generate --url ./spec.yaml --output ./generated
cd ./generated
npm install
npm run build4. 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-outputFor more details, see CONTRIBUTING.md and ARCHITECTURE.md.
License
MIT
Credits
Built with:
- ts-morph - TypeScript Compiler API wrapper
- class-transformer - Object transformation
- class-validator - Runtime validation
- ajv - JSON Schema validator
- ajv-formats - Format validation for Ajv
- zod - TypeScript-first schema validation
- swagger2openapi - Swagger 2.0 to OpenAPI 3.0 conversion
- mustache - Template engine
- commander - CLI framework
Note: Klasik is a complete rebuild with AST-based code generation, replacing the string manipulation approach from v1. See ARCHITECTURE.md for technical details.
