PHP Middleware in Slim Framework — Complete Guide to PSR-15 Middleware Patterns
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
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