vagebond/runtype
Runtype brings lightweight runtime type checking to PHP and Laravel. Define expected types and validate values as your app runs, catching mismatches early and making data flow safer and clearer without relying solely on static analysis.
Architecture fit. runtype targets a specific architectural seam: the boundary between Laravel's JsonResource transformation layer and a TypeScript-consuming front end (Inertia/React/Vue/Next). Rather than introspecting Eloquent schemas directly (which misrepresents the API surface, since Resources often reshape, rename, paginate, and conditionally include fields), a Resource-driven generator aligns with how Laravel teams already think. This is the correct leverage point and fits cleanly into a typical Laravel monolith + SPA stack without requiring API schema-first tooling (OpenAPI, GraphQL) or a contract registry.
Integration feasibility. Mechanically low-friction: a Composer dev dependency, a generation command wired into an Artisan target or CI step, and a typed output artifact committed to the front-end source tree (or emitted during a build). The cost of a spike is measured in hours, not days. The real feasibility question is whether the generator's fidelity matches your Resource usage: nested resources, whenLoaded/when() conditionals, additional(), mergeWhen(), paginated collections, and enum casts are the usual places where these tools diverge from runtime shape.
Technical risk. Materially elevated for production adoption:
Resource idioms.2026-04-03 release suggests active iteration, but also churn.Key questions.
whenLoaded, mergeWhen, conditional additional(), and resource collections with fidelity to the actual JSON payload?Carbon/DateTimeInterface, and money/decimal casts mapped?--dry-run/diff gate) to prevent drifted types from merging?Stack fit. Strong fit where the stack is Laravel (+ Sanctum/Breeze/Jetstream) with an Inertia or SPA front end backed by JsonResource classes. Poor fit if the API is not Resource-shaped (controllers returning raw models/arrays), if the consumer is not TypeScript, or if the organization has standardized on OpenAPI/GraphQL contracts — introducing a second contract source creates two truths.
Migration path. Introduce incrementally, not big bang:
UserResource, InvoiceResource) behind a feature flag, generating into a types/generated/ directory.Compatibility. Validate against your exact matrix before committing: Laravel major (9/10/11/12), PHP version, and the TS target (strict mode, exactOptionalPropertyTypes, satisfies). Because the package emits .d.ts/.ts, a TS strictness bump in the front end is a co-dependency to watch.
Sequencing.
generate --check) so drift breaks the build rather than sneaking in; add a runtime contract test on one endpoint..gitattributes marking it generated), and define the upgrade runbook.Maintenance. Low day-to-day cost if the CI gate is in place; the risk is maintenance debt on the package itself. Budget an explicit contingency: if upstream goes stale, you either pin to a fork or replace with a homegrown generator (a bounded, ~1–2 week effort given the narrow scope). Treat the generated output as derived, never hand-edited — enforce via header comment and codeowners.
Support. Effectively community-only. There is no vendor SLA; the issue tracker is the support channel. For an internal TPM, that means the team owns triage. Escalation path is "fork and patch," which is realistic here because the surface area is small.
Scaling. Generation cost scales with number of Resources and is trivially parallel/cheap; the real scaling constraint is organizational: as teams add Resources, the generated namespace becomes a shared artifact. Without conventions (naming, folder layout, ownership), front-end teams will start patching generated types in place. Establish a lint rule forbidding imports from generated/ outside approved boundaries if that becomes a problem.
Failure modes to instrument.
--check.any/unknown for nested structures) degrading DX — caught during pilot review.Ramp-up. For a PHP-strong team, near-zero: it's an Artisan command. For a front-end team consuming the output, the learning is in understanding why a field is optional (conditional inclusion) rather than how to use the tool. Budget half a day of onboarding per team plus a short written convention doc. The bigger ramp-up cost is cultural: getting engineers to stop hand-maintaining interfaces and trust the generator.
Recommendation. Worth a bounded pilot in a Laravel + TypeScript codebase given the low integration cost and clear productivity upside. Do not promote to a load-bearing contract without (a) the CI drift gate, (b) at least one runtime payload validation, and (c) a named owner and a documented fork/replace contingency. The package's narrow scope makes it low-risk to trial and cheap to reverse — which is, for a TPM, the decisive factor at this maturity level.
How can I help you explore Laravel packages today?