Installation:
composer require brunopicci/services-bundle
Add the bundle to config/app.php under providers:
Brunopicci\ServicesBundle\ServicesBundle::class,
Publish Configuration (if needed):
php artisan vendor:publish --provider="Brunopicci\ServicesBundle\ServicesBundle" --tag="config"
This generates a config file at config/services-bundle.php.
First Use Case:
Inject the ServicesBundle service into a controller or command:
use Brunopicci\ServicesBundle\ServicesBundle;
public function __construct(private ServicesBundle $servicesBundle) {}
public function callApi()
{
$response = $this->servicesBundle->call('https://api.example.com/endpoint', 'GET');
return $response; // Returns decoded JSON as array
}
Basic API Calls:
Use the call() method for simple requests:
$this->servicesBundle->call('https://api.example.com/users', 'GET');
Raw JSON Response:
Pass false as the third argument to avoid decoding:
$rawResponse = $this->servicesBundle->call('https://api.example.com/users', 'GET', false);
POST/PUT/PATCH Requests with Data: Pass an array as the third argument for the request body:
$this->servicesBundle->call(
'https://api.example.com/users',
'POST',
['name' => 'John Doe', 'email' => 'john@example.com']
);
Custom Headers:
Use the withHeaders() method to attach headers:
$this->servicesBundle->withHeaders(['Authorization' => 'Bearer token'])
->call('https://api.example.com/protected', 'GET');
Configuration via Config File:
Define default settings in config/services-bundle.php:
'default_headers' => [
'Accept' => 'application/json',
'Content-Type' => 'application/json',
],
'timeout' => 30,
Dependency Injection: Bind the service to a custom interface for better testability:
$this->app->bind(
\App\Contracts\ApiClient::class,
function ($app) {
return $app->make(Brunopicci\ServicesBundle\ServicesBundle::class);
}
);
Error Handling: Wrap calls in a try-catch block to handle exceptions:
try {
$response = $this->servicesBundle->call('https://api.example.com/users', 'GET');
} catch (\Exception $e) {
Log::error('API call failed: ' . $e->getMessage());
return response()->json(['error' => 'Service unavailable'], 503);
}
Rate Limiting: Use Laravel's throttle middleware for API rate limiting:
Route::middleware(['throttle:60,1'])->group(function () {
Route::get('/api-data', [YourController::class, 'callApi']);
});
No Built-in Retry Logic:
The bundle does not implement automatic retries for failed requests. Use a library like spatie/retries or implement custom logic:
use Spatie\Retry\Retry;
Retry::times(3)->attempt(function () {
$this->servicesBundle->call('https://api.example.com/users', 'GET');
});
Configuration Overrides:
If you publish the config file, ensure you merge it correctly in config/app.php to avoid conflicts:
'services-bundle' => [
'timeout' => env('API_TIMEOUT', 30),
// Other overrides...
],
No Middleware Support: The bundle does not support Laravel middleware out of the box. For advanced use cases, extend the service:
class CustomServicesBundle extends ServicesBundle
{
public function call($url, $method, $data = [], $decode = true)
{
$this->middleware->dispatch(new Request());
return parent::call($url, $method, $data, $decode);
}
}
Deprecated Methods:
The bundle is under heavy development. Check the README or CHANGELOG for breaking changes between versions.
Enable Guzzle Debugging:
Add this to your config/services-bundle.php to log requests:
'debug' => env('APP_DEBUG', false),
Inspect Raw Responses:
Use the false decode parameter to debug raw responses:
$raw = $this->servicesBundle->call('https://api.example.com/users', 'GET', [], false);
Log::debug('Raw response:', ['response' => $raw]);
Validate API Responses:
Use Laravel's Validator to validate responses:
$response = $this->servicesBundle->call('https://api.example.com/users', 'GET');
$validator = Validator::make($response, [
'data.*.id' => 'required|integer',
'data.*.name' => 'required|string',
]);
Custom Response Handling: Extend the bundle to transform responses:
class ExtendedServicesBundle extends ServicesBundle
{
public function call($url, $method, $data = [], $decode = true)
{
$response = parent::call($url, $method, $data, $decode);
return $this->transformResponse($response);
}
protected function transformResponse($response)
{
// Custom logic (e.g., flatten arrays, add metadata)
return $response;
}
}
Add Authentication: Create a decorator for authentication:
class AuthenticatedServicesBundle extends ServicesBundle
{
public function call($url, $method, $data = [], $decode = true)
{
$this->withHeaders(['Authorization' => $this->getAuthToken()]);
return parent::call($url, $method, $data, $decode);
}
private function getAuthToken()
{
return 'Bearer ' . Auth::user()->apiToken;
}
}
Support for Async Requests: Use Laravel Queues to offload API calls:
dispatch(new CallApiJob('https://api.example.com/users', 'GET'));
class CallApiJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable;
public function handle()
{
$response = app(ServicesBundle::class)->call($this->url, $this->method);
// Process response...
}
}
How can I help you explore Laravel packages today?