DEV Community

Nehonix Inc
Nehonix Inc

Posted on

Fortify Schema: Interface-Style TypeScript Validation with Conditional Logic

Fortify Schema: Interface-Style TypeScript Validation with Conditional Logic

A TypeScript validation library designed around familiar interface syntax and advanced conditional validation capabilities.


When building TypeScript applications that handle external data, validation becomes essential. Whether you're processing API requests, user forms, or configuration files, you need a way to ensure data integrity while maintaining type safety.

Fortify Schema addresses this need by providing a validation library that uses interface-like syntax familiar to TypeScript developers, combined with advanced conditional validation capabilities for complex business logic.

Core Design Principles

Interface-Native Syntax: Schema definitions use a string-based syntax that mirrors TypeScript interface declarations, making validation rules intuitive for TypeScript developers.

Conditional Validation: Built-in support for runtime conditional logic, allowing fields to be required, optional, or have different validation rules based on other data properties.

Type Integration: Complete TypeScript inference ensures validated data maintains proper typing without additional type assertions.

Basic Schema Definition

Here's how you define a schema in Fortify Schema:

import { Interface } from "fortify-schema";

const UserSchema = Interface({
  id: "uuid",
  email: "email",
  name: "string(2,50)",
  age: "number(18,120)?",
  role: "admin|user|guest",
  tags: "string[]?",
  createdAt: "date"
});

// Validation with full type inference
const result = UserSchema.safeParse(userData);
if (result.success) {
  // result.data is properly typed
  console.log(result.data.email); // string
  console.log(result.data.age);   // number | undefined
}
Enter fullscreen mode Exit fullscreen mode

Comprehensive Type Support

Fortify Schema supports a wide range of validation types:

const ComprehensiveSchema = Interface({
  // Basic types
  name: "string",
  count: "number", 
  active: "boolean",
  timestamp: "date",

  // Constrained types
  username: "string(3,20)",     // 3-20 characters
  score: "number(0,100)",       // Range validation

  // Format validation
  email: "email",
  website: "url",
  phone: "phone",
  userId: "uuid",

  // Arrays with constraints
  tags: "string[]",             // Required array
  scores: "number[]?",          // Optional array
  limitedTags: "string[](1,5)", // 1-5 items

  // Union types
  status: "active|inactive|pending",
  priority: "low|medium|high",

  // Literal values
  version: "2.0",
  type: "user",

  // Optional fields
  description: "\"string?\","
  metadata: "any?"
});
Enter fullscreen mode Exit fullscreen mode

Nested Object Validation

Complex nested structures are handled naturally:

const ProfileSchema = Interface({
  user: {
    id: "uuid",
    email: "email",
    profile: {
      firstName: "string(1,50)",
      lastName: "string(1,50)",
      avatar: "url?",
      settings: {
        theme: "light|dark|auto",
        language: "string(/^[a-z]{2}$/)",
        notifications: {
          email: "boolean",
          push: "boolean",
          sms: "boolean"
        }
      }
    }
  },
  metadata: {
    createdAt: "date",
    lastUpdated: "date",
    version: "number"
  }
});
Enter fullscreen mode Exit fullscreen mode

Conditional Validation Logic

The conditional validation system allows you to express complex business rules directly in schema definitions:

const ConditionalSchema = Interface({
  // Context objects for conditional logic
  config: "any?",
  user: "any?",

  // Core data
  id: "uuid",
  type: "individual|business",

  // Conditional fields based on runtime properties
  businessName: "when type=business *? string(2,100) : =null",
  taxId: "when type=business *? string : =null",

  // Conditional validation based on configuration
  requiresApproval: "when config.strictMode.$exists() *? boolean : =false",

  // Default values based on conditions
  permissions: "when user.role=admin *? string[] : =[\"read\"]",

  // Complex nested conditions
  advancedFeatures: "when user.plan=premium && config.enableAdvanced.$exists() *? object : =null"
});
Enter fullscreen mode Exit fullscreen mode

Runtime Validation Methods

The conditional system includes several runtime validation methods:

const RuntimeValidationSchema = Interface({
  data: "any?",

  // Property existence
  hasProfile: "when data.profile.$exists() *? boolean : =false",

  // Empty state checking
  hasItems: "when data.items.$empty() *? boolean : =true",

  // Null validation
  isConfigured: "when data.config.$null() *? boolean : =false",

  // String operations
  hasKeyword: "when data.title.$contains(important) *? boolean : =false",
  isPrefixed: "when data.code.$startsWith(PRE-) *? boolean : =false",

  // Numeric range validation
  inValidRange: "when data.score.$between(0,100) *? boolean : =false",

  // Value inclusion
  hasValidStatus: "when data.status.$in(active,pending,completed) *? boolean : =false"
});
Enter fullscreen mode Exit fullscreen mode

