Export Types
Rhino includes an Artisan command that generates TypeScript interfaces from your registered models. It introspects the database schema, builds an OpenAPI 3.0 spec internally, and runs openapi-typescript to produce a .d.ts file.
Requirements
The command runs npx openapi-typescript under the hood, so Node.js must be available on the machine where you run the command.
Usage
php artisan rhino:export-types
This introspects all registered models from config/rhino.php, reads their database columns, and writes TypeScript interfaces to the configured output paths.
Command Flags
| Flag | Default | Description |
|---|---|---|
--output | (none) | Explicit output file path. Overrides config/env paths. |
Examples
# Write to configured paths (RHINO_CLIENT_PATH / RHINO_MOBILE_PATH)
php artisan rhino:export-types
# Write to a specific file
php artisan rhino:export-types --output=resources/js/types/rhino.d.ts
Configuration
Set the output paths in your .env file:
RHINO_CLIENT_PATH=../client
RHINO_MOBILE_PATH=../mobile
Or in config/rhino.php:
return [
'client_path' => env('RHINO_CLIENT_PATH'),
'mobile_path' => env('RHINO_MOBILE_PATH'),
// ...
];
When both RHINO_CLIENT_PATH and RHINO_MOBILE_PATH are set, the command writes to both:
{RHINO_CLIENT_PATH}/src/types/rhino.d.ts{RHINO_MOBILE_PATH}/src/types/rhino.d.ts
The --output flag overrides both and writes to a single explicit path.
Generated Output
The command generates one interface per model. All fields are optional because Rhino's policy system controls which attributes are visible per user/role at runtime.
export interface Post {
id?: number;
title?: string;
content?: string;
is_published?: boolean;
blog_id?: number;
created_at?: string;
updated_at?: string;
deleted_at?: string | null;
}
export interface Blog {
id?: number;
name?: string;
created_at?: string;
updated_at?: string;
}
Type Mapping
| Database Column Type | TypeScript Type |
|---|---|
integer, bigint, smallint, tinyint | number |
decimal, float, double | number |
boolean | boolean |
timestamp, datetime, date | string |
json, jsonb | Record<string, unknown> |
text, varchar, string | string |
| Nullable columns | adds | null |
Using Generated Types
Import the generated types and pass them as generics to Rhino hooks:
import type { Post } from '../types/rhino';
import { useModelIndex, useModelStore, useModelUpdate } from '@rhino-dev/rhino-react';
// Typed list — data.data is Post[]
const { data } = useModelIndex<Post>('posts', { sort: '-created_at' });
// Typed create — mutate accepts Partial<Post>
const store = useModelStore<Post>('posts');
store.mutate({ title: 'Hello', content: 'World' });
// Typed update — mutate accepts { id, data: Partial<Post> }
const update = useModelUpdate<Post>('posts');
update.mutate({ id: 1, data: { title: 'Updated' } });
Rhino's policy system (permittedAttributesForShow, hiddenAttributesForShow) dynamically controls which fields appear in API responses based on the authenticated user's role and custom logic. The generated types represent the union of all possible fields, not a guarantee of which fields will be present in any given response.
Workflow
- Define your models and run migrations
- Run
php artisan rhino:export-types - Import the generated types in your React/React Native code
- Re-run the command whenever you change your database schema