dandoetech/laravel-bff

Laravel bridge for DanDoeTech BFF metadata: metadata endpoints, policy & visibility decorators.

Maintainers

Package info

github.com/dandoetech/laravel-bff

pkg:composer/dandoetech/laravel-bff

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 1

Open Issues: 0

v0.2.0 2026-03-20 12:07 UTC

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
  1. DefaultMetadataBuilder — derives widgets, table, forms, actions from the resource definition
  2. VisibilityDecorator — applies visibility rules (e.g., hide audit fields from non-admins)
  3. 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