PHPackages                             sushidev/fairu-sdk - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [API Development](/categories/api)
4. /
5. sushidev/fairu-sdk

ActiveLibrary[API Development](/categories/api)

sushidev/fairu-sdk
==================

Laravel SDK for Fairu GraphQL API

v1.2.0(2mo ago)0311↓40.6%[1 PRs](https://github.com/sushidev-team/fairu-sdk/pulls)MITPHPPHP ^8.2CI passing

Since Dec 30Pushed 1w agoCompare

[ Source](https://github.com/sushidev-team/fairu-sdk)[ Packagist](https://packagist.org/packages/sushidev/fairu-sdk)[ RSS](/packages/sushidev-fairu-sdk/feed)WikiDiscussions master Synced today

READMEChangelog (6)Dependencies (14)Versions (9)Used By (0)

Fairu SDK for Laravel
=====================

[](#fairu-sdk-for-laravel)

A comprehensive Laravel SDK for the [Fairu](https://fairu.app) GraphQL API with dynamic fragments, caching, and full type support.

Requirements
------------

[](#requirements)

- PHP 8.2+
- Laravel 10, 11, or 12

Installation
------------

[](#installation)

```
composer require sushidev/fairu-sdk
```

The package will auto-register its service provider and facade.

### Publish Configuration

[](#publish-configuration)

```
php artisan vendor:publish --tag=fairu-config
```

Configuration
-------------

[](#configuration)

Add the following to your `.env` file:

```
FAIRU_URL=https://fairu.app
FAIRU_TOKEN=your-api-token
```

### Full Configuration Options

[](#full-configuration-options)

```
// config/fairu.php
return [
    'base_url' => env('FAIRU_URL', 'https://fairu.app'),
    'token' => env('FAIRU_TOKEN'),
    'timeout' => 30,

    'retry' => [
        'times' => 3,
        'sleep' => 100,
    ],

    'cache' => [
        'enabled' => true,
        'store' => null, // null = default cache store
        'prefix' => 'fairu_',
        'ttl' => [
            'tenant' => 3600,
            'roles' => 3600,
            'assets' => 300,
            'default' => 600,
        ],
    ],
];
```

Important: UUIDs
----------------

[](#important-uuids)

All IDs in the Fairu API are UUIDs (Universally Unique Identifiers). This applies to all resources:

- Assets
- Folders
- Galleries
- Copyrights
- Licenses
- Users
- Roles
- Disks
- Tenants

Example:

```
$assetId = '550e8400-e29b-41d4-a716-446655440000';
$asset = Fairu::assets()->find($assetId);
```

Basic Usage
-----------

[](#basic-usage)

### Queries

[](#queries)

```
use SushiDev\Fairu\Facades\Fairu;

// Health Check
$status = Fairu::health()->check();

// Get single asset
$asset = Fairu::assets()->find('uuid');
$asset = Fairu::assets()->findByPath('/images/logo.png');

// Get multiple assets
$assets = Fairu::assets()->findMany(['uuid-1', 'uuid-2']);

// Search assets
$results = Fairu::assets()->search('logo', page: 1, perPage: 20);

// List assets in folder
$assets = Fairu::assets()->all(folderId: 'folder-uuid', page: 1, perPage: 20);

// Get folder content (folders + assets)
$content = Fairu::folders()->content('folder-uuid');
$content->folders; // Array of Folder objects
$content->assets;  // Array of Asset objects

// Get tenant info
$tenant = Fairu::tenant()->get();

// List sub-tenants of the current tenant
$subTenants = Fairu::tenant()->subTenants();
foreach ($subTenants as $subTenant) {
    $subTenant->getId();
    $subTenant->getName();
    $subTenant->getParentId();
    $subTenant->isSubTenant(); // true
}

// List galleries
$galleries = Fairu::galleries()->all(['tenant-uuid'], from: '2024-01-01');

// Other queries
$copyrights = Fairu::copyrights()->all();
$licenses = Fairu::licenses()->all();
$workflows = Fairu::workflows()->all();
$users = Fairu::users()->all();
$roles = Fairu::roles()->all();
$disks = Fairu::disks()->all();
```

### Mutations

[](#mutations)

```
use SushiDev\Fairu\Facades\Fairu;
use SushiDev\Fairu\DTOs\FileDTO;
use SushiDev\Fairu\DTOs\FolderDTO;
use SushiDev\Fairu\DTOs\GalleryDTO;
use SushiDev\Fairu\Enums\UploadType;

// Upload a file
$uploadLink = Fairu::uploads()->createLink(
    filename: 'photo.jpg',
    type: UploadType::STANDARD,
    folderId: 'folder-uuid',
    alt: 'Photo description'
);
// Use $uploadLink->getUrl() to upload your file via PUT request

// Update asset metadata
$asset = Fairu::assetMutations()->update(
    FileDTO::make()
        ->id('asset-uuid')
        ->alt('New alt text')
        ->description('Updated description')
        ->copyrightIds(['copyright-uuid'])
);

// Delete asset
Fairu::assetMutations()->delete('asset-uuid');

// Move asset to another folder
Fairu::assetMutations()->move('asset-uuid', 'new-folder-uuid');

// Create folder
$folder = Fairu::folderMutations()->create(
    FolderDTO::make()
        ->name('New Folder')
        ->parent('parent-uuid')
);

// Create gallery
$gallery = Fairu::galleryMutations()->create(
    GalleryDTO::make()
        ->name('Event 2024')
        ->folderId('folder-uuid')
        ->date(now())
        ->location('Berlin')
);

// Create a sub-tenant of the current tenant.
// The returned API key is only shown once - store it securely.
$result = Fairu::tenantMutations()->createSubTenant('Client Workspace');
$subTenantId = $result->getId();
$apiKey = $result->getApiKey();
$createdAt = $result->getCreatedAt();

// Detach a sub-tenant, making it a fully independent tenant
$detached = Fairu::tenantMutations()->detachSubTenant($subTenantId);
```

Fragments
---------

[](#fragments)

Fragments allow you to customize which fields are returned from the API.

### Predefined Fragments

[](#predefined-fragments)

Each resource type has three predefined variants: `minimal`, `default`, and `full`.

```
use SushiDev\Fairu\Facades\Fairu;

// Use predefined fragments
$asset = Fairu::assets()->find('uuid', Fairu::fragments()->asset('minimal'));
$asset = Fairu::assets()->find('uuid', Fairu::fragments()->asset('full'));
```

### Custom Fragments with FragmentBuilder

[](#custom-fragments-with-fragmentbuilder)

```
use SushiDev\Fairu\Fragments\FragmentBuilder;

// Build custom fragment
$fragment = FragmentBuilder::for('FairuAsset')
    ->select(['id', 'name', 'mime', 'url', 'width', 'height', 'blurhash'])
    ->with('copyrights', fn($f) => $f->select(['id', 'name', 'email']))
    ->with('licenses', fn($f) => $f->select(['id', 'name', 'type', 'start', 'end']))
    ->build();

$asset = Fairu::assets()->find('uuid', $fragment);

// With arguments (e.g., for URL parameters)
$fragment = FragmentBuilder::for('FairuAsset')
    ->select(['id', 'name'])
    ->withArguments('url', ['width' => 800, 'height' => 600, 'quality' => 80], [])
    ->build();
```

### Register Custom Fragments

[](#register-custom-fragments)

```
// In a service provider
Fairu::fragments()->register('my_asset_card',
    FragmentBuilder::for('FairuAsset')
        ->select(['id', 'name', 'url', 'blurhash', 'width', 'height'])
        ->build()
);

// Use it later
$asset = Fairu::assets()->find('uuid', Fairu::fragments()->get('my_asset_card'));
```

Caching
-------

[](#caching)

The SDK supports Laravel's cache system for API responses.

```
// Enable caching for a query (uses config TTL)
$tenant = Fairu::tenant()->cached()->get();

// Custom TTL (in seconds)
$roles = Fairu::roles()->cached(ttl: 3600)->all();

// Force fresh data (bypass cache)
$tenant = Fairu::tenant()->fresh()->get();

// Clear cached data
Fairu::tenant()->forget('cache-key');
```

DTOs (Data Transfer Objects)
----------------------------

[](#dtos-data-transfer-objects)

All input types have fluent DTOs for type-safe data handling.

```
use SushiDev\Fairu\DTOs\FileDTO;
use SushiDev\Fairu\DTOs\FolderDTO;
use SushiDev\Fairu\DTOs\CopyrightDTO;
use SushiDev\Fairu\DTOs\LicenseDTO;
use SushiDev\Fairu\DTOs\GalleryDTO;
use SushiDev\Fairu\DTOs\DiskDTO;
use SushiDev\Fairu\DTOs\DiskCredentialsDTO;
use SushiDev\Fairu\Enums\DiskType;
use SushiDev\Fairu\Enums\LicenseType;

// File DTO
$file = FileDTO::make()
    ->id('uuid')
    ->name('photo.jpg')
    ->alt('Description')
    ->caption('Caption text')
    ->description('Full description')
    ->focalPoint('50-50')
    ->copyrightIds(['cr-1', 'cr-2'])
    ->licenseIds(['lic-1']);

// Copyright DTO
$copyright = CopyrightDTO::make()
    ->name('John Doe Photography')
    ->email('john@example.com')
    ->phone('+1234567890')
    ->website('https://johndoe.com')
    ->active(true);

// License DTO
$license = LicenseDTO::make()
    ->name('Annual License')
    ->type(LicenseType::PERIOD)
    ->copyrightId('copyright-uuid')
    ->start(now())
    ->end(now()->addYear())
    ->days(365);

// Disk DTO with credentials
$credentials = DiskCredentialsDTO::make()
    ->key('aws-access-key')
    ->secret('aws-secret')
    ->bucket('my-bucket')
    ->region('eu-west-1');

$disk = DiskDTO::make()
    ->name('S3 Backup')
    ->type(DiskType::S3)
    ->folderId('folder-uuid')
    ->credentials($credentials)
    ->active(true);
```

Response Objects
----------------

[](#response-objects)

All API responses are wrapped in typed response objects.

```
$asset = Fairu::assets()->find('uuid');

// Access properties
$asset->id;
$asset->name;
$asset->mime;
$asset->url;
$asset->width;
$asset->height;
$asset->blurhash;

// Helper methods
$asset->isImage();      // true for image/* mime types
$asset->isVideo();      // true for video/* mime types
$asset->isPdf();        // true for application/pdf
$asset->getAspectRatio(); // width/height ratio

// Nested relations
$asset->getCopyrights(); // Array of Copyright objects
$asset->getLicenses();   // Array of License objects

// Array access
$asset['name'];
$asset['url'];

// JSON serialization
json_encode($asset);
```

### Paginated Lists

[](#paginated-lists)

```
$results = Fairu::assets()->search('logo');

// Access items
$results->items();       // Array of Asset objects
$results->first();       // First item
$results->last();        // Last item
$results->isEmpty();     // Check if empty
$results->count();       // Items on current page

// Pagination info
$results->total();       // Total items across all pages
$results->currentPage(); // Current page number
$results->lastPage();    // Last page number
$results->perPage();     // Items per page
$results->hasMorePages(); // Has more pages?

// Iteration
foreach ($results as $asset) {
    echo $asset->name;
}

// Collection-like methods
$ids = $results->pluck('id');
$filtered = $results->filter(fn($a) => $a->isImage());
$mapped = $results->map(fn($a) => $a->name);
```

Enums
-----

[](#enums)

All GraphQL enums are available as PHP 8.1 backed enums.

```
use SushiDev\Fairu\Enums\UploadType;
use SushiDev\Fairu\Enums\SortingDirection;
use SushiDev\Fairu\Enums\LicenseType;
use SushiDev\Fairu\Enums\WorkflowStatus;
use SushiDev\Fairu\Enums\WorkflowType;
use SushiDev\Fairu\Enums\UserStatus;
use SushiDev\Fairu\Enums\DiskType;
use SushiDev\Fairu\Enums\WebhookType;
use SushiDev\Fairu\Enums\CustomDomainStatus;
use SushiDev\Fairu\Enums\GallerySortingField;
use SushiDev\Fairu\Enums\VideoVersions;
use SushiDev\Fairu\Enums\DmcaStatus;
use SushiDev\Fairu\Enums\UploadShareLinkExpiration;
use SushiDev\Fairu\Enums\PdfSignatureRequestStatus;

// Usage
$type = UploadType::STANDARD;
$direction = SortingDirection::DESC;

// From string
$status = WorkflowStatus::from('PROCESSING');
$status = WorkflowStatus::tryFrom('INVALID'); // null
```

Error Handling
--------------

[](#error-handling)

```
use SushiDev\Fairu\Exceptions\FairuException;
use SushiDev\Fairu\Exceptions\AuthenticationException;
use SushiDev\Fairu\Exceptions\GraphQLException;

try {
    $asset = Fairu::assets()->find('uuid');
} catch (AuthenticationException $e) {
    // Invalid or missing token (401)
} catch (GraphQLException $e) {
    // GraphQL errors
    $errors = $e->getGraphQLErrors();
    $first = $e->getFirstError();

    if ($e->hasValidationErrors()) {
        $validation = $e->getValidationErrors();
    }
} catch (FairuException $e) {
    // General API errors
}
```

Events
------

[](#events)

The SDK dispatches events for debugging and logging.

```
use SushiDev\Fairu\Events\QueryExecuted;
use SushiDev\Fairu\Events\MutationExecuted;

// In EventServiceProvider
protected $listen = [
    QueryExecuted::class => [
        LogQueryListener::class,
    ],
    MutationExecuted::class => [
        LogMutationListener::class,
    ],
];

// Listener
class LogQueryListener
{
    public function handle(QueryExecuted $event)
    {
        Log::debug('Fairu Query', [
            'query' => $event->query,
            'variables' => $event->variables,
            'response' => $event->response,
        ]);
    }
}
```

Multipart Uploads
-----------------

[](#multipart-uploads)

For large files, use multipart uploads.

```
// Initialize multipart upload
$init = Fairu::uploads()->initMultipart(
    filename: 'large-video.mp4',
    folderId: 'folder-uuid',
    fileSize: 104857600, // 100MB
    contentType: 'video/mp4'
);

$fileId = $init->getId();
$uploadId = $init->getUploadId();

// Get upload URL for each part
$parts = [];
for ($i = 1; $i getMultipartPartUrl($fileId, $uploadId, $i);

    // Upload part to $partInfo['url'] via PUT
    // Collect ETag from response
    $parts[] = [
        'partNumber' => $i,
        'etag' => $etag,
    ];
}

// Complete upload
$result = Fairu::uploads()->completeMultipart($fileId, $uploadId, $parts);

// Or abort if needed
Fairu::uploads()->abortMultipart($fileId, $uploadId);
```

File Proxy
----------

[](#file-proxy)

The File Proxy provides image transformation and optimized file delivery.

### Configuration

[](#configuration-1)

```
FAIRU_FILE_PROXY_URL=https://files.fairu.app
```

### Basic Usage

[](#basic-usage-1)

```
use SushiDev\Fairu\Facades\Fairu;

// Generate a URL for an asset
$url = Fairu::fileProxy()->url('asset-uuid', 'image.jpg')->toUrl();

// From an Asset object
$asset = Fairu::assets()->find('asset-uuid');
$url = Fairu::fileProxy()->fromAsset($asset)->toUrl();
```

### Image Transformations

[](#image-transformations)

```
use SushiDev\Fairu\Facades\Fairu;
use SushiDev\Fairu\Enums\FileProxyFit;
use SushiDev\Fairu\Enums\FileProxyFormat;

// Resize image
$url = Fairu::fileProxy()
    ->url('asset-uuid', 'photo.jpg')
    ->width(800)
    ->height(600)
    ->toUrl();

// Set dimensions with aspect ratio
$url = Fairu::fileProxy()
    ->url('asset-uuid', 'photo.jpg')
    ->dimensions(1200, 800)
    ->toUrl();

// Change format and quality
$url = Fairu::fileProxy()
    ->url('asset-uuid', 'photo.jpg')
    ->format(FileProxyFormat::WEBP)
    ->quality(85)
    ->toUrl();

// Shorthand format methods
$url = Fairu::fileProxy()->url('asset-uuid', 'photo.jpg')->webp()->toUrl();
$url = Fairu::fileProxy()->url('asset-uuid', 'photo.jpg')->jpg()->toUrl();
$url = Fairu::fileProxy()->url('asset-uuid', 'photo.jpg')->png()->toUrl();

// Fit modes
$url = Fairu::fileProxy()
    ->url('asset-uuid', 'photo.jpg')
    ->dimensions(400, 400)
    ->cover()  // Scale and crop to fill (default)
    ->toUrl();

$url = Fairu::fileProxy()
    ->url('asset-uuid', 'photo.jpg')
    ->dimensions(400, 400)
    ->contain()  // Maintain aspect ratio within dimensions
    ->toUrl();

// Focal point for smart cropping
$url = Fairu::fileProxy()
    ->url('asset-uuid', 'photo.jpg')
    ->dimensions(400, 400)
    ->focal(50, 30)  // x=50%, y=30%
    ->toUrl();

// With zoom level
$url = Fairu::fileProxy()
    ->url('asset-uuid', 'photo.jpg')
    ->focal(50, 50, 2.0)  // x, y, zoom
    ->toUrl();
```

### Video Features

[](#video-features)

```
use SushiDev\Fairu\Enums\VideoVersions;

// Extract video frame at timestamp
$url = Fairu::fileProxy()
    ->url('asset-uuid', 'video.mp4')
    ->timestamp('00:00:05.000')
    ->toUrl();

// Video quality version
$url = Fairu::fileProxy()
    ->url('asset-uuid', 'video.mp4')
    ->videoVersion(VideoVersions::HIGH)
    ->toUrl();

// HLS streaming URL
$hlsUrl = Fairu::fileProxy()->hlsUrl('tenant-uuid', 'asset-uuid');
```

### Other Options

[](#other-options)

```
// Raw file download (no processing)
$url = Fairu::fileProxy()
    ->url('asset-uuid', 'document.eps')
    ->raw()
    ->toUrl();

// Process SVG to raster
$url = Fairu::fileProxy()
    ->url('asset-uuid', 'logo.svg')
    ->processSvg()
    ->width(200)
    ->toUrl();

// Signed URLs for restricted content
$url = Fairu::fileProxy()
    ->url('asset-uuid', 'file.jpg')
    ->signature($hmacSignature, $signatureDate)
    ->toUrl();
```

### Utility Methods

[](#utility-methods)

```
// Check if file exists
$exists = Fairu::fileProxy()->exists('asset-uuid');

// Get image dimensions
$meta = Fairu::fileProxy()->meta('asset-uuid');
// Returns: ['width' => 1920, 'height' => 1080]

// Health check
$healthy = Fairu::fileProxy()->health();
```

### Available Parameters

[](#available-parameters)

ParameterRangeDescription`width`1-6000Output width in pixels`height`1-6000Output height in pixels`quality`1-100JPEG/WebP quality (default: 95)`format`jpg, png, webpOutput format (default: webp)`fit`cover, containResize mode (default: cover)`focal`x-y-zoomSmart crop focal pointTesting
-------

[](#testing)

```
composer test
```

License
-------

[](#license)

MIT License. See [LICENSE](LICENSE) for details.

###  Health Score

45

—

FairBetter than 91% of packages

Maintenance92

Actively maintained with recent releases

Popularity16

Limited adoption so far

Community9

Small or concentrated contributor base

Maturity53

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 50% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~20 days

Recently: every ~25 days

Total

6

Last Release

83d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/4183575?v=4)[Rafael Issao](/maintainers/sushidev)[@sushidev](https://github.com/sushidev)

---

Top Contributors

[![leganz](https://avatars.githubusercontent.com/u/3373530?v=4)](https://github.com/leganz "leganz (17 commits)")[![Gitsack](https://avatars.githubusercontent.com/u/22915882?v=4)](https://github.com/Gitsack "Gitsack (11 commits)")[![github-actions[bot]](https://avatars.githubusercontent.com/in/15368?v=4)](https://github.com/github-actions[bot] "github-actions[bot] (6 commits)")

###  Code Quality

TestsPest

### Embed Badge

![Health badge](/badges/sushidev-fairu-sdk/health.svg)

```
[![Health](https://phpackages.com/badges/sushidev-fairu-sdk/health.svg)](https://phpackages.com/packages/sushidev-fairu-sdk)
```

###  Alternatives

[roots/acorn

Framework for Roots WordPress projects built with Laravel components.

9762.4M131](/packages/roots-acorn)[propaganistas/laravel-disposable-email

Disposable email validator

6023.0M6](/packages/propaganistas-laravel-disposable-email)[psalm/plugin-laravel

Psalm plugin for Laravel

3355.3M345](/packages/psalm-plugin-laravel)[spatie/laravel-export

Create a static site bundle from a Laravel app

674146.0k6](/packages/spatie-laravel-export)[simplestats-io/laravel-client

Server-side analytics for Laravel that follows the full funnel from visit to registration to payment, attributed to the channel that drove it. Revenue, MRR, churn and ad-spend profit (ROAS/CAC) per channel. GDPR compliant, ad-blocker proof.

5021.9k](/packages/simplestats-io-laravel-client)[harris21/laravel-fuse

Circuit breaker for Laravel queue jobs. Protect your workers from cascading failures.

44855.7k](/packages/harris21-laravel-fuse)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
