A group applies shared attributes to every route defined inside its closure: middleware(), controller(), prefix(), name(), domain(), scopeBindings() and where(). The bookshop's catalog pages change rarely, so one group wraps them in the built-in cache.headers middleware:
Route::middleware('cache.headers:public;max_age=300;etag')->group(function () {
Route::get('/categories', fn () => Category::orderBy('name')->get());
Route::get('/bestsellers', fn () => Product::orderByDesc('price')->limit(2)->get());
});$ curl -i http://127.0.0.1:8303/categories HTTP/1.1 200 OK Cache-Control: max-age=300, public Content-Type: application/json ETag: "0a4534ae7e76d2fe9e41f01fb4d584b6" ...
Route::controller(OrderController::class)->group(...) lets each route inside name only its method, as in Route::get('/orders/{id}', 'show'). Groups nest, and the merge rules are worth memorizing: middleware and where conditions accumulate, while prefixes and name prefixes are appended, so a name('admin.') group inside a name('shop.') group produces shop.admin.orders.index. Middleware is collected outer group first, then inner, then the route's own, before the priority list in Middleware Aliases and Sorting reorders the ones it knows.