BlackBits.io

APIs The Eloquent Way

APIs The Eloquent Way

Motivation

Almost every modern project requires third-party API integration — weather forecasts, stock data, and countless other use cases. While some APIs offer SDKs, many don't.

Quick access using ZTTP looks like:

$symbols = \Zttp\Zttp::get("https://api.iextrading.com/1.0/ref-data/symbols")->json();

As complexity grows, you need abstraction for errors, validation, and transformation. The goal: access APIs the way you access a database:

$user = User::where('email', $email)->first();

What if APIs worked the same way?

$symbol = IEX::Symbol()->where('symbol', $apiSymbol)->first();

Solution

The approach introduces an API → Endpoint → Shape pattern:

  • ApiConsumer — represents one API (e.g., JSONPlaceholder)
  • Endpoint — represents one resource (e.g., /posts), with chainable query methods
  • Shape — represents the data structure returned

Endpoints mirror Eloquent's query builder. Shapes mirror models. All results return as collections, enabling methods like filter() when the API itself doesn't support filtering:

$users = User::all();
$users = $users->filter(function ($value, $key) {
    return $value->age >= 21;
});

Shapes also support mutator-style custom methods (e.g., combining first_name + last_name), structural validation, and field normalization via $transformations.

Relations between endpoints are supported via hasOne() and hasMany():

$user = JSONPlaceholder::Post()->first()->user();

Results

The package black-bits/laravel-api-consumer provides:

  • Artisan generators for ApiConsumers, Endpoints, and Shapes
  • Chainable collection methods on Endpoints
  • Relation support between resources
  • Built-in caching out of the box

Show Case

Two APIs were implemented: IEX Trading and JSONPlaceholder.

Laravel showcase app — Show all Symbols

ApiConsumers Setup

composer require black-bits/laravel-api-consumer
php artisan vendor:publish --provider="BlackBits\ApiConsumer\ApiConsumerServiceProvider"
php artisan make:api-consumer IEX
php artisan make:api-consumer-endpoint Symbol -c IEX

Add base URL to /config/api-consumers.php:

return [
    'IEX' => [
        'apiBasePath' => 'https://api.iextrading.com/1.0',
    ],
];

ApiConsumer class (/app/ApiConsumers/IEX/IEX.php):

namespace App\ApiConsumers\IEX;

use BlackBits\ApiConsumer\ApiConsumer;

class IEX extends ApiConsumer
{
    protected function getEndpoint()
    {
        return config('api-consumers.IEX.apiBasePath');
    }
}

Endpoint class with caching (SymbolEndpoint.php):

class SymbolEndpoint extends Endpoint
{
    protected $path = '/ref-data/symbols';
    protected $shouldCache = true;
    protected $cacheDurationInMinutes = 10;

    public function all()
    {
        return $this->get();
    }
}

Shape class with transformations and relations (SymbolShape.php):

class SymbolShape extends BaseShape
{
    protected $return_shape_data_only = true;
    protected $require_shape_structure = true;

    protected $transformations = [
        'symbol'    => 'symbol',
        'name'      => 'name',
        'date'      => 'date',
        'isEnabled' => 'is_enabled',
        'type'      => 'type',
        'iexId'     => 'iex_id'
    ];

    protected $fields = ['symbol', 'name', 'date', 'is_enabled', 'type', 'iex_id'];

    public function company()
    {
        return $this->hasOne(CompanyEndpoint::class, 'symbol');
    }
}

Usage

$symbols = IEX::Symbol()->all();

// With relation
$company = IEX::Symbol()->first()->company();

Controller example with filtering:

public function list(Request $request)
{
    $symbols = IEX::Symbol();

    if (!empty($request->get('symbol'))) {
        $symbol = $request->get('symbol');
        $symbols->filter(function ($value, $key) use ($symbol) {
            return str_contains($value->symbol, $symbol);
        });
    }

    if (!empty($request->get('name'))) {
        $symbols->filterName($request->get('name'));
    }

    $data = ['symbols' => $symbols->take(10)->get()];
    return response()->view('symbols.list', $data);
}

JSONPlaceholder example — fetch a post with its comments:

Laravel showcase app — List Comments

public function listComments($post)
{
    $post = JSONPlaceholder::Post()->where('id', $post)->first();

    $data = [
        'comments' => $post->comments(),
        'post'     => $post
    ];

    return response()->view('json-placeholder.comments-list', $data);
}

Next Steps

  • Authentication support (next priority)
  • Additional HTTP methods (POST, PUT, etc.)
  • Improved relation functionality
  • Testing
  • Long-term: auto-generation for standardized endpoints (JSON API / Swagger)

Conclusion

The package aims to fill a gap in the Laravel ecosystem by offering a standardized, Eloquent-style interface for third-party API consumption. Open questions for feedback:

  • Thoughts on the Consumer → Endpoint → Shape structure?
  • Missing features?
  • Which APIs cause the most implementation friction?

Expert Level Laravel Web Development & Consulting Agency

We love Laravel, and so should you. Let us show you why.

About Us

Founded in 2014, Black Bits helps customers to reach their goals and to expand their market positions. We work with a wide range of companies, from startups that have a vision and first funding to get to market faster, to big industry leaders that want to profit from modern technologies. If you want to start on your project without building an internal dev-team first, or if you need extra expertise or resources, Black Bits – the Laravel Web Development Agency is here to help.

Laravel

Laravel

Vue.js

Vue.js

React

React

Next.js

Next.js

TailwindCSS

TailwindCSS

AWS

AWS

Laravel Cloud

Laravel Cloud

Vercel

Vercel

DigitalOcean

DigitalOcean

Cloudflare

Cloudflare

Terraform

Terraform

Kubernetes

Kubernetes

Wiz

Wiz

OpenAI

OpenAI

Stripe

Stripe

Let's Talk

You have the vision, we have the development expertise. We would love to hear about your project.

Platform down right now?

Call us directly at (+1) 541 237-0201. We're here to help!

Or send us a message. We'll get back to you within 24 hours.

Grants Pass, Oregon, U.S.A.

(+1) 541 237-0201

hello@blackbits.io