Skip to content

PHP Middleware in Slim Framework — Complete Guide to PSR-15 Middleware Patterns

DodaTech Updated 2026-06-28 4 min read

In this tutorial, you will learn about PHP Middleware in Slim Framework. We cover key concepts, practical examples, and best practices to help you master this topic.

PHP Slim Framework implements PSR-15 middleware for HTTP request processing, supporting application-level, route-level, and group-level middleware with Dependency Injection integration.

What You'll Learn

By the end of this tutorial, you'll add middleware to Slim applications, create route-specific middleware, handle errors, integrate with PHP-DI, and build reusable middleware components.

Why Slim Middleware Matters

Slim's middleware architecture demonstrates PSR-15 in action. Understanding it helps you build modular, testable HTTP applications that work with any PSR-15 compatible framework.

Real-World Use

A Slim 4 API applies CORS middleware globally, JWT authentication on /api routes, Rate Limiting on /auth routes, and JSON body Parsing middleware so controllers receive structured request data.

Slim Middleware Path

flowchart LR
  A[PSR-15 Middleware] --> B[Slim Middleware]
  B --> C[PHP-DI Container]
  B --> D[Router]
  C --> E[Doctrine ORM]
  B --> F{You Are Here}
  style F fill:#f90,color:#fff

Slim 4 App Setup

Create a Slim application with middleware support.

<?php
use Slim\Factory\AppFactory;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
require_once "vendor/autoload.php";
$app = AppFactory::create();
$app->addRoutingMiddleware();
$app->addErrorMiddleware(true, true, true);
$app->get("/hello", function ($request, $response) {
    $response->getBody()->write("Hello World");
    return $response;
});
$app->run();

Application-Level Middleware

Middleware added with add() runs on every request.

<?php
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
class RequestLoggerMiddleware implements MiddlewareInterface {
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface {
        $start = microtime(true);
        $response = $handler->handle($request);
        $duration = (microtime(true) - $start) * 1000;
        error_log(sprintf("%s %s - %d (%dms)",
            $request->getMethod(),
            (string) $request->getUri(),
            $response->getStatusCode(),
            $duration
        ));
        return $response;
    }
}
$app->add(new RequestLoggerMiddleware());

Route-Specific Middleware

Apply middleware to specific routes using the add() method on routes.

<?php
use Slim\Routing\RouteCollectorProxy;
$app->post("/api/users", function ($request, $response) {
    $response->getBody()->write("User created");
    return $response;
})->add(new JsonBodyParserMiddleware());
$app->group("/admin", function (RouteCollectorProxy $group) {
    $group->get("/dashboard", function ($request, $response) {
        $response->getBody()->write("Admin Dashboard");
        return $response;
    });
    $group->get("/users", function ($request, $response) {
        $response->getBody()->write("User Management");
        return $response;
    });
})->add(new AdminAuthMiddleware());

Dependency Injection with PHP-DI

Integrate PHP-DI with Slim for controller dependency injection.

<?php
use DI\ContainerBuilder;
use Slim\Factory\AppFactory;
$containerBuilder = new ContainerBuilder();
$containerBuilder->addDefinitions([
    PDO::class => fn() => new PDO($_ENV["DB_DSN"], $_ENV["DB_USER"], $_ENV["DB_PASS"]),
]);
$container = $containerBuilder->build();
AppFactory::setContainer($container);
$app = AppFactory::create();
$app->get("/users/{id}", function ($request, $response, $args) {
    $pdo = $this->get(PDO::class);
    $stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
    $stmt->execute([$args["id"]]);
    $user = $stmt->fetch();
    $response->getBody()->write(json_encode($user));
    return $response->withHeader("Content-Type", "application/json");
});

Middleware with Container

Inject dependencies into middleware via the container.

<?php
class AuthMiddleware implements MiddlewareInterface {
    public function __construct(
        private JwtService $jwt,
        private UserRepository $users,
    ) {}
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface {
        $header = $request->getHeaderLine("Authorization");
        if (empty($header)) {
            $response = new \Slim\Psr7\Response(401);
            $response->getBody()->write("Unauthorized");
            return $response;
        }
        return $handler->handle($request);
    }
}

Common Mistakes

1. Adding Middleware After Routing

Middlewares added after routing are not applied. Add middleware before calling $app->run().

2. Forgetting Error Middleware

Without error middleware, exceptions return HTML instead of JSON. Always add error middleware.

3. Modifying Request in Middleware Without Returning New Instance

PSR-7 is immutable. Use $request->withAttribute() which returns a new instance.

4. Heavy Logic in Route-Level Middleware

Route-specific middleware should be lightweight. Heavy logic belongs in the controller or service layer.

5. Not Using Type-Hinted Middleware

Type-hint MiddlewareInterface to ensure compatibility with Slim's middleware dispatcher.

Practice Questions

1. How do you add global middleware in Slim 4?

$app->add(new MiddlewareClass()) before $app->run().

2. How do you add middleware to a specific route?

Attach ->add(new MiddlewareClass()) to the route definition.

3. What does addErrorMiddleware do?

Handles exceptions by returning appropriate HTTP responses with error details.

4. How do you integrate PHP-DI with Slim?

Create a container with ContainerBuilder and pass it to AppFactory::setContainer().

5. Challenge: Create a validation middleware for Slim 4.

<?php
class ValidationMiddleware implements MiddlewareInterface {
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface {
        if ($request->getMethod() === "POST") {
            $data = json_decode((string) $request->getBody(), true);
            $errors = [];
            if (empty($data["name"])) $errors[] = "Name is required";
            if (empty($data["email"])) $errors[] = "Email is required";
            if (!empty($errors)) {
                $response = new \Slim\Psr7\Response(400);
                $response->getBody()->write(json_encode(["errors" => $errors]));
                return $response->withHeader("Content-Type", "application/json");
            }
        }
        return $handler->handle($request);
    }
}

FAQ

What is the difference between Slim 3 and Slim 4 middleware?

Slim 3 uses the 'middleware' pattern with $next. Slim 4 uses PSR-15 RequestHandlerInterface.

Can I use Slim without middleware?

Yes, but middleware provides essential features like error handling, CORS, and body parsing.

How do I test middleware in Slim?

Create a request, process it through the middleware with a mock handler, and assert the response.

Is Slim compatible with other PSR-15 middleware?

Yes. Any PSR-15 middleware works with Slim 4 without modification.

What is the order of middleware execution?

Middleware added first runs first (outermost). Route-specific middleware runs after application middleware.

Mini Project: Slim API with JWT Auth Middleware

Build a Slim 4 API with JWT authentication middleware.

<?php
use Slim\Factory\AppFactory;
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
require_once "vendor/autoload.php";
$app = AppFactory::create();
$app->addErrorMiddleware(true, true, true);
$app->post("/login", function ($request, $response) {
    $data = json_decode((string) $request->getBody(), true);
    $token = JWT::encode(["sub" => 1, "role" => "admin"], "secret", "HS256");
    $response->getBody()->write(json_encode(["token" => $token]));
    return $response->withHeader("Content-Type", "application/json");
});
$app->get("/protected", function ($request, $response) {
    $response->getBody()->write("Protected data");
    return $response;
})->add(function ($request, $handler) {
    $auth = $request->getHeaderLine("Authorization");
    if (!str_starts_with($auth, "Bearer ")) {
        $res = new \Slim\Psr7\Response(401);
        $res->getBody()->write("Unauthorized");
        return $res;
    }
    return $handler->handle($request);
});
$app->run();

What's Next

PHP Router PHP DI Container PHP PSR-15 Middleware

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro