Letting a client choose which fields it wants is a good feature and moves the eager-loading decision from the controller to the request.
// GET /orders?fields=id,total,customer.name
$relations = collect($request->fields())
->filter(fn (string $f) => str_contains($f, '.'))
->map(fn (string $f) => Str::before($f, '.'))
->unique()
->intersect(self::ALLOWED_RELATIONS)
->all();
Order::with($relations)->paginate();
Without deriving the eager loads from the requested fields, a controller either loads everything — wasteful for the common case — or loads a fixed set and produces an N+1 whenever a client asks for something else. Intersecting against an allowed list is not optional: a relation name taken from a query parameter is otherwise an arbitrary method call. Query-count assertions per endpoint should cover the field combinations clients actually use, which means logging them.