mediamonks/rest-api-bundle
Symfony bundle for quickly building REST APIs with annotation-driven routing, automatic request/response serialization, validation, and consistent error handling, cutting boilerplate for controllers and DTOs.
composer require mediamonks/rest-api-bundle. It is a small bundle (15 stars, 0 known dependents), so pin the version explicitly and read vendor/mediamonks/rest-api-bundle/composer.json before you start — check the Symfony version constraint and the PHP constraint against your app rather than trusting the README.config/bundles.php (Mediamonks\RestApiBundle\…) — the class name is visible in vendor/mediamonks/rest-api-bundle/src/, read the DependencyInjection/*Extension.php to see what services and config keys it actually registers before you guess at YAML.src/Controller (base controller / traits), src/Query or src/Request (query-string parsing), src/Response (problem/error payloads), and the Tests/ directory. With a bundle this small, the tests are the most reliable documentation of intended usage — they show the query params it understands and the shape of the response bodies.GET /articles — where the bundle handles the boring parts (parsing ?sort=, ?page=, ?filter=, producing a paginated collection envelope, and emitting RFC 7807-style errors). Get that working end-to-end before wiring it into anything write-heavy.A realistic expectation: this package is a thin, opinionated layer over Symfony's HTTP kernel and (probably) Doctrine. Its value is consistency across controllers, not magic. Treat it as reference implementation plus a small amount of glue you may end up owning.
Base controller inheritance. The dominant pattern in bundles like this is an abstract controller that exposes helpers for building responses and for reading the parsed query. Subclass it per resource (ArticlesController extends ApiController) rather than injecting the helpers into plain controllers — you get the exception-to-response mapping for free if it's wired as a kernel exception listener.
Query object as the boundary. The core abstraction is almost certainly a value object built from $request->query->all(). Use it as your only input to the repository layer:
public function list(Request $request): Response
{
$query = $this->getQuery($request); // check exact method name in src/
$qb = $this->articleRepository->createQueryBuilder('a');
// apply filters/sorts/pagination through the bundle's helpers
return $this->createCollectionResponse($qb->getQuery()->getResult(), $total);
}
The win is that every endpoint parses params identically. The cost is that the bundle owns your public API contract — parameter names, operators, and pagination shape become hard to change later.
Error handling. Emit structured errors (application/problem+json or the bundle's equivalent) for both validation failures and domain exceptions, and register the bundle's exception listener early enough that it wins over Symfony's default ErrorListener. Map Symfony Validator ConstraintViolationList into that same error shape so 400s look uniform across the API.
Serialization. Don't return entities directly. Use Symfony Serializer with groups (or JMS if that's what the bundle assumes) and keep the serialization config per-resource. This is also where you'll discover whether the bundle prescribes anything — if it doesn't, pick one and stay consistent.
Integration tip. Keep your Doctrine QueryBuilder mapping in a per-resource repository method (applyApiQuery()), not in the controller. Then the bundle's query object stays a transport concern and your persistence layer stays testable without an HTTP request.
text/html error pages instead of your problem+json shape. Check kernel.event_listener priority in the bundle's service definitions and override it in your own service config if needed.page, sort, filter, limit may already be used by your frontend or by API Platform if it's also installed. Two bundles both parsing the query string produces confusing results — run bin/console debug:event-dispatcher and debug:router to see who's listening and in what order.bin/console debug:container --tag=kernel.event_listener, debug:config <bundle_alias>, and the Symfony profiler's Request/Performance panels will tell you more than reading the source in isolation. Dump the parsed query object in a test rather than var_dump-ing inside a controller.Configuration.php — if it's absent or empty, there's nothing to tune, and any behavior change means overriding a service definition in services.yaml or using a compiler pass.How can I help you explore Laravel packages today?