Route Groups

As an app grows, you'll often have several routes that share the same URL prefix — for example, every route under /api or every admin route under /admin. Slim lets you collect these related routes into a route group so the shared prefix (and any shared middleware) is written once instead of repeated on every route.

Creating a Group

Call $app->group() with the common prefix and a callback. Inside the callback you define routes on the $group object instead of $app — Slim automatically prepends the group's prefix to each one.

<?php
$app->group('/api', function ($group) {

    $group->get('/status', function ($request, $response) {
        $response->getBody()->write('OK');
        return $response;
    });

});

The route above is reachable at /api/status — the /api prefix came entirely from the group, so it never has to be typed again inside the callback.

Applying Middleware to a Whole Group

Just like a single route, a group can have middleware attached with ->add(). This runs the middleware for every route inside that group — a common use case is requiring authentication for all /admin routes without adding the check to each one individually.

<?php
$requireAdmin = function ($request, $handler) {
    // Check session, token, etc. before letting the request continue
    if (!isset($_SESSION['is_admin'])) {
        throw new \Slim\Exception\HttpForbiddenException($request);
    }
    return $handler->handle($request);
};

$app->group('/admin', function ($group) {
    $group->get('/dashboard', function ($request, $response) {
        $response->getBody()->write('Admin dashboard');
        return $response;
    });
})->add($requireAdmin);
Note: Every route defined inside that /admin group now runs through $requireAdmin first — there's no way to accidentally forget the check on a new admin route added later.

Worked Example: Grouping a REST Resource

Grouping shines when a single resource — like users — needs several HTTP methods (GET, POST, PUT, DELETE). Instead of repeating '/api/users' on every line, the prefix is declared once.

<?php
$app->group('/api/users', function ($group) {

    // GET /api/users - list all users
    $group->get('', function ($request, $response) {
        return $response;
    });

    // POST /api/users - create a user
    $group->post('', function ($request, $response) {
        return $response;
    });

    // PUT /api/users/{id} - update one user
    $group->put('/{id}', function ($request, $response, $args) {
        return $response;
    });

    // DELETE /api/users/{id} - remove one user
    $group->delete('/{id}', function ($request, $response, $args) {
        return $response;
    });

});

Four routes, one resource, one prefix declared a single time. Read together, this block reads almost like documentation of everything the /api/users endpoint can do.

Why Bother Grouping?

BenefitWhy it helps
Less repetitionThe shared prefix is written once instead of on every route
Shared middlewareAuth checks, logging, or rate limiting can apply to a whole section of the app at once
ReadabilityRelated endpoints are visually and logically grouped together in the code
Easier refactoringChanging a prefix (e.g. /api to /api/v1) means editing one line
Next up: Learn how to stop creating services with new everywhere and instead let a container hand them to you, in Dependency Injection.