UNPKG

fortify-schema

Version:

A modern TypeScript validation library designed around familiar interface syntax and powerful conditional validation. Experience schema validation that feels natural to TypeScript developers while unlocking advanced runtime validation capabilities.

396 lines (317 loc) 9.12 kB
# FortifyJS Schema System A schema validation system with **TypeScript interface-like syntax** that's incredibly easy to use and completely separate from validators. ## 🎯 The Problem with Current Schema Libraries **Zod, Joi, Yup** - they all suffer from the same issues: - **Complex syntax** that doesn't feel natural - **Verbose definitions** that are hard to read - **Steep learning curves** for simple validation - **Heavy bundle sizes** with unnecessary features ## 💡 Our Solution: Interface-Based Schemas We created a schema system that **looks and feels exactly like TypeScript interfaces** but provides runtime validation: ```typescript // ❌ Zod (complex and verbose) const UserSchema = z.object({ id: z.number().int().positive(), email: z.string().email(), name: z.string().min(2).max(50), age: z.number().int().min(0).max(120).optional(), isActive: z.boolean().default(true), tags: z.array(z.string()).max(10).optional(), }); // ✅ FortifyJS Interface (clean and intuitive) const UserSchema = Interface({ id: "number", email: "email", name: "string", age: "number?", // Optional with ? isActive: "boolean?", // Optional with smart defaults tags: "string[]?", // Optional array role: "=admin", // Constant values }); ``` **It's literally that simple!** 🎉 ## 🚀 Two Powerful Approaches ### 1. Interface-Based (Recommended) **TypeScript interface syntax with runtime validation** ```typescript import { Interface } from "fortify-schema"; const UserSchema = Interface({ id: "number", email: "email", name: "string", age: "number?", // Optional isActive: "boolean?", // Optional tags: "string[]?", // Optional array role: "=admin", // Constant value profile: { // Nested object bio: "string?", avatar: "url?", }, }); ``` ### 2. Fluent API (Traditional) **Chainable method syntax for complex validation** ```typescript import { Schema } from "fortify-schema"; const UserSchema = Schema.object({ id: Schema.number().int().positive(), email: Schema.string().email(), name: Schema.string().min(2).max(50), age: Schema.number().int().min(0).max(120).optional(), isActive: Schema.boolean().default(true), }); ``` ## 🎯 Why Interface-Based is Better ### ✅ Pros of Interface Syntax: - **Instantly familiar** to TypeScript developers - **Extremely readable** - looks like documentation - **Minimal learning curve** - no new API to learn - **Faster to write** - less typing, more intuitive - **Self-documenting** - schema IS the documentation ### ❌ Cons of Traditional Fluent APIs: - **Verbose and complex** - lots of chaining - **Hard to read** - especially with nested objects - **Learning curve** - need to memorize API methods - **More typing** - repetitive method calls ## 📚 Interface Field Types ### Basic Types ```typescript { name: "string", // String age: "number", // Number active: "boolean", // Boolean created: "date", // Date data: "any" // Any type } ``` ### Optional Fields (with `?`) ```typescript { name: "string", // Required age: "number?", // Optional bio: "string?", // Optional tags: "string[]?" // Optional array } ``` ### Format Validation ```typescript { email: "email", // Email format website: "url", // URL format id: "uuid", // UUID format phone: "phone", // Phone format slug: "slug", // URL slug username: "username" // Username format } ``` ### Number Types ```typescript { id: "int", // Integer count: "positive", // Positive number price: "float", // Float number rating: "number" // Any number } ``` ### Array Types ```typescript { tags: "string[]", // Array of strings scores: "number[]", // Array of numbers flags: "boolean[]", // Array of booleans ids: "int[]", // Array of integers emails: "email[]", // Array of emails urls: "url[]" // Array of URLs } ``` ### Constant Values (Safe Syntax) ```typescript import { Make } from "fortify-schema"; { version: Make.const("1.0"), // Safe constant string status: Make.const(200), // Safe constant number type: Make.const("user"), // Safe constant value enabled: Make.const(true) // Safe constant boolean } ``` ### Union Types (Multiple Allowed Values) ```typescript { status: Make.union("pending", "accepted", "rejected"), priority: Make.union("low", "medium", "high"), role: Make.unionOptional("user", "admin", "moderator") // Optional union } ``` ### Nested Objects ```typescript { user: { // Nested object name: "string", email: "email" }, address: { // Deeply nested street: "string", city: "string", coordinates: { lat: "number", lng: "number" } } } ``` ## 🔥 Real-World Examples ### User Registration ```typescript const UserRegistrationSchema = Interface({ // Required fields email: "email", password: "string", firstName: "string", lastName: "string", // Optional fields age: "number?", phone: "phone?", website: "url?", // Arrays interests: "string[]?", // Nested object address: { street: "string", city: "string", zipCode: "string", country: "string", }, // Safe constants type: Make.const("user"), version: Make.const("1.0"), }); ``` ### API Response ```typescript const APIResponseSchema = Interface({ success: "boolean", status: Make.const(200), // Safe constant status data: { users: [ { // Array of objects id: "int", email: "email", profile: { name: "string", avatar: "url?", }, }, ], pagination: { page: "int", total: "int", hasNext: "boolean", }, }, errors: "string[]?", timestamp: "date", }); ``` ### Configuration ```typescript const ConfigSchema = Interface({ database: { host: "string", port: "int", name: "string", ssl: "boolean?", }, server: { port: "int", cors: "boolean?", }, features: { auth: "boolean?", logging: "boolean?", }, environment: Make.const("production"), // Safe constant }); ``` ## 🛠️ Validation Methods ### Parse (Throws on Error) ```typescript try { const user = UserSchema.parse(userData); // user is fully typed and validated } catch (error) { console.error("Validation failed:", error.message); } ``` ### Safe Parse (Returns Result) ```typescript const result = UserSchema.safeParse(userData); if (result.success) { console.log("Valid data:", result.data); } else { console.log("Errors:", result.errors); console.log("Warnings:", result.warnings); } ``` ### Strict Mode ```typescript const StrictSchema = Interface({ id: "number", name: "string", }).strict(); // No extra properties allowed ``` ## 🛡️ Safer Syntax for Constants and Unions ### The Problem with Raw Values ```typescript // ❌ DANGEROUS - Looks like a string type but it's actually a constant! const BadSchema = Interface({ status: "pending", // Is this a string type or constant "pending"? role: "=admin", // Very confusing and error-prone! }); ``` ### The Solution - Make ```typescript // ✅ SAFE - Crystal clear what's a type vs constant import { Interface, Make } from "fortify-schema"; const GoodSchema = Interface({ // Clear type definitions name: "string", email: "email", // Clear constant values status: Make.const("pending"), role: Make.const("admin"), // Clear union types (multiple allowed values) priority: Make.union("low", "medium", "high"), category: Make.unionOptional("tech", "business", "design"), }); ``` ### Benefits of Make - **🔒 Type Safety**: No confusion between types and constants - **📖 Readability**: Crystal clear intent in your schemas - **🐛 Bug Prevention**: Eliminates dangerous ambiguity - **🔧 IntelliSense**: Better IDE support and autocomplete ## 📊 Comparison | Feature | FortifyJS Interface | Zod | Joi | | ------------------ | ------------------- | ----------- | ---------- | | **Syntax** | TypeScript-like | Complex API | Verbose | | **Learning Curve** | None | Steep | Moderate | | **Readability** | Excellent | Poor | Fair | | **Bundle Size** | Small | Large | Very Large | | **Type Safety** | Full | Full | Limited | | **Performance** | Fast | Slow | Slower | ## 🎉 Get Started ```bash npm install fortify-schema ``` ```typescript import { Interface } from "fortify-schema"; const MySchema = Interface({ id: "number", name: "string", email: "email", active: "boolean?", }); const result = MySchema.safeParse(data); ``` **That's it!** No complex APIs to learn, no verbose syntax - just clean, readable schemas that work exactly like TypeScript interfaces! 🚀