Practical Application: API Request Validation

Here's a real-world example for validating API requests:

const CreateProductRequestSchema = Interface({
  // Request context
  user: "any?",
  permissions: "any?",

  // Product data
  name: "string(1,200)",
  description: "\"string(,2000)?\","
  price: "number(0.01,999999.99)",
  category: "electronics|books|clothing|home|sports",

  // Inventory
  initialStock: "number(0,)",
  trackInventory: "boolean",

  // Conditional validation based on user permissions
  bulkDiscount: "when permissions.canSetBulkPricing.$exists() *? object? : =null",

  // Admin-only fields
  internalNotes: "when user.role=admin *? string? : =null",
  costPrice: "when user.role=admin *? number(0,) : =null",

  // Category-specific requirements
  isbn: "when category=books *? string : =null",
  size: "when category=clothing *? string : =null",

  // Media uploads
  images: "url[](1,10)",
  documents: "when category=books *? url[] : =[]",

  // Metadata
  tags: "string[](0,20)",
  status: "draft|active",
  publishAt: "date?"
});
Enter fullscreen mode Exit fullscreen mode

Schema Transformations

Fortify Schema provides utilities for creating schema variations:

const BaseUserSchema = Interface({
  id: "uuid",
  email: "email",
  firstName: "string",
  lastName: "string",
  password: "string",
  role: "user|admin",
  createdAt: "date"
});

// Schema transformations
const PublicUserSchema = Mod.omit(BaseUserSchema, ["password"]);
const UpdateUserSchema = Mod.partial(Mod.omit(BaseUserSchema, ["id", "createdAt"]));
const AdminUserSchema = Mod.extend(BaseUserSchema, {
  permissions: "string[]",
  lastLogin: "date?",
  adminNotes: "string?"
});

// Combining schemas
const UserWithProfileSchema = Mod.extend(BaseUserSchema, {
  profile: {
    avatar: "url?",
    bio: "string(,500)?",
    website: "url?",
    location: "string?"
  }
});
Enter fullscreen mode Exit fullscreen mode

Error Handling and Debugging

Fortify Schema provides detailed error information for debugging:

const result = UserSchema.safeParse(invalidData);

if (!result.success) {
  result.errors.forEach(error => {
    console.log(`Field: ${error.path.join('.')}`);
    console.log(`Error: ${error.message}`);
    console.log(`Code: ${error.code}`);
    console.log(`Expected: ${error.expected}`);
    console.log(`Received: ${error.received}`);
  });
}
Enter fullscreen mode Exit fullscreen mode

Performance Considerations

Fortify Schema is designed for production use with several performance optimizations:

  • Schema Compilation: Schemas are optimized at creation time rather than during validation
  • Caching: Repeated validations benefit from intelligent caching of constraint checks
  • Memory Efficiency: Minimal memory overhead per validation operation
  • Early Termination: Validation stops on first error for faster feedback

Development Tooling

A VS Code extension provides enhanced development experience:

  • Syntax highlighting for schema definitions
  • IntelliSense with type and method completion
  • Real-time validation with inline error detection
  • Documentation on hover for validation types

Installation and Setup

npm install fortify-schema
Enter fullscreen mode Exit fullscreen mode

Basic usage:

import { Interface } from "fortify-schema";

const schema = Interface({
  email: "email",
  name: "string(2,100)",
  age: "number(18,)?"
});

const result = schema.safeParse(data);
Enter fullscreen mode Exit fullscreen mode

Use Cases

Fortify Schema is well-suited for:

  • API Request Validation: Complex request schemas with role-based field requirements
  • Configuration Management: Validating application configuration with conditional settings
  • Form Processing: Multi-step forms with conditional field requirements
  • Data Pipeline Validation: Ensuring data integrity in processing workflows
  • Business Rule Enforcement: Expressing complex validation logic in a readable format

Technical Architecture

The library provides several validation methods:

  • parse(data): Synchronous validation with exceptions
  • safeParse(data): Safe validation returning result objects
  • parseAsync(data): Asynchronous validation with promises
  • safeParseAsync(data): Safe asynchronous validation

All methods maintain full TypeScript type inference, ensuring validated data is properly typed without additional assertions.


Fortify Schema offers TypeScript developers a validation solution that integrates naturally with existing codebases while providing advanced conditional validation capabilities. The interface-like syntax reduces the learning curve, while the conditional validation system handles complex business logic requirements.

Resources:

Have you worked with conditional validation requirements in your TypeScript projects? What approaches have you found most effective?

Top comments (1)

Collapse
 
procoder1728 profile image
ProCoder 😌

🀩πŸ₯°