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.
830 lines (634 loc) • 18.8 kB
Markdown
# Field Types Reference
Complete guide to all field types available in Fortify Schema, with examples and constraints.
## 📚 Table of Contents
- [Basic Types](#basic-types)
- [Constrained Types](#constrained-types)
- [Format Validation](#format-validation)
- [Array Types](#array-types)
- [Union Types](#union-types)
- [Optional Types](#optional-types)
- [Literal Values](#literal-values)
- [Custom Patterns](#custom-patterns)
- [Special Types](#special-types)
## Basic Types
### String Type
```typescript
const StringSchema = Interface({
// Basic string
name: "string",
// String with length constraints
username: "string(3,20)", // 3-20 characters
password: "string(8,)", // Minimum 8 characters
code: "string(6,6)", // Exactly 6 characters
description: "string(,500)", // Maximum 500 characters
// Optional string
nickname: "string?",
bio: "string(,200)?",
});
```
**Constraint Syntax:**
- `string(min,max)` - Length between min and max
- `string(min,)` - Minimum length
- `string(,max)` - Maximum length
- `string(exact,exact)` - Exact length
### Number Type
```typescript
const NumberSchema = Interface({
// Basic number
age: "number",
// Number with range constraints
score: "number(0,100)", // Range 0-100
price: "number(0.01,)", // Minimum 0.01
discount: "number(,1.0)", // Maximum 1.0
temperature: "number(-273.15,)", // Absolute zero minimum
// Integer constraints
count: "int", // Integer only
positiveInt: "int(1,)", // Positive integer
limitedInt: "int(0,1000)", // Integer range
// Positive/negative shortcuts
id: "positive", // Positive number
debt: "negative", // Negative number
// Optional number
rating: "number(1,5)?",
});
```
**Constraint Syntax:**
- `number(min,max)` - Range between min and max
- `int` - Integer validation
- `positive` - Positive number (> 0)
- `negative` - Negative number (< 0)
### Boolean Type
```typescript
const BooleanSchema = Interface({
// Basic boolean
isActive: "boolean",
// Optional boolean
isVerified: "boolean?",
// Boolean with smart conversion (strings "true"/"false")
enableFeature: "boolean",
});
```
### Date Type
```typescript
const DateSchema = Interface({
// Basic date
createdAt: "date",
// Optional date
updatedAt: "date?",
lastLogin: "date?",
// Date validation accepts:
// - Date objects
// - ISO date strings
// - Unix timestamps (numbers)
birthday: "date",
expiresAt: "date",
});
```
## Constrained Types
### String Constraints
```typescript
const StringConstraintsSchema = Interface({
// Length constraints
shortCode: "string(2,5)", // 2-5 characters
longText: "string(100,)", // Minimum 100 characters
tweet: "string(,280)", // Maximum 280 characters
// Exact length
countryCode: "string(2,2)", // Exactly 2 characters
zipCode: "string(5,5)", // Exactly 5 characters
// Combined with optional
optionalCode: "string(3,10)?", // Optional, 3-10 chars if present
});
```
### Number Constraints
```typescript
const NumberConstraintsSchema = Interface({
// Range constraints
percentage: "number(0,100)", // 0-100
probability: "number(0,1)", // 0-1
temperature: "number(-50,50)", // -50 to 50
// Open ranges
minimumAge: "number(18,)", // 18 or higher
maxDiscount: "number(,0.5)", // 0.5 or lower
// Integer constraints
pageNumber: "int(1,)", // Positive integer
itemCount: "int(0,1000)", // 0-1000 integer
// Special number types
userId: "positive", // Any positive number
balance: "number", // Any number (can be negative)
});
```
### Array Constraints
```typescript
const ArrayConstraintsSchema = Interface({
// Basic arrays
tags: "string[]", // Array of strings
scores: "number[]", // Array of numbers
flags: "boolean[]", // Array of booleans
// Array size constraints
limitedTags: "string[](1,5)", // 1-5 items
minItems: "string[](2,)", // Minimum 2 items
maxItems: "string[](,10)", // Maximum 10 items
exactItems: "string[](3,3)", // Exactly 3 items
// Optional arrays
optionalTags: "string[]?", // Optional array
limitedOptional: "string[](1,5)?", // Optional, 1-5 items if present
// Nested constraints
usernames: "string(3,20)[](1,100)", // Array of 3-20 char strings, 1-100 items
ages: "int(0,120)[](,50)", // Array of 0-120 integers, max 50 items
});
```
## Format Validation
### Built-in Formats
```typescript
const FormatSchema = Interface({
// Email validation
email: "email",
workEmail: "email?",
// URL validation
website: "url",
avatar: "url?",
// UUID validation
id: "uuid",
sessionId: "uuid?",
// Phone validation (international format)
phone: "phone",
mobile: "phone?",
// IP address validation
serverIp: "ip",
clientIp: "ip?",
// JSON validation
config: "json",
metadata: "json?",
// Hex color validation
primaryColor: "hexcolor",
accentColor: "hexcolor?",
// Base64 validation
encodedData: "base64",
attachment: "base64?",
// JWT validation
authToken: "jwt",
refreshToken: "jwt?",
// Semantic version validation
appVersion: "semver",
minVersion: "semver?",
});
```
### URL Validation with Arguments
Fortify Schema provides powerful URL validation with specialized arguments for different use cases. Each URL argument enforces specific protocol and security restrictions.
#### Available URL Arguments
```typescript
const UrlSchema = Interface({
// Basic URL validation (defaults to web protocols)
website: "url",
// HTTPS-only validation (most secure)
secureApi: "url.https",
paymentGateway: "url.https",
// HTTP-only validation
legacyEndpoint: "url.http",
// Web protocols (HTTP + HTTPS)
publicWebsite: "url.web",
// Development mode (permissive for localhost/IPs)
devServer: "url.dev",
localApi: "url.dev",
// FTP-only validation
fileServer: "url.ftp",
backupLocation: "url.ftp",
// Optional URL arguments
optionalWebsite: "url.https?",
optionalApi: "url.web?",
// URL arrays with arguments
apiEndpoints: "url.https[]",
devServers: "url.dev[](1,5)",
});
```
#### URL Argument Specifications
| Argument | Protocols | Localhost | Private IPs | Use Case |
| ----------- | ----------- | --------- | ----------- | --------------------- |
| `url` | HTTP, HTTPS | ❌ | ❌ | General web URLs |
| `url.https` | HTTPS only | ❌ | ❌ | Secure APIs, payments |
| `url.http` | HTTP only | ❌ | ❌ | Legacy systems |
| `url.web` | HTTP, HTTPS | ❌ | ❌ | Public websites |
| `url.dev` | HTTP, HTTPS | ✅ | ✅ | Development/testing |
| `url.ftp` | FTP only | ❌ | ❌ | File transfers |
#### Security Features
```typescript
const SecurityExamples = Interface({
// ✅ HTTPS-only - blocks HTTP, localhost, IPs
securePayment: "url.https",
// Valid: "https://api.stripe.com/v1/charges"
// Invalid: "http://api.stripe.com", "https://localhost:3000"
// ✅ Development mode - allows localhost and IPs
devEnvironment: "url.dev",
// Valid: "http://localhost:3000", "https://192.168.1.100:8080"
// Invalid: "ftp://files.example.com"
// ✅ Web protocols - production-ready validation
publicApi: "url.web",
// Valid: "https://api.example.com", "http://legacy.example.com"
// Invalid: "ftp://files.com", "https://localhost:3000"
});
```
#### Error Handling for Invalid Arguments
```typescript
// ❌ Invalid URL arguments throw clear errors
try {
const InvalidSchema = Interface({
badUrl: "url.invalid", // Invalid argument
});
} catch (error) {
console.log(error.message);
// "Invalid URL argument: url.invalid. Valid arguments are: url.https, url.http, url.web, url.dev, url.ftp"
}
// ❌ Case-sensitive validation
try {
const CaseSchema = Interface({
badCase: "URL.HTTPS", // Wrong case
});
} catch (error) {
console.log("URL arguments are case-sensitive");
}
```
#### Real-World Examples
```typescript
const ECommerceSchema = Interface({
// Payment processing (HTTPS required)
paymentWebhook: "url.https",
stripeEndpoint: "url.https",
// Public website URLs
companyWebsite: "url.web",
socialMediaLinks: "url.web[]?",
// Development configuration
devApiUrl: "url.dev",
testingEndpoints: "url.dev[](1,10)",
// File storage
ftpBackupServer: "url.ftp",
assetCdnUrls: "url.https[]",
});
const MicroserviceSchema = Interface({
// Service discovery
authService: "url.https",
userService: "url.https",
// Internal communication (dev environment)
internalServices: "url.dev[]",
// External integrations
thirdPartyApis: "url.https[]",
webhookUrls: "url.https[](1,5)",
});
```
### Advanced Format Types
#### Hex Color Validation
```typescript
const ColorSchema = Interface({
// Basic hex colors
primaryColor: "hexcolor", // #RGB, #RRGGBB, #RRGGBBAA
secondaryColor: "hexcolor?", // Optional color
// Color arrays
palette: "hexcolor[]", // Array of colors
gradientColors: "hexcolor[](2,10)", // 2-10 colors
// Examples of valid values:
// "#F00", "#FF0000", "#FF0000FF"
// "#abc", "#ABCDEF", "#12345678"
});
```
#### Base64 Validation
```typescript
const DataSchema = Interface({
// Basic Base64
encodedData: "base64",
attachment: "base64?",
// Base64 arrays
files: "base64[]",
images: "base64[](1,5)",
// Examples of valid values:
// "SGVsbG8gV29ybGQ="
// "VGhpcyBpcyBhIHRlc3Q="
});
```
#### JWT (JSON Web Token) Validation
```typescript
const AuthSchema = Interface({
// JWT tokens
accessToken: "jwt",
refreshToken: "jwt?",
// JWT arrays
tokens: "jwt[]",
sessionTokens: "jwt[](1,3)",
// Examples of valid values:
// "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
});
```
#### Semantic Version (SemVer) Validation
```typescript
const VersionSchema = Interface({
// Semantic versions
appVersion: "semver",
minVersion: "semver?",
// Version arrays
supportedVersions: "semver[]",
compatibleVersions: "semver[](1,10)",
// Examples of valid values:
// "1.0.0", "2.1.3", "1.0.0-alpha.1"
// "1.2.3-beta.4+build.5"
});
```
### Custom Regex Patterns
```typescript
const RegexSchema = Interface({
// US ZIP code
zipCode: "string(/^\\d{5}(-\\d{4})?$/)",
// Product code (2 letters + 4 digits)
productCode: "string(/^[A-Z]{2}\\d{4}$/)",
// Username (alphanumeric + underscore, 3-20 chars)
username: "string(/^[a-zA-Z0-9_]{3,20}$/)",
// Hex color code
color: "string(/^#[0-9A-Fa-f]{6}$/)",
// Credit card number (basic format)
creditCard: "string(/^\\d{4}[\\s-]?\\d{4}[\\s-]?\\d{4}[\\s-]?\\d{4}$/)",
// ISO country code
country: "string(/^[A-Z]{2}$/)",
// Semantic version
version: "string(/^\\d+\\.\\d+\\.\\d+$/)",
// Optional regex patterns
optionalCode: "string(/^[A-Z]{3}\\d{3}$/)?",
});
```
## Array Types
### Basic Arrays
```typescript
const ArraySchema = Interface({
// Simple arrays
tags: "string[]",
scores: "number[]",
active: "boolean[]",
dates: "date[]",
// Optional arrays
categories: "string[]?",
ratings: "number[]?",
// Empty arrays allowed by default
emptyAllowed: "string[]", // Can be []
});
```
### Constrained Arrays
```typescript
const ConstrainedArraySchema = Interface({
// Size constraints
requiredTags: "string[](1,)", // At least 1 item
limitedTags: "string[](1,10)", // 1-10 items
maxTags: "string[](,5)", // Maximum 5 items
exactTags: "string[](3,3)", // Exactly 3 items
// Element constraints with array constraints
usernames: "string(3,20)[](1,100)", // 1-100 usernames, each 3-20 chars
ages: "int(0,120)[](,50)", // Max 50 ages, each 0-120
emails: "email[](1,10)", // 1-10 email addresses
urls: "url[](,5)", // Max 5 URLs
// Complex nested constraints
productCodes: "string(/^[A-Z]{2}\\d{4}$/)[](1,)", // At least 1 valid product code
});
```
### Nested Arrays
```typescript
const NestedArraySchema = Interface({
// Array of objects
users: {
id: "uuid",
name: "string",
email: "email"
}[],
// Array of arrays
matrix: "number[][]",
coordinates: "number[](2,2)[]", // Array of coordinate pairs
// Optional nested arrays
optionalMatrix: "number[][]?",
groups: "string[][]?"
});
```
## Union Types
### Basic Unions
```typescript
const UnionSchema = Interface({
// String unions
status: "active|inactive|pending",
role: "admin|user|guest|moderator",
theme: "light|dark|auto",
// Number unions
priority: "1|2|3|4|5",
version: "1.0|2.0|3.0",
// Mixed type unions
id: "string|number",
value: "string|number|boolean",
// Optional unions
optionalStatus: "active|inactive?",
optionalValue: "string|number?",
});
```
### Complex Unions
```typescript
const ComplexUnionSchema = Interface({
// Union with constraints
identifier: "string(3,)|number(1,)", // String 3+ chars OR number 1+
// Union with formats
contact: "email|phone", // Email OR phone number
// Union with arrays
data: "string[]|number[]", // Array of strings OR numbers
// Union with regex
code: "string(/^[A-Z]{3}$/)|string(/^\\d{6}$/)", // 3 letters OR 6 digits
// Nested unions in objects
config: {
mode: "development|staging|production",
debug: "boolean|string",
port: "number|string",
},
});
```
## Optional Types
### Basic Optional
```typescript
import { NehoID as ID } from "nehoid";
const OptionalSchema = Interface({
// Required fields
id: "uuid",
name: "string",
// Optional fields (can be undefined)
nickname: "string?",
bio: "string?",
avatar: "url?",
// Optional with constraints
age: "number(0,120)?",
tags: "string[](1,10)?",
// Optional nested objects
profile: {
firstName: "string?",
lastName: "string",
middleName: "string?", // Optional within nested object
},
});
const profile = {
lastName: "Eleazar",
};
const result = OptionalSchema.safeParse({
id: ID.uuid(),
age: 1000,
name: "Seth",
profile,
});
if (result.success) {
console.log("✅ Expected success:", result.data);
} else {
console.log("❌ Unexpected errors:", result.errors);
}
```
### Optional vs Nullable
```typescript
const OptionalVsNullableSchema = Interface({
// Optional - can be undefined, not present in object
optionalField: "string?",
// Nullable - must be present, can be null
nullableField: "string|null",
// Both optional and nullable
flexibleField: "string|null?",
// Array variations
optionalArray: "string[]?", // Array can be undefined
nullableArray: "string[]|null", // Array can be null
arrayOfOptional: "string?[]", // Array of optional strings
});
```
## Literal Values
### Constant Values
```typescript
const LiteralSchema = Interface({
// String literals
type: "=user", // Must be exactly "user"
version: "=2.0", // Must be exactly "2.0"
// Number literals
apiVersion: "=1", // Must be exactly 1
maxRetries: "=3", // Must be exactly 3
// Boolean literals
isEnabled: "=true", // Must be exactly true
isLegacy: "=false", // Must be exactly false
// Complex literals
config: '={"theme":"dark"}', // Must be exact object
tags: '=["default","user"]', // Must be exact array
// Optional literals
optionalType: "=admin?", // Optional, but if present must be "admin"
});
```
## Custom Patterns
### Advanced Regex
```typescript
const AdvancedPatternSchema = Interface({
// Complex email pattern
corporateEmail: "string(/^[a-zA-Z0-9._%+-]+@company\\.com$/)",
// Strong password pattern
strongPassword:
"string(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]{8,}$/)",
// International phone
internationalPhone: "string(/^\\+[1-9]\\d{1,14}$/)",
// Social security number
ssn: "string(/^\\d{3}-\\d{2}-\\d{4}$/)",
// License plate (US format)
licensePlate: "string(/^[A-Z]{1,3}[0-9]{1,4}$/)",
// MAC address
macAddress: "string(/^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/)",
// IP address
ipv4: "ip", // or use a custom patterns
//JWT token validation
isJWT: "jwt", // or custom can be also use
//sem version
version: "semver" // custom could be use
//b64 test
isB64: "base64", // use custom if you want
//json test
isJson: "json",
//hexcolor validation
isHexC: "hexcolor", // use custom if want
// Credit card (Luhn algorithm not included)
creditCardNumber:
"string(/^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|3[47][0-9]{13})$/)",
});
```
## Special Types
### Any Type
```typescript
const AnyTypeSchema = Interface({
// Any type (use sparingly)
metadata: "any",
config: "any?",
// Better alternatives when possible
stringOrNumber: "string|number", // Prefer unions
jsonData: "json", // Prefer JSON validation
// Any in arrays
mixedArray: "any[]",
optionalMixed: "any[]?",
});
```
### Nested Objects
```typescript
const NestedSchema = Interface({
user: {
id: "uuid",
profile: {
name: "string",
contact: {
email: "email",
phone: "phone?",
address: {
street: "string",
city: "string",
country: "string(2,2)",
zipCode: "string(/^\\d{5}(-\\d{4})?$/)"
}?
}
}
},
// Optional nested objects
settings: {
theme: "light|dark",
notifications: {
email: "boolean",
push: "boolean"
}
}?
});
```
## 🎯 Best Practices
### Type Selection Guidelines
1. **Use specific types over generic ones**
```typescript
// ✅ Good
email: "email",
age: "int(0,120)",
// ❌ Avoid
email: "string",
age: "number"
```
2. **Prefer constraints over validation logic**
```typescript
// ✅ Good
username: "string(3,20)",
// ❌ Avoid (handle in business logic instead)
username: "string"
```
3. **Use unions for known values**
```typescript
// ✅ Good
status: "active|inactive|pending",
// ❌ Avoid
status: "string"
```
4. **Be explicit with optional fields**
```typescript
// ✅ Good - clear intent
nickname: "string?",
bio: "string(,500)?",
// ❌ Unclear
nickname: "string|undefined"
```
### Performance Tips
1. **Simple types are faster than complex regex**
2. **Built-in formats are optimized**
3. **Union types with fewer options perform better**
4. **Array constraints are validated efficiently**
## 🔗 Related Documentation
- **[Getting Started](./GETTING-STARTED.md)** - Basic usage and setup
- **[Conditional Validation](./CONDITIONAL-VALIDATION.md)** - Advanced business logic
- **[Examples Collection](./EXAMPLES.md)** - Real-world usage patterns
- **[Quick Reference](./QUICK-REFERENCE.md)** - Syntax cheat sheet