Property Types
Routier provides a comprehensive set of property types for building robust schemas. Each type can be enhanced with modifiers to specify behavior and constraints.
Basic Types
String
import { s } from "@routier/core/schema";
// String property types within schemas
const userSchema = s.define("users", {
// Basic string
name: s.string(),
// String with literals
status: s.string("active", "inactive", "pending"),
// String with modifiers
email: s.string().distinct().optional(),
description: s.string().nullable().default(""),
readonlyField: s.string().readonly(),
// String with serialization
jsonData: s.string().serialize((obj) => JSON.stringify(obj))
.deserialize((str) => JSON.parse(str)),
}).compile();Number
import { s } from "@routier/core/schema";
// Number property types within schemas
const productSchema = s.define("products", {
// Basic number
age: s.number(),
// Number with literals
priority: s.number(1, 2, 3, 4, 5),
// Number with modifiers
score: s.number().default(0),
rating: s.number().nullable().optional(),
readonlyCount: s.number().readonly(),
// Number with serialization
price: s.number().serialize((num) => num.toString())
.deserialize((str) => parseFloat(str)),
}).compile();Boolean
import { s } from "@routier/core/schema";
// Boolean property types within schemas
const settingsSchema = s.define("settings", {
// Basic boolean
isActive: s.boolean(),
// Boolean with modifiers
isEnabled: s.boolean().default(true),
isOptional: s.boolean().optional().nullable(),
isReadonly: s.boolean().readonly(),
// Boolean with serialization
flag: s.boolean().serialize((bool) => bool ? "1" : "0")
.deserialize((str) => str === "1"),
}).compile();Date
import { s } from "@routier/core/schema";
// Date property types within schemas
const eventSchema = s.define("events", {
// Basic date
createdAt: s.date(),
// Date with modifiers
updatedAt: s.date().default(() => new Date()),
publishedAt: s.date().optional().nullable(),
readonlyDate: s.date().readonly(),
// Date with serialization
customDate: s.date().serialize((date) => date.toISOString())
.deserialize((str) => new Date(str)),
}).compile();Complex Types
Object
import { s } from "@routier/core/schema";
// Object property types within schemas
const companySchema = s.define("companies", {
// Basic object
address: s.object({
street: s.string(),
city: s.string(),
zipCode: s.string(),
}),
// Object with modifiers
metadata: s.object({
version: s.string(),
author: s.string(),
}).optional(),
config: s.object({
theme: s.string(),
language: s.string(),
}).nullable().default({}),
// Object with serialization
settings: s.object({
notifications: s.boolean(),
privacy: s.string(),
}).serialize((obj) => JSON.stringify(obj))
.deserialize((str) => JSON.parse(str)),
}).compile();Array
import { s } from "@routier/core/schema";
// Array property types within schemas
const postSchema = s.define("posts", {
// Basic array
tags: s.array(s.string()),
// Array with modifiers
items: s.array(s.string()).default([]),
optionalList: s.array(s.number()).optional(),
nullableArray: s.array(s.boolean()).nullable(),
// Array with serialization
serializedArray: s.array(s.string()).serialize((arr) => arr.join(","))
.deserialize((str) => str.split(",")),
// Complex array
users: s.array(s.object({
name: s.string(),
email: s.string(),
})),
}).compile();Type Constraints with Generics
Routier’s type system allows you to constrain properties to specific literal values using TypeScript generics:
String Literals
import { s } from "@routier/core/schema";
// String literals within schemas
const orderSchema = s.define("orders", {
status: s.string("pending", "approved", "rejected"),
role: s.string("admin", "user", "guest"),
theme: s.string("light", "dark", "auto"),
}).compile();Number Literals
import { s } from "@routier/core/schema";
// Number literals within schemas
const taskSchema = s.define("tasks", {
priority: s.number(1, 2, 3, 4, 5),
level: s.number(0, 1, 2, 3),
rating: s.number(1, 2, 3, 4, 5),
}).compile();Boolean Literals
import { s } from "@routier/core/schema";
// Boolean literals within schemas (boolean type doesn't need literals)
const configSchema = s.define("config", {
// Boolean is already constrained to true/false
isActive: s.boolean(),
isEnabled: s.boolean(),
isVisible: s.boolean(),
}).compile();Type Composition
Combining Types
import { s } from "@routier/core/schema";
// Type composition examples within schemas
const userSchema = s.define("users", {
// Combining different types
id: s.string().key().identity(),
name: s.string(),
age: s.number(),
isActive: s.boolean().default(true),
createdAt: s.date().default(() => new Date()),
tags: s.array(s.string()).default([]),
}).compile();
const postSchema = s.define("posts", {
// Nested objects with arrays
title: s.string(),
content: s.string(),
metadata: s.object({
views: s.number().default(0),
tags: s.array(s.string()),
}),
}).compile();Type Conversion
Converting to Arrays
Any type can be converted to an array using the .array() modifier:
import { s } from "@routier/core/schema";
// Converting types to arrays within schemas
const dataSchema = s.define("data", {
// String to array
stringArray: s.string().array(),
// Number to array
numberArray: s.number().array(),
// Boolean to array
booleanArray: s.boolean().array(),
// Date to array
dateArray: s.date().array(),
// Object to array
objectArray: s.object({
name: s.string(),
value: s.number(),
}).array(),
}).compile();Special Use Cases
Identity Properties
Properties that auto-generate values:
import { s } from "@routier/core/schema";
// Identity properties (auto-generate values) within schemas
const entitySchema = s.define("entities", {
// String identity
id: s.string().key().identity(),
// Number identity
userId: s.number().key().identity(),
// Date identity
timestamp: s.date().key().identity(),
}).compile();Key Properties
Properties that serve as unique identifiers:
import { s } from "@routier/core/schema";
// Key properties (unique identifiers) within schemas
const recordSchema = s.define("records", {
// String key
email: s.string().key(),
// Number key
userId: s.number().key(),
// Date key
timestamp: s.date().key(),
}).compile();Indexed Properties
Properties that create database indexes:
import { s } from "@routier/core/schema";
// Indexed properties (create database indexes) within schemas
const searchSchema = s.define("search", {
// Single index
email: s.string().index("email_idx"),
// Multiple indexes
category: s.string().index("category_idx", "search_idx"),
// Distinct index (unique)
username: s.string().distinct(),
// Indexed with other modifiers
status: s.string("active", "inactive").index("status_idx").default("active"),
}).compile();Best Practices
1. Use Literal Types for Constrained Values
import { s } from "@routier/core/schema";
// Use literal types for constrained values
const statusSchema = s.define("status", {
id: s.string().key().identity(),
// Use literals for constrained values
orderStatus: s.string("pending", "processing", "shipped", "delivered"),
userRole: s.string("admin", "user", "guest").default("user"),
priority: s.number(1, 2, 3, 4, 5).default(1),
// Instead of free-form strings
// ❌ Bad: s.string() // Allows any string
// ✅ Good: s.string("active", "inactive") // Constrains to specific values
}).compile();2. Leverage Type Inference
import { s, InferType, InferCreateType } from "@routier/core/schema";
// Leverage TypeScript type inference
const userSchema = s.define("users", {
id: s.string().key().identity(),
name: s.string(),
email: s.string().distinct(),
age: s.number().default(18),
isActive: s.boolean().default(true),
tags: s.array(s.string()).default([]),
}).compile();
// TypeScript automatically infers the correct types
type User = InferType<typeof userSchema>;
type CreateUser = InferCreateType<typeof userSchema>;
// Use inferred types for type safety
function processUser(user: User): string {
return `${user.name} (${user.email})`;
}
function createNewUser(userData: CreateUser): User {
// TypeScript ensures all required fields are provided
return userData as User;
}3. Use Appropriate Types
import { s } from "@routier/core/schema";
// Use appropriate types for the data
const productSchema = s.define("products", {
id: s.string().key().identity(),
// Use appropriate types for different data
name: s.string(), // Text data
price: s.number(), // Numeric data
isAvailable: s.boolean(), // True/false data
releaseDate: s.date(), // Date/time data
// Use arrays for collections
tags: s.array(s.string()),
reviews: s.array(s.object({
rating: s.number(1, 2, 3, 4, 5),
comment: s.string(),
})),
// Use objects for structured data
metadata: s.object({
weight: s.number(),
dimensions: s.string(),
}),
// Avoid inappropriate types
// ❌ Bad: s.string() for numeric IDs
// ✅ Good: s.number() for numeric IDs
// ❌ Bad: s.number() for text descriptions
// ✅ Good: s.string() for text descriptions
}).compile();4. Structure Complex Data
import { s } from "@routier/core/schema";
// Structure complex data with nested objects and arrays
const blogPostSchema = s.define("blogPosts", {
id: s.string().key().identity(),
title: s.string(),
content: s.string(),
// Structure complex nested data
author: s.object({
id: s.string(),
name: s.string(),
email: s.string(),
bio: s.string().optional(),
}),
// Use arrays for collections
tags: s.array(s.string()).default([]),
categories: s.array(s.string("tech", "business", "lifestyle")),
// Nested arrays and objects
comments: s.array(s.object({
id: s.string().identity(),
author: s.string(),
content: s.string(),
createdAt: s.date().default(() => new Date()),
replies: s.array(s.object({
id: s.string().identity(),
author: s.string(),
content: s.string(),
createdAt: s.date().default(() => new Date()),
})).default([]),
})).default([]),
// Metadata as structured object
metadata: s.object({
views: s.number().default(0),
likes: s.number().default(0),
publishedAt: s.date().optional(),
lastModified: s.date().default(() => new Date()),
}),
}).compile();Type Compatibility
Modifier Support
Different types support different modifiers:
| Modifier | String | Number | Boolean | Date | Object | Array |
|---|---|---|---|---|---|---|
.optional() | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
.nullable() | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
.default() | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
.readonly() | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ |
.deserialize() | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ |
.serialize() | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ |
.array() | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
.index() | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ |
.key() | ✅ | ✅ | ❌ | ✅ | ❌ | ❌ |
.identity() | ✅ | ✅ | ❌ | ✅ | ✅ | ❌ |
.distinct() | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ |
Summary of Types
- Array:
s.array(innerType) - Boolean:
s.boolean() - Date:
s.date() - Number:
s.number() - Object:
s.object({...}) - String:
s.string()
Next Steps
- Modifiers - Property modifiers and constraints
- Creating A Schema - Back to schema creation