Filament action retries: client-side disabling versus server-side uniqueness constraints
0 reputation · 25 Sept 2020, 09:24 UTC
0 reputation · 25 Sept 2020, 09:24 UTC
Filament v3 form actions are Livewire server-side handlers that execute on every submission, so a user retrying a create or update action can trigger duplicate writes if the handler is not guarded. The framework does not provide a documented idempotency token or automatic deduplication for action invocations.
On the client side, disabling the action button during submission with Livewire's wire:loading.attr or Filament's disabled() method can reduce accidental double-clicks, though clicks arriving before the disabled attribute is applied may still pass through. On the server side, a unique database index rejects duplicate rows at the storage layer, but it does not surface a Filament-native retry message without custom validation or exception handling.
The unresolved decision is whether client-side disabling alone is sufficient for a low-concurrency admin panel, or whether server-side uniqueness constraints should be treated as mandatory regardless of the UI state. Version differences between Filament v2 and v3 in action lifecycle and Livewire integration add further uncertainty to which mitigation is reliable across upgrades.
Should a Filament v3 action rely on client-side disabling for retry protection, or must server-side uniqueness constraints be enforced independently of the UI? How should the trade-off be evaluated when the action performs an upsert rather than a simple insert? What verification steps confirm that repeated submissions do not duplicate writes under the chosen approach?
Client-side disabling alone is not sufficient for retry protection in Filament v3. Server-side uniqueness constraints must be enforced independently of the UI for both create and upsert actions. Client-side measures improve user experience but cannot guarantee idempotency under network retries, concurrent sessions, or requests already in flight.
wire:loading.attr="disabled" or Filament's disabled() method only applies after the first request leaves the browser. Clicks arriving before the attribute is applied, browser back-button resubmits, network-layer retries, and concurrent tabs all bypass client-side disabling.email, slug, composite natural key).For a simple insert, the failure mode is duplicate rows. For an upsert (e.g., updateOrCreate on a natural key), the failure mode is unintended overwrites — a retry may silently replace data written by a concurrent request. The server-side constraint on the natural key remains mandatory; you must also decide whether the upsert logic should be idempotent (re-applying the same input produces the same state) or whether a retry should be rejected as a conflict.
php artisan migrate.Rule::unique('table', 'column')->ignore($this->record?->id).Illuminate\Database\QueryException with error code 23000 (integrity constraint) and call Notification::make()->danger()->title('Duplicate entry')->send().Does the action perform any non-idempotent side effects (email sends, webhook calls, external API mutations) before the database write? If yes, you need an application-level idempotency key (stored in a separate table with a unique constraint) in addition to the database uniqueness constraint, because the DB index only protects the write, not the side effect.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.