connectholland/tulip-api-client
PHP client for the Tulip API, providing a simple way to authenticate and call Tulip endpoints from Laravel or any PHP app. Wraps requests and responses to help you integrate with Tulip services with minimal boilerplate.
Installation
composer require connectholland/tulip-api-client
Verify the package is autoloaded in composer.json under "autoload": { "psr-4": { ... } }.
First Use Case: Authentication Initialize the client with your API credentials:
use ConnectHolland\TulipApiClient\Client;
$client = new Client(
'your_api_key',
'your_api_secret',
'your_base_url' // e.g., 'https://api.tulip.example.com'
);
First API Call Fetch a basic endpoint (e.g., user info):
try {
$response = $client->get('/users/me');
$userData = json_decode($response->getBody(), true);
dd($userData); // Debug output
} catch (\Exception $e) {
dd($e->getMessage());
}
Key Files to Explore
src/Client.php: Core client logic, request handling.src/Exception/: Custom exceptions (e.g., ApiException, AuthException).tests/: Example test cases for common workflows.Create (POST)
$data = ['name' => 'Test User', 'email' => 'test@example.com'];
$response = $client->post('/users', json_encode($data), [
'headers' => ['Content-Type' => 'application/json']
]);
Read (GET)
$response = $client->get('/users/123');
$user = json_decode($response->getBody(), true);
Update (PUT/PATCH)
$client->put('/users/123', json_encode(['email' => 'new@example.com']));
Delete (DELETE)
$client->delete('/users/123');
Service Provider
Bind the client to Laravel’s container in AppServiceProvider:
public function register()
{
$this->app->singleton(Client::class, function ($app) {
return new Client(
config('services.tulip.api_key'),
config('services.tulip.api_secret'),
config('services.tulip.base_url')
);
});
}
Facade (Optional) Create a facade for cleaner syntax:
// app/Facades/Tulip.php
namespace App\Facades;
use Illuminate\Support\Facades\Facade;
class Tulip extends Facade
{
protected static function getFacadeAccessor() { return 'tulip'; }
}
Update config/app.php to bind 'tulip' => Client::class.
Request Wrapper Extend the client for Laravel-specific features (e.g., logging, retries):
use Illuminate\Support\Facades\Log;
class LaravelTulipClient extends Client
{
public function request($method, $endpoint, $body = null, $headers = [])
{
try {
return parent::request($method, $endpoint, $body, $headers);
} catch (\Exception $e) {
Log::error("Tulip API Error: {$e->getMessage()}");
throw $e;
}
}
}
Manually parse paginated responses (library lacks built-in support):
$page = 1;
$perPage = 20;
$users = [];
do {
$response = $client->get("/users?page={$page}&per_page={$perPage}");
$data = json_decode($response->getBody(), true);
$users = array_merge($users, $data['data']);
$page++;
} while (!empty($data['links']['next']));
Deprecated API
/users/me may not exist in newer versions (use /auth/me instead).No Built-in Rate Limiting
use Symfony\Component\HttpClient\RetryableHttpClient;
$client = new RetryableHttpClient(
$originalClient,
[
'max_retries' => 3,
'delay' => 100,
'multiplier' => 2,
'statuses' => [429, 500, 502, 503, 504],
]
);
Authentication Quirks
if ($response->getStatusCode() === 401) {
$newToken = $client->refreshToken();
$client->setAuthToken($newToken);
// Retry request
}
No Type Safety
$schema = [
'type' => 'object',
'properties' => [
'id' => ['type' => 'integer'],
'name' => ['type' => 'string']
],
'required' => ['id', 'name']
];
JsonSchema::validate($userData, $schema);
Enable Guzzle Middleware Add logging to requests/responses:
$client->getClient()->getEmitter()->attach(
new \GuzzleHttp\Middleware::tap(function ($request) {
Log::debug('Request:', [
'method' => $request->getMethod(),
'uri' => (string) $request->getUri(),
'body' => $request->getBody() ? $request->getBody()->getContents() : null
]);
})
);
Mocking for Tests
Use GuzzleHttp\Handler\MockHandler to simulate API responses:
$mock = new MockHandler([
new Response(200, [], json_encode(['id' => 1, 'name' => 'Test']))
]);
$client->setClient(new ClientHandlerStack($mock));
Custom Endpoints Extend the client to add domain-specific methods:
class ExtendedTulipClient extends Client
{
public function createOrder(array $data)
{
return $this->post('/orders', json_encode($data));
}
}
Webhook Handling Validate incoming webhooks (not part of the client):
public function handleWebhook(Request $request)
{
$signature = $request->header('X-Tulip-Signature');
$payload = $request->getContent();
if (!$this->verifySignature($payload, $signature)) {
abort(403, 'Invalid signature');
}
// Process payload
}
Caching Responses Cache frequent requests (e.g., user data):
use Illuminate\Support\Facades\Cache;
$user = Cache::remember("tulip_user_{$userId}", now()->addHours(1), function () use ($client, $userId) {
return $client->get("/users/{$userId}")->getBody();
});
https://) and ends with /.config/services.php:
'tulip' => [
'timeout' => 30, // seconds
'connect_timeout' => 5,
],
Then configure the client:
$client->getClient()->getConfig(['timeout' => config('services.tulip.timeout')]);
How can I help you explore Laravel packages today?