Fortify Schema
TypeScript-First Validation Library with Intuitive Syntax
Report any bugs to Nehonix-Team via: team@nehonix.space
Fortify Schema, is a powerful TypeScript-first schema validation library that combines the familiarity of TypeScript interfaces with runtime validation and automatic type inference.
β¨ Key Features
- π― Interface-like Syntax β Define schemas using familiar TypeScript interface syntax
- β‘ Runtime Type Inference β Validated data is automatically typed without manual casting
- π« Non-Empty Validation β New "!" syntax for non-empty strings and non-zero numbers
- π§ Rich Constraints β Support for string length, number ranges, arrays, unions, and constants
- 
π οΈ Schema Utilities β Transform schemas with partial(),omit(), andextend()methods
- π¨ VSCode Integration β Dedicated extension with syntax highlighting and IntelliSense
- π¦ Zero Dependencies β Lightweight and performant
π Quick Start
Installation
npm install fortify-schema
# or
yarn add fortify-schema
# or
pnpm add fortify-schema
Requirements: TypeScript 4.5+ and Node.js 14+
Basic Usage
import { Interface } from 'fortify-schema';
// Define your schema
const UserSchema = Interface({
  id: "number!",                 // Non-zero number (basic type with "!")
  name: "string(2,50)",          // String with length constraints (2-50 chars)
  email: "email",                // Email validation (no "!" available)
  age: "number(18,120)?",        // Optional age between 18-120
  bio: "string!?",               // Optional, but if provided must be non-empty
  tags: "string[](1,10)?",       // Optional array of 1-10 strings
  status: "active|inactive",     // Union type
  role: "=admin",                // Constant value
});
// Valid data
const userData = {
  id: 1,                         // β
 Non-zero number
  name: "Jane Doe",              // β
 String within length constraints
  email: "hello@example.com",    // β
 Valid email format
  status: "active",
  role: "admin",
};
// Invalid data examples
const invalidData = {
  id: 0,                         // β Fails: number! rejects 0
  name: "J",                     // β Fails: string(2,50) requires minimum 2 chars
  email: "invalid-email",        // β Fails: invalid email format
  status: "active",
  role: "admin",
};
const result = UserSchema.safeParse(userData);
if (result.success) {
  // β
 Data is valid and properly typed
  console.log("Welcome,", result.data.name);
  console.log("User ID:", result.data.id); // TypeScript knows this is a number
} else {
  // β Handle validation errors
  console.error("Validation failed:", result.error.issues);
}
π Schema Syntax Reference
Primitive Types
const schema = Interface({
  text: "string",           // Any string (including empty "")
  count: "number",          // Any number (including 0)
  flag: "boolean",          // Boolean value
  timestamp: "date",        // Date object
  contact: "email",         // Valid email format
  website: "url",           // Valid URL format
});
Non-Empty/Non-Zero Values (New!)
const schema = Interface({
  // "!" syntax - ONLY for basic string and number types
  name: "string!",          // Non-empty string (rejects "")
  count: "number!",         // Non-zero number (rejects 0)
  // Other types don't support "!" - they have their own validation logic
  email: "email",           // β Cannot do "email!" - not supported
  url: "url",               // β Cannot do "url!" - not supported  
  date: "date",             // β Cannot do "date!" - not supported
  // Optional variants
  title: "string!?",        // Optional, but if provided must be non-empty
  score: "number!?",        // Optional, but if provided must be non-zero
});
Constrained Types
const schema = Interface({
  // Constraint syntax - for length/range validation
  username: "string(3,20)",      // String with length 3-20
  age: "number(0,150)",          // Number between 0-150
  score: "number(0,)",           // Number >= 0
  code: "string(,10)",           // String with max length 10
});
// Non-empty validation - alternative to constraints
const nonEmptySchema = Interface({
  title: "string!",              // Non-empty string (simpler than "string(1,)")
  quantity: "number!",           // Non-zero number
});
// IMPORTANT: Choose ONE approach - cannot combine both:
// β
 "string(1,100)"     - Use constraints for length validation
// β
 "string!"           - Use for simple non-empty validation  
// β "string!(1,100)"    - INVALID: Cannot combine both syntaxes
Arrays
const schema = Interface({
  tags: "string[]",              // Array of strings
  scores: "number[](1,5)",       // 1-5 numbers
  items: "string[](,10)",        // Max 10 strings
  required: "number[](1,)",      // At least 1 number
});
Unions and Constants
const schema = Interface({
  status: "pending|approved|rejected",  // Union type
  role: "=admin",                       // Constant value
  priority: "low|medium|high",          // Multiple options
  version: "=1.0.0",                    // Exact match
});
Optional Fields
const schema = Interface({
  id: "number!",          // Required non-zero number
  name: "string!",        // Required non-empty string
  email: "email!?",       // Optional, but if provided must be non-empty
  phone: "string?",       // Optional, can be empty string
  bio: "string!?",        // Optional, but if provided must be non-empty
  tags: "string[]?",      // Optional array
});
π§ Schema Utilities
Making Fields Optional
const BaseSchema = Interface({
  id: "number",
  name: "string",
  email: "email",
});
// Make specific fields optional
const PartialSchema = Mod.partial(BaseSchema, ['email']);
// Result: { id: number, name: string, email?: string }
// Make all fields optional
const AllOptionalSchema = Mod.partial(BaseSchema);
// Result: { id?: number, name?: string, email?: string }
Omitting Fields
const UserSchema = Interface({
  id: "number",
  name: "string", 
  email: "email",
  password: "string",
});
// Remove sensitive fields
const PublicUserSchema = Mod.omit(UserSchema, ['password']);
// Result: { id: number, name: string, email: string }
Extending Schemas
const BaseSchema = Interface({
  id: "number",
  name: "string",
});
// Add new fields
const ExtendedSchema = Mod.extend(BaseSchema, {
  email: "email",
  createdAt: "date",
});
// Result: { id: number, name: string, email: string, createdAt: Date }
π Advanced Examples
API Response Validation
const ApiResponseSchema = Interface({
  success: "boolean",
  data: Interface({
    users: Interface({
      id: "number",
      username: "string(3,20)",
      email: "email",
      role: "admin|user|moderator",
      isActive: "boolean",
      lastLogin: "date?",
    })[],
  }),
  pagination: Interface({
    page: "number(1,)",
    limit: "number(1,100)",
    total: "number(0,)",
  }),
});
// Use in API handler
async function getUsers(req: Request) {
  const response = await fetch('/api/users');
  const rawData = await response.json();
  const result = ApiResponseSchema.safeParse(rawData);
  if (!result.success) {
    throw new Error('Invalid API response format');
  }
  // Fully typed response data
  return result.data;
}
Form Validation
const ContactFormSchema = Interface({
  name: "string!",              // Required non-empty name (basic validation)
  email: "email",               // Required valid email (has built-in validation)
  phone: "string(10,15)?",      // Optional phone, if provided 10-15 chars
  subject: "support|sales|general",
  message: "string(10,1000)",   // Required message, 10-1000 chars (use constraints)
  newsletter: "boolean?",
});
// Test data
const formData = {
  name: "",                     // β Will fail: string! rejects empty
  email: "user@example.com",    // β
 Valid email
  subject: "support",           // β
 Valid union value
  message: "Hi there",          // β
 Valid length
};
function handleFormSubmit(formData: unknown) {
  const result = ContactFormSchema.safeParse(formData);
  if (!result.success) {
    return {
      success: false,
      errors: result.error.issues.map(issue => ({
        field: issue.path.join('.'),
        message: issue.message
      }))
    };
  }
  // Process valid form data
  return { success: true, data: result.data };
}
Configuration Validation
const ConfigSchema = Interface({
  database: Interface({
    host: "string!",            // Required non-empty host
    port: "number(1,65535)",    // Required port with range validation
    name: "string(1,50)",       // Required database name with length constraint
    ssl: "boolean?",
  }),
  redis: Interface({
    url: "url",                 // Required valid URL (has built-in validation)
    ttl: "number(60,)",         // Required TTL with minimum constraint
  })["?"], // Optional nested object
  features: Interface({
    enableCache: "boolean",
    maxUsers: "number!",        // Required non-zero max users
    environment: "development|staging|production",
  }),
});
// Load and validate configuration
function loadConfig(): ConfigType {
  const rawConfig = JSON.parse(process.env.APP_CONFIG || '{}');
  const result = ConfigSchema.safeParse(rawConfig);
  if (!result.success) {
    console.error('Invalid configuration:', result.error.issues);
    process.exit(1);
  }
  return result.data;
}
type ConfigType = typeof ConfigSchema.infer;
π― Use Cases
- API Validation β Validate request/response payloads with automatic typing
- Form Processing β Ensure user input meets requirements before processing
- Configuration Management β Validate app settings and environment variables
- Data Pipelines β Type-safe data transformation and validation
- Database Models β Validate data before database operations
- Integration Testing β Ensure external APIs return expected data structures
π οΈ Error Handling
const result = UserSchema.safeParse(invalidData);
if (!result.success) {
  // Access detailed error information
  result.error.issues.forEach(issue => {
    console.log({
      path: issue.path.join('.'),        // Field path: "user.email"
      code: issue.code,                  // Error code: "invalid_email" 
      message: issue.message,            // Human readable message
      received: issue.received,          // Actual value received
      expected: issue.expected,          // Expected type/format
    });
  });
}
π VSCode Extension
Enhance your development experience with our VSCode extension:
- Search for "Fortify Schema" in the Extensions marketplace
- Install the extension for syntax highlighting and IntelliSense
- Enjoy autocomplete and validation in your schema definitions
See the GitHub. Report any bugs to team@nehonix.space
 
 
              
 
    
Top comments (0)