avro-safe
v0.2.0
Published
A powerful and flexible Avro schema validation package
Readme
avro-safe
avro-safe is a robust and flexible Avro schema validation package designed to handle complex validation cases with ease. Whether you're dealing with required fields, optional fields, or fields with default values, avro-safe ensures your payloads conform to the Avro schema while providing clear and concise error reporting.
Features
- Default Values: Fields with default values are automatically handled. If a field is missing from the payload, the default is used. If present, the field’s type is validated.
- Null & Undefined Handling: Supports
nullandundefinedvalues gracefully, following the schema’s requirements. - Required Fields: Ensures all required fields are present and correctly typed.
- Comprehensive Error Reporting: Clear and actionable error messages for missing or mismatched fields.
- Nested Fields Validation: Supports recursive validation for nested records and arrays.
- TypeScript Model Generator: Generates TypeScript interfaces/types from an Avro schema — via CLI or programmatic API — similar to Avro's Java codegen.
Installation
To install avro-safe, use npm:
npm install avro-safeUsage (TypeScript)
Here's how to use avro-safe to validate a payload against an Avro schema:
import { validateAvroPayload } from 'avro-safe';
const schema = {
type: 'record',
name: 'PetOwner',
fields: [
{ name: 'name', type: 'string' },
{ name: 'age', type: ['null', 'int'], default: null }, // Optional field with default value
{ name: 'country', type: 'string', default: 'Unknown' }, // Field with default value
{
name: 'pets',
type: {
type: 'array',
items: {
type: 'record',
name: 'Pet',
fields: [
{ name: 'kind', type: { type: 'enum', name: 'PetKind', symbols: ['CAT', 'DOG'] } },
{ name: 'name', type: 'string' }
]
}
}
}
]
};
const payload = {
name: 'John Doe',
pets: [
{ kind: 'DOG', name: 'Buddy' },
{ kind: 'CAT', name: 'Whiskers' }
]
};
const errors: string[] = validateAvroPayload(schema, payload);
if (errors.length > 0) {
console.log('Validation failed:', errors);
} else {
console.log('Validation successful!');
}Validation Logic
- Required Fields: If a required field is missing, an error is generated.
- Default Values: Fields with default values are automatically validated or ignored if absent.
- Optional Fields: If a field is
nullorundefinedand the schema allows it, validation passes. - Nested Fields: Handles validation for nested records and arrays.
- Clear Error Messages: Any type mismatches or missing fields will produce detailed error messages.
Examples
Valid Payload
const validPayload = {
name: 'Jane Doe',
pets: [
{ kind: 'CAT', name: 'Mittens' },
{ kind: 'DOG', name: 'Rover' }
]
};
const validErrors: string[] = validateAvroPayload(schema, validPayload);
console.log(validErrors); // []Invalid Payload
const invalidPayload = {
name: 'Jane Doe',
country: null, // Invalid, should be string
pets: [
{ name: 'Buddy' } // Missing 'kind'
]
};
const invalidErrors: string[] = validateAvroPayload(schema, invalidPayload);
console.log(invalidErrors); // ["Field 'country' is null, but null is not allowed", "Missing required field: 'kind'"]TypeScript Model Generator
avro-safe can also generate TypeScript interfaces/types from an Avro schema, similar to Avro's Java codegen (avro-tools compile schema).
CLI
npx avro-safe-codegen <schema.avsc | schema-dir> [...more] -o <output-dir>This emits one .ts file per named record/enum/fixed type in the schema, laid out under namespace-derived directories the same way Java packages become directories — e.g. namespace com.example.pet becomes com/example/pet/<Name>.ts. Cross-file references become relative import type statements, and self-/mutually-referential records (common in Avro) work without runtime circular-import issues since import type is erased at compile time.
npx avro-safe-codegen schemas/pet-owner.avsc -o src/generatedProgrammatic API
import { generateTypeScript, generateFlatTypeScript } from 'avro-safe';
// One file per named type, namespace-aware (what the CLI uses under the hood)
const files = generateTypeScript(schema); // => [{ filePath, content }, ...]
// A single string with every named type, dependencies first.
// Throws if two different namespaces reuse the same type name.
const source = generateFlatTypeScript(schema);Given the PetOwner schema from the validation example above, generateFlatTypeScript produces:
export type PetKind = 'CAT' | 'DOG';
export interface Pet {
kind: PetKind;
name: string;
}
export interface PetOwner {
name: string;
age?: number;
country: string;
pets: Pet[];
}Contribute
Feel free to open issues and submit pull requests for feature enhancements, bug fixes, or improvements!
Changes:
- Added Nested Fields: Included in the features section.
- Updated Usage Examples: Provided examples for valid and invalid payloads.
- Enhanced Validation Logic: Detailed how nested fields are validated and how different field types are handled.
