rupadana/filament-api-service
Installation
composer require rupadana/filament-api-service
Publish the config file:
php artisan vendor:publish --provider="Rupadana\FilamentApiService\FilamentApiServiceServiceProvider" --tag="config"
Enable for a Resource
Add the ApiService trait to your Filament resource:
use Rupadana\FilamentApiService\Traits\ApiService;
class PostResource extends Resource
{
use ApiService;
// ...
}
First API Endpoint After enabling, API endpoints are automatically generated for CRUD operations:
GET /api/resources/posts (List)POST /api/resources/posts (Create)GET /api/resources/posts/{id} (Retrieve)PUT /api/resources/posts/{id} (Update)DELETE /api/resources/posts/{id} (Delete)Test with:
curl -X GET http://your-app.test/api/resources/posts -H "Authorization: Bearer YOUR_TOKEN"
config/filament-api-service.php under auth.
'auth' => [
'driver' => 'sanctum', // or 'passport', 'jwt'
'guard' => 'api',
],
'base_path' => 'api/v1',
Override default behavior for specific resources:
class PostResource extends Resource
{
use ApiService;
public static function getApiService(): array
{
return [
'enabled' => true,
'public' => false, // Disable public access
'middleware' => ['throttle:60,1'], // Add middleware
'transformer' => PostTransformer::class, // Custom transformer
];
}
}
Configure defaults in config/filament-api-service.php:
'defaults' => [
'enabled' => true,
'public' => false,
'transformer' => \App\Transformers\DefaultTransformer::class,
'filters' => true, // Enable query filters
'sorting' => true, // Enable sorting
],
Leverage Spatie’s laravel-query-builder for dynamic queries:
use Spatie\QueryBuilder\QueryBuilder;
class PostResource extends Resource
{
use ApiService;
public static function getApiServiceQueryBuilder(): QueryBuilder
{
return QueryBuilder::for(Post::class)
->allowedFilters('title', 'published_at')
->allowedSorts('title', 'created_at');
}
}
Create a transformer to customize API responses:
namespace App\Transformers;
use Rupadana\FilamentApiService\Transformers\Transformer;
class PostTransformer extends Transformer
{
public function transform($resource)
{
return [
'id' => $resource->id,
'title' => $resource->title,
'slug' => $resource->slug,
'created_at' => $resource->created_at->toDateTimeString(),
];
}
}
Enable tenant-aware APIs:
'tenancy' => [
'enabled' => true,
'model' => \Stancl\Tenancy\Database\Models\Tenant::class,
'tenant_key' => 'id',
],
php artisan vendor:publish --tag="sanctum-config" to configure Sanctum.auth.driver to passport.tyrimden/jwt-auth or similar for JWT-based auth.'authorization' => [
'driver' => 'filament-shield', // or 'spatie-permission'
'policies' => [
Post::class => PostPolicy::class,
],
],
composer require scramjet/laravel-api-docs
Then configure in config/filament-api-service.php:
'documentation' => [
'enabled' => true,
'provider' => \Scramjet\LaravelApiDocs\Providers\ApiDocsServiceProvider::class,
],
public function test_api_endpoints()
{
$response = $this->actingAs(User::factory()->create())
->getJson('/api/resources/posts');
$response->assertOk();
}
Middleware Conflicts
auth:api vs. Filament’s auth).getApiService() or adjust Filament’s panel middleware.Transformer Overrides
transform() will result in empty responses.return ['key' => $resource->value];
Query Builder Caching
remember() or cache().->withoutCache().Public Endpoints Security
'public' => true) bypass authentication but may expose sensitive data.'public_fields' => ['id', 'title'], // Only expose these fields
Tenancy Misconfiguration
tenant_key or model is incorrect.\Stancl\Tenancy\Database\Models\Tenant::resolve();
Log API Requests Add middleware to log requests:
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Log;
class LogApiRequests
{
public function handle($request, Closure $next)
{
Log::info('API Request', [
'method' => $request->method(),
'path' => $request->path(),
'query' => $request->query(),
]);
return $next($request);
}
}
Register in config/filament-api-service.php:
'middleware' => [
\App\Http\Middleware\LogApiRequests::class,
],
Check Generated Routes Dump routes to verify API endpoints:
php artisan route:list | grep "api/resources"
Validate Transformers Test transformers in isolation:
$post = Post::first();
$transformer = new PostTransformer();
dd($transformer->transform($post));
Custom API Actions Add custom API endpoints alongside CRUD:
public static function getApiServiceActions(): array
{
return [
'publish' => [
'method' => 'PATCH',
'path' => '/posts/{post}/publish',
'handler' => [PostResource::class, 'publishPost'],
],
];
}
public static function publishPost($request, $post)
{
$post->update(['published_at' => now()]);
return response()->json(['message' => 'Post published']);
}
Dynamic Base Path Override the base path per resource:
public static function getApiService(): array
{
return [
'base_path' => 'admin/api', // Override global setting
];
}
Event Listeners
Hook into API events (e.g., ApiResourceCreated):
namespace App\Listeners;
use Rupadana\FilamentApiService\Events\ApiResourceCreated;
class LogApiCreation
{
public function handle(ApiResourceCreated $event)
{
\Log::info('API Resource Created', ['id' => $event->resource->id]);
}
}
Register in EventServiceProvider:
protected $listen = [
Api
How can I help you explore Laravel packages today?