The component runs in the user's browser, so think of it as you would JavaScript: the user can read all of it and change any of it. Your server methods are the boundary.
Every server method is a public endpoint
Anyone can call any public #[Server] method, with any arguments, whatever your template does
with it. Treat each one as a controller action that takes user input:
- Check permission with
#[Server(can: 'ability')], a policy, or in the method. - Validate arguments with
#[Validate], or in the method. - Don't trust state. Server methods don't see the component's properties, by design. If a method needs a value, take it as an argument and check it.
#[Server(can: 'update-note')]
public static function rename(int $id, #[Validate('required|max:100')] string $title): void
{
Note::findOrFail($id)->update(['title' => $title]);
}
Gate::define('update-note', fn(User $user, int $id) => Note::find($id)?->user_id === $user->id);
Return only what the page may see
What a server method returns goes to the browser. Pick the columns:
return User::where('team_id', $teamId)->get(['id', 'name'])->toArray(); // not ->get()
What the browser receives
The browser's copy of a component (/unscripted/components/<name>.blade.php) is the file as
you wrote it, except:
- Public
#[Server]methods' bodies are replaced by stubs. Their default parameter values are copied into the stubs, though, so don't put secrets there. - Private and protected
#[Server]methods are removed entirely.
Everything else is sent as written: the browser methods, the template, use lines (which name
your classes), and #[Validate] rules. Keep secrets, keys and queries in server methods.
Middleware
Calls to POST /unscripted/call go through the web group: sessions, auth and CSRF. Add rate
limiting, or require login for every call, with the middleware option in the
config:
'middleware' => ['web', 'auth', 'throttle:60,1'],
Over the WebSocket transport, no middleware runs per message: authenticate through the socket's private channel, and rely on Reverb's rate limiting.
Escaping
{{ }} escapes; {!! !!}, and <?= ?> in a render() method, don't. Use {!! !!} only for
HTML you made yourself, and e() in <?= ?>.
Live channels are public
Live::changed() sends only a key, and each page then fetches through its
own checked call. Live::send() sends data on a public channel that anyone can listen to: send
only what anyone may see.