Generated Output
What Zodra generates from your Ruby type definitions
Generated Output
Zodra generates TypeScript types and Zod schemas from your Ruby definitions. Run the export task to produce the output files:
rails zodra:exportType → Zod schema
A Ruby type definition:
Zodra.type :product do
uuid :id
string :name, min: 1, max: 255
decimal :price, min: 0
string :currency, enum: %w[USD EUR GBP]
boolean :in_stock, default: true
array :tags, of: :string
timestamps
endGenerates:
import { z } from "zod";
export const ProductSchema = z.object({
id: z.uuid(),
name: z.string().min(1).max(255),
price: z.number().min(0),
currency: z.enum(["USD", "EUR", "GBP"]),
inStock: z.boolean().default(true),
tags: z.array(z.string()),
createdAt: z.iso.datetime(),
updatedAt: z.iso.datetime(),
});
export interface Product {
id: string;
name: string;
price: number;
currency: "USD" | "EUR" | "GBP";
inStock: boolean;
tags: string[];
createdAt: string;
updatedAt: string;
}Key format
Keys are transformed based on the key_format configuration:
# config/initializers/zodra.rb
Zodra.configure do |config|
config.key_format = :camel # created_at → createdAt (default)
endSee Configuration for all options.
Enum → Zod enum
Zodra.enum :order_status, values: %i[draft confirmed shipped]export const OrderStatusSchema = z.enum(["draft", "confirmed", "shipped"]);
export type OrderStatus = z.infer<typeof OrderStatusSchema>;Union → discriminated union
Zodra.union :payment_method, discriminator: :type do
variant :card do
string :last_four
string :brand
end
variant :bank_transfer do
string :bank_name
end
endexport const PaymentMethodSchema = z.discriminatedUnion("type", [
z.object({
type: z.literal("card"),
lastFour: z.string(),
brand: z.string(),
}),
z.object({
type: z.literal("bank_transfer"),
bankName: z.string(),
}),
]);Contract → client definitions
Contracts generate action definitions that the @zodra/client uses for typed API calls:
Each contract gets its own file with params schemas and the contract descriptor:
import { z } from 'zod';
import { ProductSchema } from '../types/product';
export const IndexProductsParamsSchema = z.object({});
export const ShowProductsParamsSchema = z.object({ id: z.uuid() });
export const CreateProductsParamsSchema = z.object({ ... });
export const ProductsContract = {
index: { method: 'GET' as const, path: '/products' as const, params: IndexProductsParamsSchema, response: ProductSchema, collection: true as const },
show: { method: 'GET' as const, path: '/products/:id' as const, params: ShowProductsParamsSchema, response: ProductSchema },
create: { method: 'POST' as const, path: '/products' as const, params: CreateProductsParamsSchema, response: ProductSchema },
} as const;The contracts barrel re-exports all contracts:
import { ProductsContract } from './products';
export const contracts = {
products: ProductsContract,
} as const;
export const baseUrl = '/api/v1';Output structure
Generated files are written to the configured output_path (default: app/javascript/types):
app/javascript/types/
├── types/
│ ├── index.ts # Re-exports all types
│ ├── product.ts # Zod schema + TypeScript interface
│ ├── order.ts # Imports from other type files
│ └── ...
├── contracts/
│ ├── index.ts # Contracts map + baseUrl
│ ├── products.ts # Params schemas + contract descriptor
│ └── ...
└── index.ts # Re-exports types/ and contracts/