simple-as-fuck/php-api-toolkit
A no-nonsense PHP toolkit for building APIs without boilerplate. It bundles routing, request validation, JSON responses, and error handling into a small, dependency-light package so you can ship endpoints faster. Simple to install, easy to extend.
Before you write a line of integration code, do the diligence that the metadata is begging for: this package has 0 stars, 0 dependents, and an Unlicense license. That combination means no community review, no downstream battle-testing, and no warranty. The "last release" date is also in the future relative to normal package lifecycles, so verify what you're actually pulling from Packagist versus the repo before trusting a version constraint.
Minimal path:
composer.json first — check the required PHP version, the PSR-4 namespace, and whether it pulls in a framework you didn't ask for. Then read src/ top to bottom. It's a "simple" toolkit; the whole surface area should be readable in one sitting.composer require simple-as-fuck/php-api-toolkit in a throwaway directory, hit it with a request, confirm the response shape. Only after that does it go into composer.json of anything real.^ constraints. If you proceed at all, "simple-as-fuck/php-api-toolkit": "X.Y.Z" and commit the lockfile.Your first use case should be the narrowest one available: a single inbound webhook endpoint or a standalone JSON microservice that a Laravel app talks to over HTTP, rather than something embedded in your main application's request lifecycle. That keeps the blast radius small if the package is abandoned or behaves unexpectedly, and it lets you replace it later with a route in Laravel without touching business logic.
If the toolkit is framework-agnostic PHP (which the name strongly implies), decide up front whether you're using it alongside Laravel — a separate service — or inside it. Mixing a second HTTP kernel into a Laravel app is where most of the pain in the next section comes from.
Standalone service, Laravel as the client. This is the pattern that ages best. The toolkit owns its own front controller and route table; your Laravel app treats it as a downstream dependency and calls it with the HTTP client.
// Laravel side
$response = Http::baseUrl(config('services.toolkit.url'))
->withToken(config('services.toolkit.token'))
->timeout(5)
->retry(2, 200)
->post('/orders', $payload);
$response->throw();
Wrap that in a small ToolkitClient class bound in a service provider, so no controller ever touches Http:: directly. When you eventually migrate the endpoint into Laravel proper, you swap the binding and nothing else changes.
Webhook ingress with a queue handoff. Inbound webhooks are the highest-value first use: the toolkit receives and normalizes the payload, then pushes to a queue rather than doing work inline. Keep the receive path under a few milliseconds — validate the signature header, dump the raw body, dispatch.
// in the toolkit's handler — validate first, parse second
$raw = $request->getBody();
// verify signature against $raw BEFORE any parsing
dispatch_to_queue('webhook.received', ['raw' => $raw]);
respond(202, ['ok' => true]);
Adapter layer for payloads. If the toolkit hands you arrays or a Request-like object, convert to a typed DTO at the boundary immediately. Nothing downstream should know the toolkit exists.
Consuming-side tests are the important ones. Because you can't trust the package's own suite, lock its behavior in from outside:
Http::fake(['toolkit.test/orders' => Http::response(['id' => 1], 201)]);
$this->postJson('/checkout', [...])->assertOk();
Add one contract test that hits the real scratch instance in CI (or a recorded fixture) so a silent behavior change upstream is caught.
Config and secrets. Assume no config system exists. Read everything from environment variables in your own bootstrap, pass explicitly into the toolkit's constructor. Never let it reach for $_ENV on its own.
/api/v2/ingest/*) and hand that entire prefix over.eval, unserialize, dynamic include, and anything that builds SQL or shell strings from request data before it ever sees production.Content-Type, array vs object for empty payloads), not logic errors.false/null. Map whatever it throws into Laravel's exception handler so failures surface as 502s with context, not silent empty 200s.protected methods or a single interface in src/Contracts. Prefer composing around them over patching vendor/ — the latter evaporates on the next composer update even with a pinned version, the moment someone runs composer update --with-dependencies.Route::post in routes/api.php. If that page is short, the risk is acceptable. If it's long, don't adopt.How can I help you explore Laravel packages today?