TypeScript code generator from OpenAPI/CRD/JSON Schema/Go structs - rebuilt from ground up with AST-based generation
npm install klasikGenerate 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
โจ 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
``bash
npm install -g klasik
Quick Start
$3
Generate from OpenAPI 3.x spec:
`bash
klasik generate --url https://api.example.com/openapi.json --output ./src/generated
`Generate from Swagger 2.0 spec (auto-converted to OpenAPI 3.0):
`bash
klasik generate --url https://petstore.swagger.io/v2/swagger.json --output ./src/generated
`Generate from Kubernetes CRDs:
`bash
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:
`bash
klasik generate-jsonschema \
--url ./schemas/user.json \
--output ./src/generated
`Generate from Go structs (requires Go installed, auto-setup on first use):
`bash
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:
`bash
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)$3
`typescript
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.$3
`typescript
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());
`$3
Request validation ensures request bodies are:
1. Instances of the expected class
2. Valid according to class-validator decorators
`typescript
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$3
Response validation ensures responses match the expected schema:
`typescript
try {
const response = await api.getTasks();
// Response is validated
} catch (error) {
if (error instanceof ValidationError) {
console.error('Validation failed:', error.validationErrors);
}
}
`$3
Use callbacks for custom error handling:
`typescript
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'));
}
});
`$3
- Models must be generated with
--class-validator flag
- For response validation: enableResponseTransformation must be true (default)
- class-validator package must be installed$3
`bash
Generate models with validation decorators
klasik generate \
--url https://api.example.com/openapi.json \
--output ./src/generated \
--class-validatorUse 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.
$3
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
$3
Enable with the
--use-ajv flag:`bash
klasik generate \
--url https://api.example.com/openapi.json \
--output ./generated \
--use-ajv
`$3
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 performanceExample generated class:
`typescript
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;
}
`$3
Valid data:
`typescript
import { User } from './generated/models';const userData = {
name: 'John Doe',
email: 'john@example.com'
};
const result = User.validateWithJsonSchema(userData);
if (result.valid) {
console.log('โ
Data is valid!');
} else {
console.error('โ Validation failed:', result.errors);
}
`Invalid data:
`typescript
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"'
// }
// ]
// }
`$3
The validator automatically validates nested objects recursively:
`typescript
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: 'john@example.com',
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.$3
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
- multipleOfArray constraints:
-
minItems, maxItems
- uniqueItemsObject constraints:
-
required properties
- additionalPropertiesOther:
-
enum values
- nullable types
- Union types with anyOf$3
The generated code includes compilation caching for optimal performance:
`typescript
// 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
$3
You can use both validation approaches together:
`bash
klasik generate \
--url https://api.example.com/openapi.json \
--output ./generated \
--class-validator \
--use-ajv
`Generated class has both:
`typescript
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.
$3
When using
--use-ajv, the following dependencies are automatically added to package.json:`json
{
"dependencies": {
"ajv": "^8.12.0",
"ajv-formats": "^2.1.1"
}
}
`Install them in your project:
`bash
cd generated
npm install
`$3
`bash
Generate models with Ajv validation
klasik generate-jsonschema \
--url ./schemas/user.json \
--output ./src/models \
--use-ajvUse in your code
cat > example.ts << 'EOF'
import { User } from './src/models';// Valid user
const validUser = {
name: 'Alice Smith',
email: 'alice@example.com',
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
`$3
You can access the generated JSON Schema directly:
`typescript
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.
$3
Advantages:
- โ
TypeScript-first - Excellent type inference with
z.infer
- โ
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 neededWhen 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
$3
Enable with the
--use-zod flag:`bash
klasik generate \
--url https://api.example.com/openapi.json \
--output ./generated \
--use-zod
`$3
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):`typescript
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;
`$3
Validate data:
`typescript
import { UserSchema } from './generated/models/user.zod';const result = UserSchema.safeParse({
id: '123e4567-e89b-12d3-a456-426614174000',
name: 'John Doe',
email: 'john@example.com',
});
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:
`typescript
try {
const user = UserSchema.parse(data);
// user is fully typed
} catch (error) {
if (error instanceof z.ZodError) {
console.error('Validation failed:', error.issues);
}
}
`$3
| 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() |$3
| Format | Zod Method |
|--------|------------|
|
email | .email() |
| url / uri | .url() |
| uuid | .uuid() |
| date-time | .datetime() |
| date | .date() |
| ipv4 | .ip({ version: 'v4' }) |
| ipv6 | .ip({ version: 'v6' }) |$3
| 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 |$3
| Property | Zod Output |
|----------|------------|
| Optional (not required) |
.optional() |
| Nullable | .nullable() |
| Both optional and nullable | .nullish() |$3
Zod schemas import and use referenced types directly:
`typescript
// 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(),
});
`$3
You can use both class-based models and Zod schemas together:
`bash
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.
$3
When using
--use-zod, the following dependency is automatically added to package.json:`json
{
"dependencies": {
"zod": "^3.23.0"
}
}
`Install it in your project:
`bash
cd generated
npm install
`$3
`bash
Generate models with Zod validation
klasik generate-jsonschema \
--url ./schemas/user.json \
--output ./src/models \
--use-zodUse 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: 'alice@example.com',
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
$3
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:
`bash
Works with Swagger 2.0 specs
klasik generate \
--url https://petstore.swagger.io/v2/swagger.json \
--output ./generatedOutput: "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:
`bash
klasik generate [options]
`Options:
| Option | Description | Default |
|--------|-------------|---------|
|
-u, --url | OpenAPI spec URL or file path (required) | - |
| -o, --output | Output directory (required) | - |
| -m, --mode | Generation mode: full or models-only | full |
| --http-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 | Custom header (repeatable) | - |
| --timeout | Request timeout | 30000 |
| --template | Custom template directory | - |
| --keep-spec | Keep downloaded spec file(s) | false |
| --export-style