Building RESTful APIs with Symfony
Building RESTful APIs with Symfony
Symfony is a powerful PHP framework that excels at building robust, scalable APIs. Let me share some best practices I’ve learned while building production APIs.
Why Symfony for APIs?
Symfony offers several advantages for API development:
- Mature ecosystem: Well-tested components
- Flexibility: Use only what you need
- Performance: Optimized for production workloads
- Developer experience: Excellent debugging tools
Project Setup
Starting a new Symfony API project is straightforward:
composer create-project symfony/skeleton my-api
cd my-api
composer require api
This installs the minimal dependencies needed for an API, including:
- API Platform (optional but powerful)
- Serializer component
- Validator component
Creating Your First Endpoint
Here’s a simple controller for a REST API:
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Routing\Annotation\Route;
class ProductController extends AbstractController
{
#[Route('/api/products', name: 'product_list', methods: ['GET'])]
public function list(): JsonResponse
{
$products = [
['id' => 1, 'name' => 'Product 1', 'price' => 29.99],
['id' => 2, 'name' => 'Product 2', 'price' => 39.99],
];
return $this->json($products);
}
}
Best Practices
1. Use DTOs (Data Transfer Objects)
Decouple your API responses from your entities:
class ProductDTO
{
public function __construct(
public readonly int $id,
public readonly string $name,
public readonly float $price
) {}
}
2. Validation
Always validate input data:
use Symfony\Component\Validator\Constraints as Assert;
class CreateProductRequest
{
#[Assert\NotBlank]
#[Assert\Length(min: 3, max: 255)]
public string $name;
#[Assert\NotBlank]
#[Assert\Positive]
public float $price;
}
3. Error Handling
Implement consistent error responses:
class ApiExceptionSubscriber implements EventSubscriberInterface
{
public function onKernelException(ExceptionEvent $event): void
{
$exception = $event->getThrowable();
$response = new JsonResponse([
'error' => $exception->getMessage(),
'code' => $exception->getCode(),
], 400);
$event->setResponse($response);
}
}
Testing
Symfony’s testing tools make API testing easy:
public function testProductList(): void
{
$client = static::createClient();
$client->request('GET', '/api/products');
$this->assertResponseIsSuccessful();
$this->assertResponseHeaderSame('content-type', 'application/json');
}
Next Steps
In future posts, I’ll cover:
- Authentication with JWT
- Rate limiting strategies
- API versioning
- Performance optimization with Doctrine
Happy coding!