micro-module/rest-bundle
A REST bundle for micro-module Laravel applications, providing a structured way to define, organize, and expose RESTful API endpoints across self-contained modules with consistent routing and response handling.
Reality check before anything else. Zero stars, zero dependents, no docs. Read composer.json first — the -bundle suffix, the micro_modules_rest_bundle package name, and the Composer vendor namespace (micro-module/rest-bundle) point hard at Symfony bundle conventions, not a Laravel package. Check for symfony/http-kernel / symfony/http-foundation in require, and whether there is any laravel/framework or illuminate/* constraint at all. If it's Symfony-only, the rest of this section is an audit spike, not an onboarding doc — you are evaluating a source you may fork, not installing a dependency. Budget 3–5 days, timeboxed, and write down go/no-go before touching a real app.
composer.json and the license. Pin constraints for php, symfony/*, psr/*. Check the license permits internal forking and redistribution — that's non-negotiable before any code enters your repo.composer install && vendor/bin/phpunit. If tests exist and pass, they are your only behavioral spec. If they don't, every edge-case assumption you make is unverified — plan to write that suite yourself.First use case: one endpoint, nothing else. Not a whole API surface. Register a single module with a single GET route that returns JSON, hit it with curl, and log the full request/response cycle — status codes, error body shape, content negotiation, and what happens on a 404 vs a thrown exception. You want to see the envelope the package gives you and what it costs you to fit it inside your domain code before that code has any dependents.
If you're on Laravel specifically: stop and decide between reference-only (steal the conventions, implement natively — cheapest, no dependency) and fork-and-extract (vendor the repo, isolate the framework-agnostic services — serializers, route descriptors, module registry — and wrap them in a MicroModuleRestServiceProvider). Direct dependency is only viable if the core is genuinely PSR-only. Verify with composer why-not laravel/framework early; Symfony bundles routinely pin symfony/* ranges that collide transitively.
Pin to a commit SHA. Always. With zero dependents, tags can be rewritten and branches force-pushed and nobody will notice. Require an exact version or SHA, or better, vendor it into your own VCS. If you only need three classes, copy them into app/Support/Rest/ and own them outright — the amount of code in a package like this is usually small enough that ownership is cheaper than a dependency.
Wrap, don't inherit. Do not make your controllers extend the bundle's base controller, and do not type-hint the bundle's response/exception classes in your domain code. Put one thin adapter layer between you and the package — a JsonResponseFactory, a ModuleRegistry, an exception normalizer — so the package can be swapped or replaced without a sweep across the app. This is the single highest-leverage pattern here; it is also what makes the "fork-and-extract" path survivable, because the adapter is your contract and the package is an implementation.
One module per bounded context; routes registered through the loader only. Resist the temptation to mix the package's route registration with your normal routes/api.php. Route registration is a single source of truth per module, or you get two competing route tables and confusing resolution order. If you're on Laravel and adapted via a service provider, register everything through Route::group() with an explicit prefix policy (e.g. api/{version}/{module}) so you can reason about collisions.
Response envelope as a single chokepoint. If the package gives you a uniform JSON response, that's the only thing worth keeping and the only thing you should let it own. Define the envelope once (success, data, meta, errors), and route all responses through it — including Laravel's validation failures and auth failures. The moment you have two envelope shapes on the API, the abstraction has failed and you've made the client's life worse than a hand-rolled response.
Centralize exception handling, and test it. The exception handler is the second thing worth keeping. Map your thrown exceptions to consistent status codes and bodies at one point, and write integration tests for the mapping (validation → 422, not-found → 404, auth → 401/403, unhandled → 500 with no stack trace leaked). This is where "consistent APIs" either happens or doesn't.
Serialization stays pluggable, but you pick one. If the package offers serializer hooks, pick one default and treat any other as a migration path — don't let models and controllers decide per-endpoint. Under Laravel, an ApiResource or an explicit toArray() is usually better than a general-purpose serializer, so unless the package's serializer is materially better, don't adopt it.
Per-module versioning is a design decision, not a default. Version at the route prefix, not at the class level, and keep it aligned across modules unless a module genuinely needs to diverge. Version pinning per module is exactly the use case that makes a modular API architecture worth it; it's also a footgun if it drifts silently.
Test the registration plumbing, not just endpoints. Write your own integration tests around module registration, route discovery, and route-caching — that's the layer with no upstream tests and the layer most likely to bite you when a convention silently resolves to a default.
composer why-not before assuming anything. If Symfony compiler passes or kernel events are in the core, the reusable surface narrows to reference design.Handler plus the bundle's exception handler means one may shadow the other. Pick one and disable or delegate to the other — check whether the package registers a listener, a middleware, or a kernel event, and trace it end to end.ApiResource vs JsonResponse from a controller: if you allow more than one, you will spend debugging time on which one shaped a given response. Enforce a single path.php artisan route:cache works with the convention-based loader. Closures and dynamically-resolved controllers break route caching, and it's easy to introduce that in an adapter.boot()/load() path when it fails.The honest day-to-day summary: for a Laravel developer, the value here is the conventions, not the code. If your team ships endpoints across multiple modules and has felt the central-routes-file bottleneck, spend the sprint, wire two real modules through it, and compare against a service provider plus a route file per module. If the comparison is a wash, keep the naming and structure ideas and write the few hundred lines yourself — you'll end up with a smaller dependency surface, a real test suite, and an owner who isn't a single unreachable maintainer.
How can I help you explore Laravel packages today?