
Building REST APIs with PHP using Slim Framework
Getting Started with Slim
To begin, you'll need to have PHP 7.4 or higher installed, along with Composer. Install Slim using Composer:
composer create-project slim/slim-skeleton my-apiNavigate into your project directory and start the built-in PHP server:
cd my-api
php -S localhost:8080 -t publicYou should now see the Slim welcome page at http://localhost:8080.
Project Structure and Dependencies
A Slim project typically includes the following key directories:
public/: Entry point for the application.src/: Contains your application logic, routes, and services.config/: Holds configuration files.dependencies.php: Sets up the dependency container.
Slim uses a dependency injection container powered by PHP-DI. This allows you to define and retrieve services in a clean and testable way.
Setting Up a Dependency Container
In dependencies.php, you can register services and middleware. For example, to register a database connection or logger:
$container->set('logger', function ($c) {
$settings = $c->get('settings')['logger'];
$logger = new Monolog\Logger($settings['name']);
$logger->pushHandler(new Monolog\Handler\StreamHandler($settings['path'], $settings['level']));
return $logger;
});Creating a REST API with Routes
Basic Route Definitions
In routes.php, define your API endpoints:
$app->get('/api/users', \App\Action\UserListAction::class)->setName('user-list');
$app->get('/api/users/{id}', \App\Action\UserGetAction::class)->setName('user-get');
$app->post('/api/users', \App\Action\UserCreateAction::class)->setName('user-create');
$app->put('/api/users/{id}', \App\Action\UserUpdateAction::class)->setName('user-update');
$app->delete('/api/users/{id}', \App\Action\UserDeleteAction::class)->setName('user-delete');Each route points to a class in the App\Action namespace, which should implement the corresponding HTTP method.
Example: UserGetAction
namespace App\Action;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Slim\Exception\HttpNotFoundException;
class UserGetAction
{
protected $userRepository;
public function __construct(UserRepository $userRepository)
{
$this->userRepository = $userRepository;
}
public function __invoke(Request $request, Response $response, array $args): Response
{
$id = $args['id'];
$user = $this->userRepository->findById($id);
if (!$user) {
throw new HttpNotFoundException($request, "User not found");
}
$response->getBody()->write(json_encode($user));
return $response->withHeader('Content-Type', 'application/json');
}
}Implementing Middleware
Middleware in Slim is used to process requests before they reach the route handler. For example, you can create a middleware to check for a valid API key:
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
class AuthMiddleware implements MiddlewareInterface
{
public function process(Request $request, RequestHandlerInterface $handler): Response
{
$apiKey = $request->getHeaderLine('X-API-KEY');
if ($apiKey !== 'your-secret-key') {
$response = new \Slim\Psr7\Response();
return $response->withStatus(401)->withJson(['error' => 'Unauthorized']);
}
return $handler->handle($request);
}
}Register the middleware in dependencies.php and apply it to routes:
$app->add(new AuthMiddleware());Or apply it to specific routes:
$app->get('/api/users/{id}', \App\Action\UserGetAction::class)
->add(new AuthMiddleware())
->setName('user-get');Best Practices for API Development
| Practice | Description |
|---|---|
| Use PSR-7 | Ensures compatibility with other middleware and services |
| Organize routes | Group related endpoints and use consistent naming |
| Return proper status codes | Use HTTP status codes like 200, 400, 404, 401, 500 for clear responses |
| Use JSON | Return data in JSON format with Content-Type: application/json |
| Validate input | Always validate and sanitize input data |
| Use middleware | Keep business logic separate from HTTP concerns |
Error Handling and Logging
Slim integrates with PSR-3 logging. You can log errors using the dependency container:
$this->container->get('logger')->error("User not found: $id");You can also define a global error handler to catch exceptions and return a consistent error response:
use Slim\Exception\HttpException;
$app->addErrorMiddleware(true, true, true);
$app->add(function ($request, $handler) {
try {
return $handler->handle($request);
} catch (HttpException $e) {
return $e->getResponse();
} catch (\Exception $e) {
return (new \Slim\Psr7\Response())
->withStatus(500)
->withJson(['error' => 'Internal Server Error']);
}
});