Skip to main content

JsonSchemaScannerAsync

An advanced asynchronous JSON Schema traversal engine with comprehensive async support for remote reference resolution and async visitor operations. Extends the synchronous JsonSchemaScanner with full async/await support, enabling complex scenarios like fetching remote schemas, async validation, database lookups during traversal, and async schema transformations. Maintains all the power of the sync version while supporting async workflows.

Signature

class JsonSchemaScannerAsync

Examples

Async schema traversal with remote reference resolution

import { JsonSchemaScannerAsync } from '@winglet/json-schema';

const schemaWithRemoteRefs = {
type: 'object',
properties: {
user: { $ref: 'https://api.example.com/schemas/user.json' },
profile: { $ref: 'https://api.example.com/schemas/profile.json' }
}
};

const asyncScanner = new JsonSchemaScannerAsync({
visitor: {
enter: async (entry) => {
console.log(`Processing: ${entry.path}`);
// Async operations like logging to database
await logToDatabase('schema_traversal', entry.path);
},
exit: async (entry) => {
if (entry.referenceResolved) {
await logToDatabase('reference_resolved', entry.referencePath!);
}
}
},
options: {
resolveReference: async (refPath) => {
// Fetch remote schemas
const response = await fetch(refPath);
return await response.json();
},
maxDepth: 5
}
});

const resolvedSchema = await asyncScanner.scan(schemaWithRemoteRefs).then(scanner =>
scanner.getValue()
);

Async schema validation with database lookups

import { isStringSchema, isObjectSchema } from '@winglet/json-schema/filter';

interface ValidationContext {
validatedPaths: string[];
errors: string[];
}

const validationScanner = new JsonSchemaScannerAsync<ValidationContext>({
options: {
context: { validatedPaths: [], errors: [] },
filter: async (entry, context) => {
// Async filtering based on database rules
const shouldProcess = await checkProcessingRules(entry.path);
return shouldProcess;
},
mutate: async (entry, context) => {
// Async mutation with external API calls
// Use isStringSchema() to handle both { type: 'string' } and { type: ['string', 'null'] }
if (isStringSchema(entry.schema) && entry.schema.format === 'email') {
const domainValidation = await validateEmailDomain(entry.schema.pattern);
if (!domainValidation.valid) {
context.errors.push(`Invalid email pattern at ${entry.path}`);
}
return {
...entry.schema,
description: `Validated email field: ${domainValidation.message}`
};
}
}
},
visitor: {
enter: async (entry, context) => {
context.validatedPaths.push(entry.path);

// Async validation against external service
// Use isObjectSchema() to handle both { type: 'object' } and { type: ['object', 'null'] }
if (isObjectSchema(entry.schema)) {
const validationResult = await validateSchemaStructure(entry.schema);
if (!validationResult.valid) {
context.errors.push(`Schema structure invalid at ${entry.path}`);
}
}
}
}
});

await validationScanner.scan(complexSchema);
const context = validationScanner.options.context;
console.log(`Validated ${context.validatedPaths.length} paths with ${context.errors.length} errors`);

Distributed schema composition with async references

const distributedSchema = {
type: 'object',
properties: {
userService: { $ref: 'service://user-service/schema' },
orderService: { $ref: 'service://order-service/schema' },
paymentService: { $ref: 'service://payment-service/schema' }
}
};

const serviceResolver = new JsonSchemaScannerAsync({
options: {
resolveReference: async (serviceRef) => {
const [, , serviceName] = serviceRef.split('/');

// Async service discovery and schema fetching
const serviceEndpoint = await discoverService(serviceName);
const schemaResponse = await fetch(`${serviceEndpoint}/api/schema`);
const schema = await schemaResponse.json();

// Cache for performance
await cacheSchema(serviceRef, schema);

return schema;
},
context: { resolvedServices: new Set<string>() }
},
visitor: {
enter: async (entry, context) => {
if (entry.referenceResolved && entry.referencePath?.startsWith('service://')) {
context.resolvedServices.add(entry.referencePath);
await notifyServiceUsage(entry.referencePath);
}
}
}
});

const composedSchema = await serviceResolver.scan(distributedSchema)
.then(scanner => scanner.getValue());

Progressive schema loading with batched async operations

const batchingScanner = new JsonSchemaScannerAsync({
options: {
resolveReference: async (refPath, context) => {
// Batch reference resolution for performance
if (!context.pendingRefs) context.pendingRefs = [];
context.pendingRefs.push(refPath);

if (context.pendingRefs.length >= 10) {
const resolvedBatch = await resolveBatchedReferences(context.pendingRefs);
context.pendingRefs = [];
return resolvedBatch[refPath];
}

// Handle single reference
return await resolveSingleReference(refPath);
}
},
visitor: {
exit: async (entry, context) => {
// Flush remaining batched operations at the end
if (entry.depth === 0 && context.pendingRefs?.length > 0) {
await resolveBatchedReferences(context.pendingRefs);
context.pendingRefs = [];
}
}
}
});

Playground

import { JsonSchemaScannerAsync } from '@winglet/json-schema';

const schemaWithRemoteRefs = {
type: 'object',
properties: {
  user: { $ref: 'https://api.example.com/schemas/user.json' },
  profile: { $ref: 'https://api.example.com/schemas/profile.json' }
}
};

const asyncScanner = new JsonSchemaScannerAsync({
visitor: {
  enter: async (entry) => {
    console.log(`Processing: ${entry.path}`);
    // Async operations like logging to database
    await logToDatabase('schema_traversal', entry.path);
  },
  exit: async (entry) => {
    if (entry.referenceResolved) {
      await logToDatabase('reference_resolved', entry.referencePath!);
    }
  }
},
options: {
  resolveReference: async (refPath) => {
    // Fetch remote schemas
    const response = await fetch(refPath);
    return await response.json();
  },
  maxDepth: 5
}
});

const resolvedSchema = await asyncScanner.scan(schemaWithRemoteRefs).then(scanner =>
scanner.getValue()
);

Notes

Async Capabilities:

  • Async Reference Resolution: Fetch schemas from remote sources
  • Async Visitors: Perform async operations during traversal
  • Async Filtering: Dynamic filtering based on async conditions
  • Async Mutation: Transform schemas using async data sources

Use Cases:

  • Remote Schema Composition: Combine schemas from multiple services
  • Dynamic Schema Validation: Validate against external rules
  • Schema Analytics: Collect metrics with async logging
  • Progressive Loading: Load schema parts on demand
  • Database Integration: Enrich schemas with database data

Performance Notes:

  • Maintains non-blocking traversal with proper async/await handling
  • Supports batched operations for efficiency
  • Provides same caching benefits as sync version
  • Handles concurrent async operations safely

This async scanner is essential for modern microservice architectures, distributed schema systems, and complex validation workflows.