Type Basics
Define object types with primitive fields, validations, and defaults
Type Basics
Types are the foundation of Zodra. Define them in app/types/ and they become the single source of truth for your backend and frontend.
Defining a type
Zodra.type :customer do
uuid :id
string :name
string :email
string? :phone
timestamps
endEach type is registered globally in the TypeRegistry and can be referenced by name from contracts, other types, and generated output.
Primitive types
| Method | Ruby type | Zod output |
|---|---|---|
string | String | z.string() |
integer | Integer | z.number().int() |
decimal | BigDecimal | z.number() |
number | Numeric | z.number() |
boolean | Boolean | z.boolean() |
datetime | DateTime | z.iso.datetime() |
date | Date | z.iso.date() |
uuid | String | z.uuid() |
binary | String | z.string() |
Optional fields
Append ? to any type method to make the field optional:
Zodra.type :profile do
string :name # required
string? :bio # optional
integer? :age # optional
endThis is equivalent to passing optional: true:
string :bio, optional: trueNullable fields
A nullable field accepts nil as a value but is still required in the payload:
Zodra.type :customer do
string :notes, nullable: true
endOptional and nullable are independent — a field can be both:
string? :notes, nullable: true # can be omitted OR set to nilValidations
min / max
For strings, validates length. For numbers, validates value:
string :name, min: 1, max: 255 # length between 1 and 255
integer :age, min: 0, max: 150 # value between 0 and 150
decimal :price, min: 0 # non-negativeformat
Adds format validation hint (used in generated TypeScript):
string :email, format: "email"enum
Restricts values to a fixed set:
string :status, enum: %w[draft published archived]
integer :priority, enum: [1, 2, 3]For reusable enums, see Enums and Unions.
default
Sets a default value:
boolean :published, default: false
string :role, default: "member"
integer :priority, default: 0Key aliasing
Use as: to override the generated key name for a field. The Ruby attribute name stays the same for serialization, but the exported TypeScript/Zod key uses the alias:
Zodra.type :product do
uuid :id
string :name
string :internal_code, as: "code"
endGenerates:
export const ProductSchema = z.object({
id: z.uuid(),
name: z.string(),
code: z.string(), // aliased from :internal_code
});This is useful when the Ruby attribute name differs from the desired API field name.
Timestamps
The timestamps shortcut adds created_at and updated_at datetime fields:
Zodra.type :post do
uuid :id
string :title
timestamps # adds created_at and updated_at
end