Eager loading a relationship with with() avoids the N+1 query problem, but by default it fetches every column of the related model — selecting only the specific columns actually needed is a genuine performance improvement, with one easy-to-overlook requirement.
Basic eager loading, fetching every column
$posts = Post::with('author')->get();
Eager loading with specific columns only
$posts = Post::with('author:id,name,email')->get();
Listing specific columns after a colon restricts the eager-loaded relationship to only those fields — genuinely useful when the related model has many columns (a large users table with dozens of fields) but only a couple are actually needed for the current view.
The essential requirement: always include the foreign key
// WRONG — missing the foreign key needed to actually match the relationship
$posts = Post::with('author:name,email')->get();
// CORRECT — id is the primary key the relationship matches against
$posts = Post::with('author:id,name,email')->get();
Eloquent needs the related model's key column (typically id) present in the selected columns to actually associate each loaded author back with the correct post — omitting it is a common mistake that results in every post either failing to show its author or showing incorrect data.
Restricting columns on the base query too
$posts = Post::select('id', 'title', 'author_id')
->with('author:id,name')
->get();
Similarly, the base query's own select() needs to include author_id (the foreign key on the posts table itself) — without it, Eloquent has no foreign key value on the parent model to match against the eager-loaded related records at all.
Restricting columns on a nested (multi-level) relationship
$posts = Post::with('author:id,name,team_id', 'author.team:id,name')->get();
Each level of a nested eager-load chain needs its own explicit column restriction and its own foreign key included — restricting only the top-level relationship's columns while leaving a nested one unrestricted still fetches every column at that deeper level.
Combining with a closure for more control (like ordering)
$posts = Post::with(['comments' => function ($query) {
$query->select('id', 'post_id', 'body', 'created_at')
->orderBy('created_at', 'desc')
->limit(5);
}])->get();
The closure form of with() is necessary once the eager load needs more than just column restriction — ordering, limiting, or additional where conditions on the related query all require this closure syntax rather than the simple colon-separated column list.
Why this optimization matters most on wide tables
The performance benefit of restricting eager-loaded columns scales with how many columns the related table actually has and how many rows are being loaded — for a small, narrow lookup table, the difference is negligible, but for a wide users table (with many profile fields, preferences, and metadata columns) being eager-loaded across a large list of posts, this optimization can measurably reduce both query time and memory usage.