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

Sdk Tools Laravel Package

juststeveking/sdk-tools

Tools for building SDKs in Laravel and PHP. juststeveking/sdk-tools provides utilities and conventions to scaffold, structure, and maintain API client packages, cutting boilerplate so you can ship clean, consistent SDKs faster.

View on GitHub
Deep Wiki
Context7

Getting Started

  1. Install the package plus a PSR-18 client. sdk-tools is transport-agnostic and ships no HTTP client of its own, so you always pair it with one:

    composer require juststeveking/sdk-tools guzzlehttp/guzzle
    

    Check composer.json constraints before you commit — the last release is from July 2023, so verify it installs cleanly on your current PHP baseline (8.2/8.3) and that the transitive PSR-7/PSR-18 versions resolve against what the rest of your app already uses.

  2. Expect to write the wiring yourself. There is no service provider, no auto-discovery, no config file to publish, no facade. You register bindings in your own provider. That is the whole integration surface:

    // app/Providers/SdkServiceProvider.php
    public function register(): void
    {
        $this->app->singleton(Connector::class, fn () => new Connector(
            client: new \GuzzleHttp\Client(['base_uri' => config('services.acme.base_url')]),
            // auth headers / default request options applied here
        ));
    
        $this->app->singleton(AcmeSdk::class, fn ($app) => new AcmeSdk(
            $app->make(Connector::class),
        ));
    }
    
  3. Where to look first. The source is small enough to read in an afternoon. Open src/ and identify four things before writing code: the abstract Sdk (how resources are registered), the Resource base (how verbs and paths are composed), the connector/transporter (whether it is PSR-18-compliant or a bespoke interface), and the response wrapper (what an error looks like — throws vs. returns). Those four answers determine every decision below.

  4. First use case: one resource, one endpoint. Do not start by modelling your whole API. Pick a single read-only endpoint, define one Resource subclass, extend Sdk to expose it, bind it in the provider, and call it from a queued job. That gets you a working vertical slice in an hour and surfaces header, base-URI, and error-model problems immediately.

Implementation Patterns

Wrap the SDK behind your own interface before you use it anywhere. The package is a scaffold, not a supported dependency. Define App\Sdk\Contracts\AcmeClient with the three or four methods your app actually needs, implement it with sdk-tools, and inject the interface. This keeps package-specific abstractions (Sdk, Resource, Connector) out of your controllers, jobs, and — critically — out of any public contract you might later expose.

One resource class per API surface. Group endpoints the way the third-party API documents them (Users, Invoices, Webhooks); don't create one god-resource. Keep resources thin: build the path, pick the verb, return the response. Push mapping, validation, and domain logic into an action or job layer above the SDK.

final class Users extends Resource
{
    public function find(int $id): Response
    {
        return $this->get("users/{$id}");
    }

    public function all(int $page = 1): Response
    {
        return $this->get('users', ['page' => $page]);
    }
}

Configure, don't hardcode. Base URL, token, timeout, and retry count belong in config/services.php read through env(), never literals in a resource. Because there's no package config file, this is your config file. Cache it with config:cache like any other config.

Use the caching layer deliberately. The package supports caching responses, and Laravel's Illuminate\Cache\Repository satisfies PSR-16, so you can hand it straight to the SDK:

$this->app->singleton(Connector::class, fn ($app) => new Connector(
    client: new \GuzzleHttp\Client([...]),
    cache: $app->make(\Illuminate\Contracts\Cache\Repository::class),
));

Only cache idempotent GETs. Namespace keys per tenant and per token, and keep TTLs short — a stale cache in an SDK is far harder to debug than a slow request.

Testing: inject a fake PSR-18 client, not Http::fake(). This is the biggest day-to-day adjustment for a Laravel developer. Http::fake() intercepts the Laravel HTTP client; it will not see these requests at all. Bind a mock PSR-18 client in your test case (a small stub returning canned PSR-7 responses, or mockery on the client interface), assert on the captured request URI and headers, and assert on the parsed domain result — not on the SDK's response object.

Expose a thin facade for ergonomics, if you want one. AcmeSdk::users()->find(1) reads nicely in a controller. Write the facade yourself against your own interface; don't let it proxy the package's Sdk class directly, or you've re-leaked the abstraction you just hid.

Normalise errors at the boundary. Map whatever the package does on 4xx/5xx into a small set of your own exceptions (AcmeNotFoundException, AcmeRateLimitException, AcmeRequestException) so the error contract is yours and a swap to Saloon or the Laravel HTTP client is a single-file change.

Add retries and timeouts at the PSR-18 client level. The toolkit won't do it for you. Guzzle middleware for Retry-After on 429/503 is worth writing once in the connector.

Gotchas and Tips

  • No service provider means no discovery — plan for it. Every consuming app writes the same ~20 lines of binding. Put them in a dedicated provider and document it; otherwise onboarding cost eats the time you saved on plumbing.
  • Verify the transport contract before you build on it. If the connector is a bespoke interface rather than PSR-18, your swap path is weaker than you think. Answer this question in the first hour, not the second week.
  • Check default Accept and Content-Type headers. Small SDK helpers commonly omit them, producing 406/415 responses against real APIs that are painful to diagnose because the request "looks fine." Set them explicitly in the connector.
  • Audit whether the base classes are final. If they are, compose; if they aren't, resist the urge to subclass deeply. Extending package internals is the fastest route to lock-in.
  • Pin to a commit hash, not a version range. Last release July 2023, one maintainer, zero dependents. A loose constraint gives you silent behavioural changes with no changelog to point at during an incident.
  • Have a fork plan before you need one. MIT makes forking trivial; do it deliberately. If you ever hit a PHP-version blocker or a security issue in a transitive dependency, you are the maintainer. Pre-scope the fork, put it under your own namespace, and name an owner.
  • Don't put this in business-domain code. Its only defensible home is inside a dedicated SDK package or an internal service-client layer. If you find Resource or Connector types appearing in your models, controllers, or API resources, stop and add the adapter.
  • The docs are the source. There are no examples and no config reference for a package this size; treat src/ as the specification and write your own short README in the wrapper so the next developer doesn't repeat the audit.
  • Time-box it. This is a spike-quality dependency. If a week of wiring behind your own interface doesn't clearly beat the Laravel HTTP client or Saloon for your use case, extract the patterns and drop the dependency — that is a perfectly good outcome, and cheaper than the annual maintenance line item.
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.
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle