dandoetech/bff-metadata

Framework-agnostic BFF UI metadata core: derive frontend-ready Resource UI metadata from a central Resource Registry with override support.

Maintainers

Package info

github.com/dandoetech/bff-metadata

pkg:composer/dandoetech/bff-metadata

Statistics

Installs: 0

Dependents: 1

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:20:35 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.

Generate frontend-ready UI metadata from a Resource Registry. Derives table columns, form layouts, field widgets, and action buttons — consistently across resources, with per-project overrides. Framework-agnostic.

Installation

composer require dandoetech/bff-metadata

Requires dandoetech/resource-registry.

Quick Start

use DanDoeTech\BffMetadata\Builders\DefaultMetadataBuilder;

// $resource is any ResourceDefinitionInterface (from registry or built manually)
$builder = new DefaultMetadataBuilder();
$metadata = $builder->forResource($resource);

echo json_encode($metadata->toArray(), JSON_PRETTY_PRINT);

Output:

{
  "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": "category", "widget": "relation", "props": { "targetResource": "category", "multiple": false } },
    "category_name": { "name": "category_name", "widget": "text", "readOnly": true }
  },
  "table": {
    "columns": ["name", "price", "category", "category_name"],
    "defaultSort": "-created_at",
    "density": "comfortable"
  },
  "create": { "layout": [["name", "price"], ["category"]] },
  "update": { "layout": [["name", "price"], ["category"]] },
  "actions": [
    { "name": "create", "kind": "primary" },
    { "name": "delete", "kind": "danger", "confirm": true }
  ]
}

Applying Overrides

Customize the generated metadata without modifying the resource definition:

use DanDoeTech\BffMetadata\Builders\OverrideApplier;

$overrides = [
    'fields' => [
        'price' => ['label' => 'Price (EUR)', 'props' => ['step' => 0.5]],
    ],
    'table' => ['columns' => ['name', 'price'], 'defaultSort' => '-price'],
];

$final = (new OverrideApplier())->apply($metadata, $overrides);

Laravel users: Use dandoetech/laravel-bff for REST endpoints, policy-based visibility, and caching decorators.

API Overview

Core

Class Purpose
DefaultMetadataBuilder Implements MetadataProviderInterface — generates metadata from a resource definition
OverrideApplier Applies per-project tweaks onto generated metadata
I18nResolver Optional translation hook for labels and help text

Metadata Value Objects

Class Contents
ResourceUiMetadata Top-level container — fields, table, forms, actions, extensions. Has toArray()
FieldUiMetadata Widget type, label, help, required, readOnly, props, options, order, width
TableMetadata Column list, defaultSort, density
FormMetadata 2D layout grid (field names per row), submitOnEnter
ActionUiMetadata Name, label, kind (primary/default/danger), confirm flag

Contract

interface MetadataProviderInterface
{
    public function forResource(ResourceDefinitionInterface $resource): ResourceUiMetadata;
}

Implement this for decorators (policy filtering, caching, logging).

Widget Mapping

FieldType Widget
String text
Integer number
Float number (props: step: 0.01)
Boolean checkbox
DateTime date
Json json
Relations relation (props: targetResource, multiple)

Computed Fields

Computed fields from the registry are included as read-only fields in the table but excluded from form layouts, since they are derived values.

I18n

Pass a translator callable to resolve labels and help text:

$i18n = new I18nResolver(fn (string $key) => $translations[$key] ?? null);
$builder = new DefaultMetadataBuilder(i18n: $i18n);

Translation keys follow the pattern resource.{key}.label, field.{key}.{name}.label, action.{key}.{name}.

Testing

composer install
composer test        # PHPUnit
composer qa          # cs:check + phpstan + test

License

MIT