arnaldo-tomo / laravel-autoscema
Automatically generate TypeScript types and validation schemas from Laravel Models with zero configuration
Requires
- php: ^8.1
- illuminate/console: >=9.0 <21.0
- illuminate/contracts: >=9.0 <21.0
- illuminate/database: >=9.0 <21.0
- illuminate/support: >=9.0 <21.0
- nikic/php-parser: ^4.15 || ^5.0
- symfony/finder: ^6.0|^7.0
Requires (Dev)
- laravel/pint: ^1.0
- orchestra/testbench: ^7.0 || ^8.0 || ^9.0 || ^10.0 || ^11.0 || ^12.0 || ^13.0
- pestphp/pest: ^2.0
- pestphp/pest-plugin-laravel: ^2.0
- phpunit/phpunit: ^10.0
README
๐ Automatically generate TypeScript types and validation schemas from Laravel Models with zero configuration.
โจ Features
- ๐ Zero Configuration - Works out of the box with intelligent defaults
- ๐ TypeScript Types - Generate perfect TypeScript interfaces from Eloquent models
- ๐ Relationships - Automatically handle Eloquent relationships
- โ Validation Schemas - Generate Zod/Yup schemas from Form Requests
- ๐ API Client - Generate typed API clients for your endpoints
- ๐๏ธ File Watcher - Real-time regeneration when models change
- ๐ฏ Framework Integration - Perfect for Inertia.js, React, Vue, and more
๐ Quick Start
Installation
composer require arnaldo-tomo/laravel-autoscema
Initialize
php artisan schema:init
Generate Types
# Generate for specific models
php artisan schema:generate --model=User --model=Post
That's it! Your TypeScript types are ready in resources/js/types/
.
๐ Example
Laravel Model
<?php namespace App\Models; use Illuminate\Database\Eloquent\Model; class User extends Model { protected $fillable = ['name', 'email', 'age']; protected $casts = [ 'email_verified_at' => 'datetime', 'preferences' => 'json', ]; public function posts() { return $this->hasMany(Post::class); } }
Generated TypeScript
// Generated at 2025-01-20 10:30:00 by Laravel AutoSchema export interface User { id: number; name: string; email: string; age: number; preferences: Record<string, any> | null; email_verified_at: Date | null; created_at: Date; updated_at: Date; // Relationships posts?: Post[]; } export type UserType = User;
Form Request Integration
<?php namespace App\Http\Requests; use Illuminate\Foundation\Http\FormRequest; class CreateUserRequest extends FormRequest { public function rules() { return [ 'name' => 'required|string|max:255', 'email' => 'required|email|unique:users', 'age' => 'nullable|integer|min:18', ]; } }
Generated Validation Schema
// Generated Zod schema export const createUserSchema = z.object({ name: z.string().max(255), email: z.string().email(), age: z.number().min(18).nullable(), }); export type CreateUserInput = z.infer<typeof createUserSchema>;
๐ ๏ธ Commands
Generate Types
# Generate all types # Generate for specific models php artisan schema:generate --model=User --model=Post # Preview without writing files php artisan schema:generate --dry-run # Force overwrite existing files php artisan schema:generate --force
File Watcher
# Watch for changes and regenerate automatically php artisan schema:watch # Watch with custom interval php artisan schema:watch --interval=5 # Watch in quiet mode php artisan schema:watch --quiet
โ๏ธ Configuration
Publish the configuration file:
php artisan vendor:publish --provider="ArnaldoTomo\LaravelAutoSchema\AutoSchemaServiceProvider" --tag="autoscema-config"
Configuration Options
<?php return [ 'output' => [ 'path' => resource_path('js/types'), 'filename_case' => 'pascal', // pascal, camel, snake, kebab 'export_format' => 'named', // named, default, namespace ], 'models' => [ 'directories' => [ app_path('Models'), ], 'include_relationships' => true, 'include_accessors' => true, ], 'validation' => [ 'enabled' => true, 'schema_format' => 'zod', // zod, yup, joi 'include_form_requests' => true, ], 'api' => [ 'generate_client' => true, 'authentication' => 'sanctum', // sanctum, passport, none ], ];
๐ง Advanced Usage
API Client Generation
When enabled, AutoSchema generates a complete API client:
// Generated API client import { userApi } from './types/api-client'; // Usage const users = await userApi.getAll(); const user = await userApi.getById(1); const newUser = await userApi.create({ name: 'John', email: 'john@example.com' });
Custom Types
Handle complex relationships and custom types:
class Product extends Model { protected $casts = [ 'metadata' => 'json', 'status' => ProductStatus::class, // Enum ]; public function variants() { return $this->hasMany(ProductVariant::class); } }
Generated TypeScript:
export interface Product { id: number; name: string; metadata: Record<string, any> | null; status: ProductStatus; // Relationships variants?: ProductVariant[]; } export enum ProductStatus { ACTIVE = 'active', INACTIVE = 'inactive', DRAFT = 'draft', }
Integration with Frontend Frameworks
React + TypeScript
import { User } from './types'; interface UserCardProps { user: User; // Fully typed! } const UserCard: React.FC<UserCardProps> = ({ user }) => { return ( <div> <h3>{user.name}</h3> <p>{user.email}</p> {user.posts && ( <span>{user.posts.length} posts</span> )} </div> ); };
Vue + TypeScript
<template> <div> <h3>{{ user.name }}</h3> <p>{{ user.email }}</p> </div> </template> <script setup lang="ts"> import type { User } from './types'; defineProps<{ user: User; }>(); </script>
Inertia.js
import { User } from './types'; interface Props { users: User[]; } const UsersIndex: React.FC<Props> = ({ users }) => { // users is fully typed! };
๐ Workflow Integration
GitHub Actions
name: Generate Types on: push: paths: - 'app/Models/**' - 'database/migrations/**' - 'app/Http/Requests/**' jobs: generate-types: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Setup PHP uses: shivammathur/setup-php@v2 with: php-version: '8.2' - name: Install dependencies run: composer install - name: Generate TypeScript types run: php artisan schema:generate - name: Commit generated types run: | git config --local user.email "action@github.com" git config --local user.name "GitHub Action" git add resources/js/types/ git commit -m "Update TypeScript types" || exit 0 git push
Pre-commit Hook
#!/bin/sh # .git/hooks/pre-commit # Check if any model files changed if git diff --cached --name-only | grep -q "app/Models\|database/migrations\|app/Http/Requests"; then echo "๐ Regenerating TypeScript types..." php artisan schema:generate --force # Add generated files to commit git add resources/js/types/ echo "โ TypeScript types updated" fi
๐ฏ Use Cases
E-commerce Platform
class Product extends Model { protected $fillable = ['name', 'price', 'description']; protected $casts = [ 'price' => 'decimal:2', 'metadata' => 'json', 'status' => ProductStatus::class, ]; public function category() { return $this->belongsTo(Category::class); } public function variants() { return $this->hasMany(ProductVariant::class); } }
Generated types enable perfect frontend integration:
// Product listing with full type safety const ProductCard: React.FC<{ product: Product }> = ({ product }) => { return ( <div className="product-card"> <h3>{product.name}</h3> <p className="price">${product.price}</p> <p className="category">{product.category?.name}</p> <div className="variants"> {product.variants?.map(variant => ( <span key={variant.id}>{variant.name}</span> ))} </div> </div> ); };
API Integration
// Fully typed API calls const products = await productApi.getAll({ category: 'electronics', status: ProductStatus.ACTIVE }); const product = await productApi.getById(1); const newProduct = await productApi.create({ name: 'New Product', price: 99.99, description: 'Amazing product' });
Form Validation
// Form with automatic validation const ProductForm: React.FC = () => { const { register, handleSubmit, formState: { errors } } = useForm<CreateProductInput>({ resolver: zodResolver(createProductSchema) }); const onSubmit = async (data: CreateProductInput) => { // data is fully typed and validated await productApi.create(data); }; return ( <form onSubmit={handleSubmit(onSubmit)}> <input {...register('name')} placeholder="Product name" /> {errors.name && <span>{errors.name.message}</span>} <input {...register('price', { valueAsNumber: true })} /> {errors.price && <span>{errors.price.message}</span>} </form> ); };
๐ง Troubleshooting
Common Issues
Types not generating?
- Check if models extend
Illuminate\Database\Eloquent\Model
- Ensure output directory is writable
- Run with
--dry-run
to see what would be generated
php artisan schema:generate --dry-run
Relationships not showing?
- Ensure relationships return
Illuminate\Database\Eloquent\Relations\Relation
- Check if
include_relationships
is enabled in config - Verify relationship methods are public
Custom types not working?
- Add custom type mappings in configuration
- Use
--force
to overwrite existing files - Check Laravel logs for errors
Debug Mode
# Enable verbose output php artisan schema:generate -v # Show detailed analysis php artisan schema:generate --dry-run -v
๐งช Testing
# Run package tests composer test # Run with coverage composer test-coverage # Run code formatting composer format
๐ค Contributing
We welcome contributions! Please see CONTRIBUTING.md for guidelines.
Development Setup
# Clone repository git clone https://github.com/arnaldo-tomo/laravel-autoscema.git cd laravel-autoscema # Install dependencies composer install # Run tests composer test # Format code composer format
๐ Changelog
Please see CHANGELOG.md for recent changes.
๐ก๏ธ Security
If you discover any security-related issues, please email me@arnaldotomo.dev instead of using the issue tracker.
๐ License
Laravel AutoSchema is open-sourced software licensed under the MIT license.
๐ Credits
- Arnaldo Tomo - Creator and maintainer
- All Contributors
๐ Support
- โญ Star the project on GitHub
- ๐ Report issues on GitHub Issues
- ๐ก Request features on GitHub Discussions
- ๐ Read the documentation
Made with โค๏ธ by Arnaldo Tomo in Mozambique ๐ฒ๐ฟ