FileHutch
Documentation

Getting started

Laravel

Attach files to Eloquent models with one string column. The same policies, IDs and delivery as every other stack.

The API key is scoped to a project and can upload, sign and delete. Keep it on your server. Browsers upload through routes this package registers in your app — see Browser-direct uploads.

Install#

Terminal

composer require filehutch/filehutch-php

Terminal

# .env
FILE_HUTCH_API_KEY=fh_…   # Dashboard → API keys

The service provider and the FileHutch facade are discovered automatically. PHP 8.2 and up, Laravel 11 and 12.

PHP

use FileHutch\Laravel\Facades\FileHutch;

FileHutch::upload($request->file('report'), policy: 'documents');

Add the column and the attachment#

One string column per attachment. No blobs table, no attachments table, no disk configuration.

PHP

Schema::table('users', fn (Blueprint $table) => $table->string('avatar_file_id')->nullable());

app/Models/User.php

use FileHutch\Laravel\HasHutch;

class User extends Model
{
    use HasHutch;

    protected function hutches(): array
    {
        return ['avatar' => 'avatars'];
    }
}

Use it#

PHP

// Assign an uploaded file. Goes to storage on save.
$user->avatar = $request->file('avatar');
$user->save();

// Read it back
$user->avatar_url;                       // public policies
$user->avatarSignedUrl(expiresIn: 600);  // any file
$user->avatarTransformUrl('thumb');      // a named transform

// Remove it, remotely and locally
$user->purgeAvatar();

A wrong content type or an oversized file becomes a ValidationException on save(), keyed by the attachment name, before anything leaves your server — the policy is checked locally first.

Browser-direct uploads#

Bytes go from the browser straight to storage; only two small JSON calls touch your app. The package registers them at /file_hutch/uploads, closed until you say who may upload, in a service provider's boot:

app/Providers/AppServiceProvider.php

use FileHutch\Laravel\DirectUploads;
use Illuminate\Http\Request;

DirectUploads::authorize(fn (Request $request, string $policy) =>
    $request->user() !== null && $policy === 'avatars');

The browser half is the <filehutch-upload> element from the TypeScript SDK. It posts to /file_hutch/uploads by default and reads Laravel's CSRF token from the csrf-token meta tag, so it needs nothing but a policy.

js

// resources/js/app.js
import { defineUploadElement } from "@filehutch/sdk/browser"
defineUploadElement()

html

<filehutch-upload policy="avatars" name="avatar" accept="image/*"></filehutch-upload>

On success the field holds the file ID, and assigning it is one line; the attachment checks on save that it is a ready file under its policy.

PHP

if ($request->filled('avatar')) {
    $request->user()->update(['avatar' => $request->input('avatar')]);
}

More in Browser uploads and the package README.