Try Live
Add Docs
Rankings
Pricing
Enterprise
Docs
Install
Install
Docs
Pricing
Enterprise
More...
More...
Try Live
Rankings
Add Docs
Filament API Service
https://github.com/rupadana/filament-api-service
Admin
Filament API Service is a powerful yet simple package that enables automatic API generation for
...
Tokens:
17,291
Snippets:
71
Trust Score:
8.7
Update:
6 months ago
Context
Skills
Chat
Benchmark
75.2
Suggestions
Latest
Show doc for...
Code
Info
Show Results
Context Summary (auto-generated)
Raw
Copy
Link
# Filament API Service Filament API Service is a Laravel package that automatically generates RESTful API endpoints for FilamentPHP resources without manual route registration. It transforms Filament admin panels into API backends by providing automatic CRUD operations, authentication, authorization, and query capabilities for any registered Filament resource. The package integrates seamlessly with Laravel Sanctum for token-based authentication, Spatie Laravel Permission for role-based access control, and Spatie Query Builder for advanced filtering, sorting, and field selection. It supports multi-tenancy, custom response transformers, public endpoints, and automatic API documentation generation through Scramble, making it ideal for building mobile apps, SPAs, or third-party integrations from existing Filament admin panels. --- ## Installation and Plugin Registration ```php // Install via Composer // composer require rupadana/filament-api-service // Register in Filament Panel Provider (e.g., app/Providers/Filament/AdminPanelProvider.php) use Filament\Panel; use Rupadana\ApiService\ApiServicePlugin; public function panel(Panel $panel): Panel { return $panel ->id('admin') ->plugins([ ApiServicePlugin::make() ->middleware([ // Optional: Add custom middleware for all API routes 'throttle:api', ]) ]); } ``` --- ## Generate API Service for Resource ```bash # Create API service for a Filament resource php artisan make:filament-api-service BlogResource # This creates: # - app/Filament/Resources/BlogResource/Api/BlogApiService.php # - app/Filament/Resources/BlogResource/Api/Handlers/CreateHandler.php # - app/Filament/Resources/BlogResource/Api/Handlers/UpdateHandler.php # - app/Filament/Resources/BlogResource/Api/Handlers/DeleteHandler.php # - app/Filament/Resources/BlogResource/Api/Handlers/PaginationHandler.php # - app/Filament/Resources/BlogResource/Api/Handlers/DetailHandler.php ``` ```php // Generated API Service (app/Filament/Resources/BlogResource/Api/BlogApiService.php) namespace App\Filament\Resources\BlogResource\Api; use Rupadana\ApiService\ApiService; use App\Filament\Resources\BlogResource; use App\Filament\Resources\BlogResource\Api\Handlers\CreateHandler; use App\Filament\Resources\BlogResource\Api\Handlers\UpdateHandler; use App\Filament\Resources\BlogResource\Api\Handlers\DeleteHandler; use App\Filament\Resources\BlogResource\Api\Handlers\PaginationHandler; use App\Filament\Resources\BlogResource\Api\Handlers\DetailHandler; class BlogApiService extends ApiService { protected static ?string $resource = BlogResource::class; // Optional: Customize route group name (default: resource slug) protected static ?string $groupRouteName = 'posts'; // Optional: Customize key field (default: 'id') protected static string $keyName = 'id'; public static function handlers(): array { return [ CreateHandler::class, UpdateHandler::class, DeleteHandler::class, PaginationHandler::class, DetailHandler::class, ]; } } // Routes automatically available at: // GET /api/admin/blogs - List all blogs (paginated) // GET /api/admin/blogs/1 - Get single blog // POST /api/admin/blogs - Create new blog // PUT /api/admin/blogs/1 - Update blog // DELETE /api/admin/blogs/1 - Delete blog ``` --- ## Authentication - Login and Logout ```bash # Publish Laravel Sanctum migrations (Laravel 11+) php artisan install:api php artisan migrate ``` ```bash # Login API endpoint curl -X POST http://localhost/api/auth/login \ -H "Content-Type: application/json" \ -d '{ "email": "admin@example.com", "password": "password" }' # Response (201 Created) { "success": true, "message": "Login success.", "token": "1|abc123xyz...plainTextToken" } # Response on failure (401 Unauthorized) { "success": false, "message": "The provided credentials are incorrect." } ``` ```bash # Logout API endpoint curl -X POST http://localhost/api/auth/logout \ -H "Authorization: Bearer 1|abc123xyz...plainTextToken" # Response (200 OK) { "success": true, "message": "Logout success." } ``` --- ## Making Authenticated API Requests ```bash # List all blogs with authentication curl -X GET http://localhost/api/admin/blogs \ -H "Authorization: Bearer 1|abc123xyz...plainTextToken" # Get single blog curl -X GET http://localhost/api/admin/blogs/5 \ -H "Authorization: Bearer 1|abc123xyz...plainTextToken" # Create new blog curl -X POST http://localhost/api/admin/blogs \ -H "Authorization: Bearer 1|abc123xyz...plainTextToken" \ -H "Content-Type: application/json" \ -d '{ "title": "My First Post", "content": "This is the blog content", "author_id": 1, "published": true }' # Response (200 OK) { "success": true, "message": "Successfully Create Resource", "data": { "id": 10, "title": "My First Post", "content": "This is the blog content", "author_id": 1, "published": true, "created_at": "2025-10-13T10:30:00.000000Z", "updated_at": "2025-10-13T10:30:00.000000Z" } } # Update blog curl -X PUT http://localhost/api/admin/blogs/10 \ -H "Authorization: Bearer 1|abc123xyz...plainTextToken" \ -H "Content-Type: application/json" \ -d '{ "title": "Updated Title", "published": false }' # Delete blog curl -X DELETE http://localhost/api/admin/blogs/10 \ -H "Authorization: Bearer 1|abc123xyz...plainTextToken" # Response (200 OK) { "success": true, "message": "Successfully Delete Resource", "data": { "id": 10, "title": "Updated Title", ... } } ``` --- ## Query Filtering, Sorting, and Field Selection ```php // Enable query features by implementing contracts in your Model namespace App\Models; use Illuminate\Database\Eloquent\Model; use Rupadana\ApiService\Contracts\HasAllowedFields; use Rupadana\ApiService\Contracts\HasAllowedSorts; use Rupadana\ApiService\Contracts\HasAllowedFilters; class Blog extends Model implements HasAllowedFields, HasAllowedSorts, HasAllowedFilters { public static function getAllowedFields(): array { return ['id', 'title', 'content', 'author_id', 'published', 'created_at', 'updated_at']; } public static function getAllowedSorts(): array { return ['title', 'created_at', 'published']; } public static function getAllowedFilters(): array { return ['title', 'author_id', 'published', 'created_at']; } } // Alternative: Use static properties instead of methods class Product extends Model { public static array $allowedFields = ['name', 'description', 'price', 'created_at']; public static array $allowedSorts = ['name', 'price', 'created_at']; public static array $allowedFilters = ['name', 'price', 'created_at']; protected $guarded = []; } ``` ```bash # Select specific fields curl "http://localhost/api/admin/blogs?fields[blogs]=id,title,created_at" \ -H "Authorization: Bearer token" # Sort by field (ascending) curl "http://localhost/api/admin/blogs?sort=created_at" \ -H "Authorization: Bearer token" # Sort descending curl "http://localhost/api/admin/blogs?sort=-created_at" \ -H "Authorization: Bearer token" # Filter results curl "http://localhost/api/admin/blogs?filter[published]=true" \ -H "Authorization: Bearer token" # Filter by multiple conditions curl "http://localhost/api/admin/blogs?filter[author_id]=5&filter[published]=true" \ -H "Authorization: Bearer token" # Pagination curl "http://localhost/api/admin/blogs?per_page=10&page=2" \ -H "Authorization: Bearer token" # Combine all features curl "http://localhost/api/admin/blogs?fields[blogs]=id,title,published&sort=-created_at&filter[published]=true&per_page=15&page=1" \ -H "Authorization: Bearer token" # Response structure { "data": [ { "id": 1, "title": "First Post", "published": true }, ... ], "links": { "first": "http://localhost/api/admin/blogs?page=1", "last": "http://localhost/api/admin/blogs?page=5", "prev": null, "next": "http://localhost/api/admin/blogs?page=2" }, "meta": { "current_page": 1, "from": 1, "last_page": 5, "per_page": 15, "to": 15, "total": 73 } } ``` --- ## Custom Response Transformers ```bash # Generate transformer php artisan make:filament-api-transformer Blog # Creates: app/Filament/Resources/BlogResource/Api/Transformers/BlogTransformer.php ``` ```php // Custom Transformer (app/Filament/Resources/BlogResource/Api/Transformers/BlogTransformer.php) namespace App\Filament\Resources\BlogResource\Api\Transformers; use Illuminate\Http\Resources\Json\JsonResource; class BlogTransformer extends JsonResource { public function toArray($request) { $blog = $this->resource->toArray(); return [ 'id' => $blog['id'], 'title' => strtoupper($blog['title']), 'excerpt' => substr($blog['content'], 0, 100), 'author' => [ 'id' => $blog['author_id'], 'name' => $this->resource->author->name ?? null, ], 'published_status' => $blog['published'] ? 'live' : 'draft', 'published_date' => $blog['created_at'], 'url' => url('/blogs/' . $blog['id']), 'hash' => md5($blog['title']), ]; } } // Register transformer in Resource namespace App\Filament\Resources; use App\Filament\Resources\BlogResource\Api\Transformers\BlogTransformer; use Filament\Resources\Resource; class BlogResource extends Resource { protected static ?string $model = Blog::class; public static function getApiTransformer() { return BlogTransformer::class; } } ``` ```bash # API response with custom transformer curl http://localhost/api/admin/blogs/1 \ -H "Authorization: Bearer token" # Response { "id": 1, "title": "MY FIRST POST", "excerpt": "This is the blog content that explains...", "author": { "id": 5, "name": "John Doe" }, "published_status": "live", "published_date": "2025-10-13T10:30:00.000000Z", "url": "http://localhost/blogs/1", "hash": "abc123def456" } ``` --- ## Custom API Handlers ```bash # Generate specific handlers php artisan make:filament-api-handler BlogResource # Or specify handler type php artisan make:filament-api-handler Blog CreateHandler php artisan make:filament-api-handler Blog CustomSearchHandler ``` ```php // Custom PaginationHandler with additional logic namespace App\Filament\Resources\BlogResource\Api\Handlers; use Illuminate\Http\Request; use Rupadana\ApiService\Http\Handlers; use App\Filament\Resources\BlogResource; use Spatie\QueryBuilder\QueryBuilder; class PaginationHandler extends Handlers { public static ?string $uri = '/'; public static string $method = 'get'; public static ?string $resource = BlogResource::class; public function handler(Request $request) { $model = static::getModel(); $query = QueryBuilder::for($model) ->allowedFields($this->getAllowedFields() ?? []) ->allowedSorts($this->getAllowedSorts() ?? []) ->allowedFilters($this->getAllowedFilters() ?? []) ->allowedIncludes($this->getAllowedIncludes() ?? []) ->where('published', true) // Custom logic: only published posts ->paginate($request->query('per_page', 15)) ->appends($request->query()); return static::getApiTransformer()::collection($query); } } // Custom DetailHandler with error handling class DetailHandler extends Handlers { public static ?string $uri = '/{id}'; public static string $method = 'get'; public static ?string $resource = BlogResource::class; public function handler($id) { $model = static::getModel()::query(); $query = QueryBuilder::for( $model->where(static::getKeyName(), $id) )->first(); if (!$query) { return static::sendNotFoundResponse(); } $transformer = static::getApiTransformer(); return new $transformer($query); } } // Custom CreateHandler with validation class CreateHandler extends Handlers { public static ?string $uri = '/'; public static string $method = 'post'; public static ?string $resource = BlogResource::class; public function handler(Request $request) { $validated = $request->validate([ 'title' => 'required|string|max:255', 'content' => 'required|string', 'author_id' => 'required|exists:users,id', 'published' => 'boolean', ]); $model = new (static::getModel()); $model->fill($validated); $model->save(); return static::sendSuccessResponse($model, 'Successfully Create Resource'); } } // Custom handler for search functionality class SearchHandler extends Handlers { public static ?string $uri = '/search'; public static string $method = 'get'; public static ?string $resource = BlogResource::class; public function handler(Request $request) { $query = $request->get('q', ''); $results = static::getModel()::query() ->where('title', 'like', "%{$query}%") ->orWhere('content', 'like', "%{$query}%") ->limit(20) ->get(); return static::getApiTransformer()::collection($results); } } ``` --- ## Public API Endpoints (No Authentication) ```php // Make specific handler public namespace App\Filament\Resources\BlogResource\Api\Handlers; use Rupadana\ApiService\Http\Handlers; use App\Filament\Resources\BlogResource; class PaginationHandler extends Handlers { public static ?string $uri = '/'; public static ?string $resource = BlogResource::class; public static bool $public = true; // No authentication required public function handler() { $model = static::getModel(); $query = QueryBuilder::for($model) ->where('published', true) ->paginate(request()->query('per_page', 15)); return static::getApiTransformer()::collection($query); } } class DetailHandler extends Handlers { public static ?string $uri = '/{id}'; public static ?string $resource = BlogResource::class; public static bool $public = true; // Public access public function handler($id) { $query = static::getModel()::where('id', $id) ->where('published', true) ->first(); if (!$query) { return static::sendNotFoundResponse(); } return new (static::getApiTransformer())($query); } } ``` ```bash # Access public endpoints without authentication curl http://localhost/api/admin/blogs curl http://localhost/api/admin/blogs/1 # No Authorization header needed ``` --- ## Multi-Tenancy Support ```php // Publish configuration // php artisan vendor:publish --tag=api-service-config // config/api-service.php return [ 'tenancy' => [ 'enabled' => true, 'awareness' => true, // Enable tenant-aware routes ], 'route' => [ 'panel_prefix' => true, // Required when tenancy awareness is enabled ], ]; ``` ```bash # Tenant-aware routes include tenant parameter # GET /api/admin/tenants/{tenant}/blogs # GET /api/admin/tenants/{tenant}/blogs/1 # POST /api/admin/tenants/{tenant}/blogs # PUT /api/admin/tenants/{tenant}/blogs/1 # DELETE /api/admin/tenants/{tenant}/blogs/1 # Example: Access blogs for tenant 'acme-corp' curl http://localhost/api/admin/tenants/acme-corp/blogs \ -H "Authorization: Bearer token" # API returns only data for the specified tenant { "data": [ { "id": 1, "title": "Acme Corp Blog Post", "tenant_id": "acme-corp", ... } ] } ``` ```php // Override tenant relationship in Handler namespace App\Filament\Resources\BlogResource\Api\Handlers; use Rupadana\ApiService\Http\Handlers; class PaginationHandler extends Handlers { protected static ?string $tenantOwnershipRelationshipName = 'organization'; // Handler methods... } ``` --- ## Configuration Options ```php // config/api-service.php return [ 'navigation' => [ 'token' => [ 'cluster' => null, 'group' => 'User', 'sort' => -1, 'icon' => 'heroicon-o-key', 'should_register_navigation' => false, ], ], 'models' => [ 'token' => [ 'enable_policy' => true, // Enable TokenPolicy authorization ], ], 'route' => [ 'panel_prefix' => true, // Routes: /api/{panel}/resource vs /api/resource 'use_resource_middlewares' => false, // Use resource-specific middleware ], 'tenancy' => [ 'enabled' => false, 'awareness' => false, // Add {tenant} parameter to routes ], 'login-rules' => [ 'email' => 'required|email', 'password' => 'required', ], 'login-middleware' => [], 'logout-middleware' => [ 'auth:sanctum', ], 'use-spatie-permission-middleware' => true, // Use Spatie Permission or Sanctum abilities ]; ``` ```php // Disable panel prefix in routes // Routes become: /api/blogs instead of /api/admin/blogs return [ 'route' => [ 'panel_prefix' => false, ], ]; // Enable resource-specific middlewares return [ 'route' => [ 'use_resource_middlewares' => true, ], ]; ``` ```php // Resource with custom middleware namespace App\Filament\Resources; use Filament\Resources\Resource; class BlogResource extends Resource { protected static ?string $model = Blog::class; protected static string|array $routeMiddleware = [ 'throttle:60,1', 'custom.middleware', ]; } ``` --- ## Role-Based Access Control with Spatie Permission ```php // Install Spatie Laravel Permission // composer require spatie/laravel-permission // php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider" // php artisan migrate // Create permissions for Blog resource use Spatie\Permission\Models\Permission; Permission::create(['name' => 'view_any_blog']); Permission::create(['name' => 'view_blog']); Permission::create(['name' => 'create_blog']); Permission::create(['name' => 'update_blog']); Permission::create(['name' => 'delete_blog']); // Assign permissions to user $user->givePermissionTo(['view_any_blog', 'create_blog', 'update_blog']); // Define permission in Handler namespace App\Filament\Resources\BlogResource\Api\Handlers; use Rupadana\ApiService\Http\Handlers; class CreateHandler extends Handlers { public static ?string $uri = '/'; public static string $method = 'post'; public static ?string $resource = BlogResource::class; protected static string $permission = 'create_blog'; // Handler logic... } ``` ```bash # User with permissions can access curl -X POST http://localhost/api/admin/blogs \ -H "Authorization: Bearer user_token_with_create_blog_permission" \ -H "Content-Type: application/json" \ -d '{"title": "New Post", "content": "..."}' # Response (200 OK) { "success": true, "message": "Successfully Create Resource", "data": {...} } # User without permission gets 403 Forbidden curl -X POST http://localhost/api/admin/blogs \ -H "Authorization: Bearer user_token_without_permission" # Response (403 Forbidden) { "message": "User does not have the right permissions." } ``` --- ## Custom Route Names and Prefixes ```php // Customize route group name in API Service namespace App\Filament\Resources\ProductResource\Api; use Rupadana\ApiService\ApiService; use App\Filament\Resources\ProductResource; class ProductApiService extends ApiService { protected static ?string $resource = ProductResource::class; protected static ?string $groupRouteName = 'our-products'; // Custom prefix public static function handlers(): array { return [ CreateHandler::class, UpdateHandler::class, DeleteHandler::class, PaginationHandler::class, DetailHandler::class, ]; } } // Routes become: // GET /api/admin/our-products // GET /api/admin/our-products/1 // POST /api/admin/our-products // PUT /api/admin/our-products/1 // DELETE /api/admin/our-products/1 ``` --- ## API Documentation with Scramble ```bash # Scramble is included automatically # Access auto-generated API documentation at: http://localhost/docs/api # Documentation includes: # - All available endpoints # - Request/response schemas # - Authentication requirements # - Query parameters # - Example requests ``` --- ## Error Responses ```php // Built-in error responses from Handlers trait namespace Rupadana\ApiService\Traits; trait HttpResponse { public static function sendNotFoundResponse() { return response()->json([ 'success' => false, 'message' => 'Resource not found.', ], 404); } public static function sendSuccessResponse($data, string $message = 'Success') { return response()->json([ 'success' => true, 'message' => $message, 'data' => $data, ], 200); } } ``` ```bash # 404 Not Found curl http://localhost/api/admin/blogs/999 \ -H "Authorization: Bearer token" { "success": false, "message": "Resource not found." } # 401 Unauthorized curl http://localhost/api/admin/blogs { "message": "Unauthenticated." } # 403 Forbidden (Insufficient permissions) { "message": "User does not have the right permissions." } # 422 Validation Error curl -X POST http://localhost/api/admin/blogs \ -H "Authorization: Bearer token" \ -d '{"title": ""}' { "message": "The title field is required.", "errors": { "title": ["The title field is required."] } } ``` --- ## Summary Filament API Service provides a zero-configuration approach to exposing Filament resources as RESTful APIs. By simply installing the plugin and generating API services with artisan commands, developers can instantly create production-ready APIs with authentication, authorization, pagination, filtering, and sorting capabilities. The package eliminates boilerplate code while maintaining full customization through handlers, transformers, and middleware. The package excels in scenarios where you need to build mobile applications, single-page applications, or third-party integrations that interact with Filament-managed data. It seamlessly integrates with Laravel's ecosystem including Sanctum for authentication, Spatie Permission for authorization, and automatically generates OpenAPI documentation. Whether building public-facing APIs, tenant-aware SaaS applications, or internal microservices, Filament API Service accelerates development by providing a consistent, secure, and well-documented API layer on top of your Filament admin panel.