dandoetech / laravel-bff
Laravel bridge for DanDoeTech BFF metadata: metadata endpoints, policy & visibility decorators.
Requires
- php: ^8.2
- dandoetech/bff-metadata: ^0.1 || ^0.2
- dandoetech/laravel-resource-registry: ^0.1 || ^0.2
- dandoetech/resource-registry: ^0.1 || ^0.2
- illuminate/auth: ^11.0 || ^12.0
- illuminate/cache: ^11.0 || ^12.0
- illuminate/contracts: ^11.0 || ^12.0
- illuminate/http: ^11.0 || ^12.0
- illuminate/routing: ^11.0 || ^12.0
- illuminate/support: ^11.0 || ^12.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.0
- orchestra/testbench: ^9.0 || ^10.0
- phpstan/phpstan: ^2.0
- phpunit/phpunit: ^11.0
This package is auto-updated.
Last update: 2026-04-20 12:25:44 UTC
README
Pre-release — Architecture by senior tech lead, implementation largely AI-assisted with human review. Not fully reviewed. Architecture may change before v1.0.0.
Serves frontend-ready UI metadata from the Resource Registry via REST endpoints. Applies policy-based capabilities and visibility rules so the frontend knows what the current user can see and do.
Installation
composer require dandoetech/laravel-bff
The service provider is auto-discovered. Publish the config:
php artisan vendor:publish --tag=ddt-bff-config
Requires dandoetech/laravel-resource-registry.
Quick Start
With resources defined and registered, the metadata endpoints are available immediately:
GET /api/bff/metadata → list all resources
GET /api/bff/metadata/{resource} → full metadata for one resource
List Response
{
"data": [
{ "key": "product", "label": "Product", "href": "/api/bff/metadata/product" },
{ "key": "category", "label": "Category", "href": "/api/bff/metadata/category" }
]
}
Single Resource Response
{
"data": {
"key": "product",
"label": "Product",
"fields": {
"name": { "name": "name", "widget": "text", "required": true },
"price": { "name": "price", "widget": "number", "required": true, "props": { "step": 0.01 } },
"category_name": { "name": "category_name", "widget": "text", "readOnly": true }
},
"table": {
"columns": ["name", "price", "category_name"],
"defaultSort": "-created_at",
"density": "comfortable"
},
"create": { "layout": [["name", "price"], ["category"]] },
"update": { "layout": [["name", "price"], ["category"]] },
"actions": [
{ "name": "create", "kind": "primary" },
{ "name": "update", "kind": "default" },
{ "name": "delete", "kind": "danger", "confirm": true }
],
"extensions": {
"endpoints": {
"apiBase": "/api",
"resource": {
"list": "/api/product",
"show": "/api/product/{id}",
"create": "/api/product",
"update": "/api/product/{id}",
"delete": "/api/product/{id}",
"action": "/api/product/actions/{action}"
}
},
"capabilities": {
"resource": { "create": true, "update": true, "delete": false, "view": true },
"fields": { "price": { "edit": false, "view": true } }
}
}
}
}
Computed fields (like category_name) appear as read-only table columns, excluded from forms.
Decorator Chain
Metadata flows through a decorator chain before reaching the frontend:
DefaultMetadataBuilder → VisibilityDecorator → PolicyDecorator → Response
- DefaultMetadataBuilder — derives widgets, table, forms, actions from the resource definition
- VisibilityDecorator — applies visibility rules (e.g., hide audit fields from non-admins)
- PolicyDecorator — evaluates user permissions, adds
capabilities, marks/removes fields
Visibility Rules
Implement VisibilityRuleInterface to filter metadata based on runtime context:
use DanDoeTech\LaravelBff\Contracts\VisibilityRuleInterface; use DanDoeTech\BffMetadata\Metadata\ResourceUiMetadata; class HideInternalFields implements VisibilityRuleInterface { public function apply(ResourceUiMetadata $meta): ResourceUiMetadata { unset($meta->fields['internal_notes']); return $meta; } }
Register in config:
'visibility_rules' => [
HideInternalFields::class,
],
Policy Evaluator
The default GatePolicyEvaluator uses Laravel Gates to check permissions:
- Resource level:
create,update,delete,viewAny - Field level:
editField,viewField
Results are injected into extensions.capabilities. Fields with edit: false are marked read-only; fields with view: false are removed entirely.
Swap the evaluator by implementing PolicyEvaluatorInterface:
use DanDoeTech\LaravelBff\Contracts\PolicyEvaluatorInterface; class CustomEvaluator implements PolicyEvaluatorInterface { public function evaluate(string $resourceKey, ResourceUiMetadata $meta): array { return [ 'resource' => ['create' => true, 'update' => true, 'delete' => false], 'fields' => ['secret' => ['edit' => false, 'view' => false]], ]; } }
Configuration
config/ddt_bff.php:
return [ // Route prefix for metadata endpoints 'prefix' => env('BFF_PREFIX', 'api/bff'), // Visibility rules applied in order 'visibility_rules' => [ \DanDoeTech\LaravelBff\Visibility\HideAuditFields::class, ], // Policy evaluator (must implement PolicyEvaluatorInterface) 'policy_evaluator' => \DanDoeTech\LaravelBff\Laravel\GatePolicyEvaluator::class, ];
To scope metadata to authenticated users, add auth middleware to the route group in your app.
API Overview
| Class | Purpose |
|---|---|
BffServiceProvider |
Binds decorated MetadataProviderInterface, registers routes |
MetadataController |
index() lists resources, show() returns full metadata |
PolicyDecorator |
Adds capabilities from policy evaluator, enforces field-level access |
VisibilityDecorator |
Applies visibility rules in sequence |
GatePolicyEvaluator |
Default evaluator using Laravel Gates |
PolicyEvaluatorInterface |
Contract for custom auth adapters |
VisibilityRuleInterface |
Contract for custom visibility rules |
Testing
composer install composer test # PHPUnit (Orchestra Testbench) composer qa # cs:check + phpstan + test
License
MIT