Skip to content
Docs

App Metadata Overlay

The app-metadata overlay is a presentation layer that describes how an application client should render the entities BifrostQL exposes: human-readable labels, form widgets, list grids, saved views, and relationship presentation. It is standalone JSON, served to SPA and React Native clients, and it sits on top of — without replacing — the existing schema-metadata system.

BifrostQL already has a schema-metadata system: the dbo.table { key: value } rule grammar that controls how the GraphQL API itself behaves — tenant filters, soft-delete, EAV flattening, policy enforcement. That metadata is consumed server-side, on the query and mutation paths.

The overlay answers a different question: how should a client present this entity to a user? That is a UI concern, not an API-behavior concern, so it lives in its own layer with its own contract.

Schema metadataApp-metadata overlay
PurposeControls API behavior (filtering, mutations, security)Controls client presentation (labels, forms, grids)
Grammardbo.table { key: value } rule stringsStandalone camelCase JSON
Consumed byBifrostQL server (query/mutation pipeline)SPA / React Native clients
Model typeDbModel, MetadataKeysAppMetadataModel, EntityMetadata
LoaderMetadataLoader, IMetadataSourceAppMetadataLoader, IAppMetadataSource
Keyed byQualified table nameQualified table name

Both layers are keyed by qualified table name (e.g. dbo.users), so the overlay aligns with DbModel tables without modifying them.

The overlay is a deliberately separate, coexisting pipeline. It is loaded independently, exposed independently, and is never merged into the schema-metadata system:

  • AddBifrostAppMetadata(...) registers the overlay as its own singleton AppMetadataModel. It is purely additive — it touches no service registered by AddBifrostQL, and can be added before, after, or omitted entirely with no effect on the schema-metadata pipeline.
  • The overlay deliberately does not reuse the { } rule-delimiter grammar. It is plain JSON.
  • Schema-metadata rules and overlay entries can both describe the same table; neither overrides the other because they govern different concerns.

Because the layers are independent, adding an overlay never changes existing API behavior, and all existing schema-metadata tests stay green.

AppMetadataModel is a pure data aggregate — no database or GraphQL dependency — describing entities keyed by qualified table name:

  • EntityMetadata — label, icon, display fields, navigation placement, plus nested Fields, Grid, and Relationships.
  • FieldMetadata — widget hint, validation rule, visibility, read-only, help text, layout group.
  • GridPresetMetadata — default columns, filters, sort, named SavedViews, and bulk actions.
  • RelationshipMetadata — target entity (by qualified table name), relationship kind (foreignKeySelector, childCollection, nestedPanel), foreign-key field, and display columns.

Overlay entries come from one or more IAppMetadataSource instances, merged in priority order by AppMetadataLoader:

  • FileAppMetadataSource — reads the overlay from a JSON file on disk (low priority). A missing file yields an empty overlay.
  • DatabaseAppMetadataSource — reads the overlay from a database table, one entity per row (higher priority).
  • CompositeAppMetadataSource — merges several sources; when more than one supplies the same qualified table name, the higher-priority source wins.
services.AddBifrostAppMetadata(new IAppMetadataSource[]
{
new FileAppMetadataSource("app-metadata.json"), // priority 0
new DatabaseAppMetadataSource(connectionString), // priority 100
});

The overlay is exposed to SPA and React Native clients through a GET endpoint, following the same middleware pattern BifrostQL uses for its info endpoint:

app.UseBifrostAppMetadata(); // GET /_app-metadata
// or with options
app.UseBifrostAppMetadata(o => o.Path = "/meta");

The endpoint serves the overlay as the stable camelCase JSON contract defined by AppMetadataJson — the same portable, RN-friendly shape the model serializes to. When no overlay is registered, the endpoint returns an empty overlay rather than 404, so clients always receive the stable contract.

A Membership Manager application tracks members, households, dues, and events. With the overlay, every presentation concern is data:

  • Labels and navigationmembers → “Members”, icon person, nav placement main.
  • Forms — each field carries a widget (text, select, datepicker), a layout group, and optional validation/help text, so a client renders the form generically with no entity-specific form code.
  • Grids — each entity’s GridPresetMetadata gives default columns, sort, filters, and saved views, so the list view needs no hardcoded grid.
  • Relationshipsmembers → households (foreign-key selector), members → dues (child collection), events → households — every target resolves to another overlay entity by qualified table name.

The result: a client can describe and render all four entities purely from the overlay JSON, with no hardcoded forms or grids.