Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Api Bundle Laravel Package

happyr/api-bundle

HappyR API Bundle is a PHP bundle that provides a reusable foundation for building APIs, helping you organize endpoints and standardize responses so you can ship consistent, maintainable API services faster.

View on GitHub
Deep Wiki
Context7

Getting Started

Before installing, do a five-minute reality check: the project is a long-dormant internal bundle ("Api stuff for most Happyr projects") that has only recently received a patch tag (0.5.1), it has essentially no downstream users, and its API surface is designed around one company's applications rather than being framework-agnostic. The 0.5.1 patch is a one-line-ish behavior change to error mapping, not a signal of renewed maintenance. Treat it as a small, readable codebase you may need to fork, not as a dependency you can lean on for years.

  1. Inspect before committing. Open composer.json first and check the symfony/* and php constraints against your project. Then read src/ top to bottom — the bundle is small enough to finish in one sitting. The …Bundle class plus Resources/config/services.{yml,xml} are the true API surface; everything else is implementation detail.
  2. Install and register manually. composer require happyr/api-bundle, then add the bundle class to config/bundles.php (Symfony 4+) or AppKernel::registerBundles(). There is no Flex recipe, so expect no auto-generated config file and no default service wiring beyond what the bundle's own service definition provides.
  3. Look for the intended entry point. The first thing to search for is the abstract controller / response-building base class or the exception listener that turns exceptions into JSON. That is almost certainly the reason the bundle exists: producing consistent API error payloads without hand-rolling try/catch in every action. Note that as of 0.5.1 the listener has an explicit AccessDeniedHttpException → forbidden (403) branch, so security-driven denials are now part of that contract.
  4. First use case. Take one existing JSON endpoint returning ad-hoc arrays, convert it to extend the bundle's base controller (or throw the bundle's exception types), and confirm the error shape matches what your clients already expect. Add an access-denied case to that smoke test specifically — if your app already returns its own 403 payload from a security entry point, you will now have two competing formats and you should resolve that before migrating anything else. If the migration does not obviously save you code, stop.

Implementation Patterns

Base-controller inheritance. The typical day-to-day pattern is a thin ApiController in your app that extends the bundle's base class, adds your auth/serialization helpers, and is extended by concrete controllers. Do your own subclass rather than extending the vendor class directly in every controller — it gives you one place to add request-ID logging, CORS headers, or a version header, and one place to swap out the parent if you later drop the bundle.

Exception-to-response mapping. Wire your domain exceptions to the bundle's error handling so controllers stay free of response formatting. Prefer this over per-action try/catch; the payoff is a single, testable place where HTTP status codes and payload keys are decided. With 0.5.1, AccessDeniedHttpException is mapped to a forbidden response out of the box, which means denials thrown from voters, denyAccessUnlessGranted(), or #[IsGranted] inside a controller will land in the bundle's envelope rather than falling through to a generic 500 or the framework default. That is the good news; the caveat is that Symfony's security component also has its own entry point / access-denied handler chain, which fires before the kernel exception listener in some configurations. Decide explicitly which one owns the 403 payload rather than letting both exist. If the bundle's mapping is still too coarse, decorate its listener rather than modifying it (see Gotchas).

Serialization boundary. Keep the bundle responsible only for the HTTP envelope (status, error body, pagination metadata) and let Symfony's serializer or your own normalizers own the payload. Mixing the two is where these small internal bundles usually become hard to reason about.

Pagination for list endpoints. If the bundle ships pagination helpers, use them only if your data source is Doctrine ORM. For anything else (Elasticsearch, external APIs, cursors) you will fight the abstraction — return your own metadata object instead and keep the response shape identical.

Coexistence with API Platform / FOSRestBundle. Do not stack this bundle with API Platform. They both want to own exception handling and response shaping, and the conflict resolution will cost you more than either library saves — the new built-in 403 branch makes the overlap with API Platform's ExceptionListener and security integration worse, not better. If you are already on API Platform, you probably only want one or two classes from this package — copy them into your app under MIT terms rather than installing the bundle.

Testing. Write one functional test per error class asserting status code and JSON keys, and one per list endpoint asserting pagination keys. Add an explicit test asserting that an access-denied path yields 403 with your expected body — that is now behavior you depend on, and it is behavior a patch release just changed. Because the bundle is otherwise unmaintained, these tests are your regression net for a future Symfony upgrade where the listener hooks may silently stop firing.

Gotchas and Tips

  • Maintenance risk is still the headline. One star, no downstream users, a codebase frozen around 2018 with a single recent patch tag. There is no upstream fixing Symfony 5/6/7 deprecations for you. Pin an exact version, and budget for vendoring the few files you actually use (MIT makes this trivial) instead of carrying the dependency.
  • 0.5.1 is a behavior change shipped as a patch. The new AccessDeniedHttpException → 403 forbidden response means that if you were previously relying on access-denied cases producing a different status code, a different payload key, or your own security entry point's body, upgrading from 0.5.0 will silently change your API contract. Semver discipline here is loose — assume any future tag can alter payload shape. Diff the listener before every bump and re-run your 403 test.
  • Two owners for 403. Symfony's AccessDeniedHandlerInterface / firewall entry point and the bundle's listener can both try to answer the same denial. The entry point handles denials raised in the firewall/voter layer before the controller runs; the listener handles ones that escape into the kernel. In practice you can end up with the entry point's response for access_control denials and the bundle's envelope for in-controller denials — two shapes for the same 403. Pick one and configure the other away.
  • Symfony version drift is the most likely failure. Watch specifically for ContainerInterface::get() usage, EventDispatcherInterface signature changes, and any services.yml using deprecated tags or %parameter% forms that no longer resolve. These fail as container compile errors, not runtime exceptions, so they surface at cache warmup.
  • No recipe means no defaults. You will get no config/packages/happyr_api.yaml. Any configuration must be written by hand from the bundle's own Configuration class — read it to learn the real option names rather than guessing from the README, which may be stale relative to the code.
  • Debugging service wiring: bin/console debug:container --tag=<tag> to see what the bundle registered, and bin/console debug:event-dispatcher to confirm the exception listener is actually attached. A silent listener is the most common "why is my error format wrong" cause. For 403-specific problems, also check whether a security entry point is answering first — the event dispatcher will show the listener registered even when it never gets called.
  • Extension points, in order of safety: (1) override container parameters/arguments via your app config; (2) alias a bundle service ID to your own class; (3) decorate the exception listener with decorates:; (4) fork. Avoid subclassing vendor services and modifying them — you lose the upgrade path entirely, even if that path is currently theoretical. If you need a 403 body different from what 0.5.1 produces, decorate the listener or override the response factory rather than editing vendor code.
  • Don't let it leak into your domain layer. Keep the bundle's classes confined to the HTTP/controller boundary. If its exception types or base classes start appearing in services or entities, you have made an abandoned internal package load-bearing in your architecture, which is exactly the coupling you want to avoid here.
  • If you are not a Happyr project, the honest recommendation is: read it for ideas, copy the parts you need, skip the dependency. The patterns (base controller, consistent error envelope, an access-denied → 403 branch, pagination metadata) are worth ten minutes of study; the package itself is not worth a long-term support commitment.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource