data-api-builder icon indicating copy to clipboard operation
data-api-builder copied to clipboard

[Enh]: support the `@db()` replacement function for metadata.

Open JerryNixon opened this issue 2 months ago • 1 comments

What?

In the same way @env() and @akv() allow string replacements in the DAB configuration, introduce @db() which reads from database metadata.

This allows DAB to dynamically pull descriptions or other extended properties directly from the connected database, ensuring that configuration remains synchronized with database documentation.

Behavior

  • Resolves during configuration load after the database connection is established.
  • Supported only when data-source.type is mssql.
  • Applicable to any property.
  • Never writes back to the database.
  • If the referenced metadata is missing, it replaces with an empty string ('').

Syntax

@db('<scope>:<property>')

Where <scope> defines the target (database, schema, table, column, or parameter). DAB automatically infers parameter scope if the last segment begins with @.

Examples:

@db(':CompanyName')                      // database-level
@db('dbo:DisplayName')                   // schema-level
@db('dbo.Author:MS_Description')         // table-level
@db('dbo.Author.Id:MS_Description')      // column-level
@db('dbo.GetCustomer.@CustomerId:MS_Description')  // parameter-level

In the parameter-level, it's the @ that indicates parameter and switches from table to procedure.

Order of operation

@db() is resolved after @env() and @akv().

sequenceDiagram
  actor Engine as Engine
  participant ConfigInMem as ConfigInMem
  participant Environment as Environment
  participant AKV as AKV
  participant DB as Database
  participant Config as ConfigFile

  Engine ->> Config: Load Config
  Config -->> Engine: Config Data
  Engine ->> ConfigInMem: Create In-Memory Config

  Note over Engine: Perform Config Replacements

  activate Engine
  ConfigInMem -->> Engine: Parse @env Values
  Engine ->> Environment: Get
  Environment -->> Engine: Values
  Engine ->> ConfigInMem: Replace @env Values
  deactivate Engine

  activate Engine
  ConfigInMem -->> Engine: Parse @akv Values
  Engine ->> AKV: Request
  AKV -->> Engine: Secrets
  Engine ->> ConfigInMem: Replace @akv Values
  deactivate Engine

  activate Engine
  ConfigInMem -->> Engine: Parse @db Values
  Engine ->> DB: Query Metadata
  DB -->> Engine: Extended Property Values
  Engine ->> ConfigInMem: Replace @db Values
  deactivate Engine

  Engine ->> Engine: Start

Example configuration

{
  "entities": {
    "Author": {
      "description": "@db('dbo.Author:MS_Description')",
      "source": {
        "object": "dbo.Author",
        "type": "table"
      },
      "fields": {
        "Id": {
          "description": "@db('dbo.Author.Id:MS_Description')"
        }
      }
    },
    "GetCustomer": {
      "description": "@db('dbo.GetCustomer:MS_Description')",
      "parameters": {
        "CustomerId": {
          "description": "@db('dbo.GetCustomer.@CustomerId:MS_Description')"
        }
      }
    },
    "Metadata": {
      "description": "@db(':CompanyName')"
    }
  }
}

Considerations

  1. Include OTEL activity wrapping each metadata lookup.
  2. Cache results by scope to minimize repeated queries.
  3. Consider expansion for triggers, constraints, and user-defined functions.
  4. If a non-MSSQL data source is in scope, log a warning and skip resolution.

Sample

DECLARE @property NVARCHAR(255) = 'MS_Description';
DECLARE @schema NVARCHAR(255) = 'dbo';
DECLARE @object NVARCHAR(255) = 'Author';
DECLARE @column NVARCHAR(255) = 'Id';

SELECT value
FROM fn_listextendedproperty (
    @property,
    'schema', NULLIF(@schema, ''),
    CASE
        WHEN @object LIKE 'Get%' THEN 'procedure'
        ELSE 'table'
    END,
    NULLIF(@object, ''),
    CASE
        WHEN LEFT(@column, 1) = '@' THEN 'parameter'
        ELSE 'column'
    END,
    NULLIF(@column, ''),
    default, default
);

Examples:

Scope Call
Database @db(':CompanyName')EXEC fn_listextendedproperty('CompanyName', default, default, default, default, default, default)
Schema @db('dbo:DisplayName')EXEC fn_listextendedproperty('DisplayName','schema','dbo',default,default,default,default)
Table @db('dbo.Author:MS_Description')EXEC fn_listextendedproperty('MS_Description','schema','dbo','table','Author',default,default)
Column @db('dbo.Author.Id:MS_Description')EXEC fn_listextendedproperty('MS_Description','schema','dbo','table','Author','column','Id')
Parameter @db('dbo.GetCustomer.@CustomerId:MS_Description')EXEC fn_listextendedproperty('MS_Description','schema','dbo','procedure','GetCustomer','parameter','@CustomerId')

JerryNixon avatar Oct 29 '25 23:10 JerryNixon

Related #2863

JerryNixon avatar Oct 29 '25 23:10 JerryNixon