or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

context-metadata.mdcore-constructs.mddependency-management.mdindex.mdtree-nodes.mdvalidation.md
tile.json

context-metadata.mddocs/

Context and Metadata

The context and metadata systems provide mechanisms for hierarchical configuration sharing and arbitrary data attachment to constructs. Context enables configuration inheritance throughout the construct tree, while metadata supports debugging, tooling, and documentation.

Capabilities

Context System

Hierarchical configuration system that allows setting values at any level of the construct tree and retrieving them with automatic inheritance from parent scopes.

/**
 * This can be used to set contextual values.
 * Context must be set before any children are added, since children may consult context info during construction.
 * If the key already exists, it will be overridden.
 * @param key The context key
 * @param value The context value
 */
setContext(key: string, value: any): void;

/**
 * Retrieves a value from tree context if present. Otherwise, would throw an error.
 * Context is usually initialized at the root, but can be overridden at any point in the tree.
 * @param key The context key
 * @returns The context value or throws error if there is no context value for this key
 */
getContext(key: string): any;

/**
 * Retrieves a value from tree context.
 * Context is usually initialized at the root, but can be overridden at any point in the tree.
 * @param key The context key
 * @returns The context value or `undefined` if there is no context value for this key.
 */
tryGetContext(key: string): any;

/**
 * Retrieves the all context of a node from tree context.
 * Context is usually initialized at the root, but can be overridden at any point in the tree.
 * @param defaults Any keys to override the retrieved context
 * @returns The context object or an empty object if there is discovered context
 */
getAllContext(defaults?: object): any;

Usage Examples:

import { Construct, RootConstruct } from "constructs";

const app = new RootConstruct("App");

// Set global configuration at the root
app.node.setContext("environment", "production");
app.node.setContext("region", "us-west-2");
app.node.setContext("enableLogging", true);

// Create child constructs that inherit context
const database = new Construct(app, "Database");
const api = new Construct(app, "API");

// Children can access parent context
console.log(database.node.getContext("environment")); // "production"
console.log(api.node.getContext("region")); // "us-west-2"

// Override context at specific levels
const staging = new Construct(app, "Staging");
staging.node.setContext("environment", "staging");
staging.node.setContext("enableLogging", false);

const stagingDb = new Construct(staging, "Database");
console.log(stagingDb.node.getContext("environment")); // "staging" (overridden)
console.log(stagingDb.node.getContext("region")); // "us-west-2" (inherited)
console.log(stagingDb.node.getContext("enableLogging")); // false (overridden)

// Safe context retrieval
const debugMode = staging.node.tryGetContext("debugMode");
console.log(debugMode); // undefined (not set)

try {
  staging.node.getContext("nonexistent"); // Throws error
} catch (error) {
  console.log(error.message); // "No context value present for nonexistent key"
}

// Get all context with defaults
const allContext = stagingDb.node.getAllContext({ 
  timeout: 30000,
  retries: 3 
});
console.log(allContext);
// {
//   timeout: 30000,
//   retries: 3,
//   environment: "staging",
//   region: "us-west-2", 
//   enableLogging: false
// }

Context Constraints

Context must be set before children are added to ensure consistency during construction.

Usage Examples:

import { Construct, RootConstruct } from "constructs";

const app = new RootConstruct("App");
const service = new Construct(app, "Service");

// Add a child first
const component = new Construct(service, "Component");

// Now trying to set context on service will fail
try {
  service.node.setContext("newConfig", "value");
} catch (error) {
  console.log(error.message); 
  // "Cannot set context after children have been added: Component"
}

// But we can still set context on the leaf node
component.node.setContext("componentConfig", "leafValue");
console.log(component.node.getContext("componentConfig")); // "leafValue"

// And we can still set context on the root
app.node.setContext("globalConfig", "rootValue");
console.log(service.node.getContext("globalConfig")); // "rootValue"

Metadata System

System for attaching arbitrary metadata to constructs, with optional stack trace capture for debugging.

/**
 * Adds a metadata entry to this construct.
 * Entries are arbitrary values and will also include a stack trace to allow tracing back to
 * the code location for when the entry was added. It can be used, for example, to include source
 * mapping in CloudFormation templates to improve diagnostics.
 * @param type a string denoting the type of metadata
 * @param data the value of the metadata (can be a Token). If null/undefined, metadata will not be added.
 * @param options options
 */
addMetadata(type: string, data: any, options?: MetadataOptions): void;

/**
 * An immutable array of metadata objects associated with this construct.
 * This can be used, for example, to implement support for deprecation notices, source mapping, etc.
 */
readonly metadata: MetadataEntry[];

Usage Examples:

import { Construct, RootConstruct, MetadataOptions } from "constructs";

const app = new RootConstruct("App");
const database = new Construct(app, "Database");

// Add simple metadata
database.node.addMetadata("version", "1.2.3");
database.node.addMetadata("description", "Primary application database");
database.node.addMetadata("tags", { 
  team: "backend",
  cost-center: "engineering",
  backup: "required"
});

// Add metadata with stack trace for debugging
const options: MetadataOptions = { 
  stackTrace: true 
};
database.node.addMetadata("deployment-info", {
  timestamp: new Date().toISOString(),
  deployer: "ci-pipeline"
}, options);

// Add metadata with custom trace function
function deploymentFunction() {
  database.node.addMetadata("deploy-source", "automated", {
    stackTrace: true,
    traceFromFunction: deploymentFunction
  });
}
deploymentFunction();

// Access metadata
const allMetadata = database.node.metadata;
console.log(allMetadata.length); // 4

allMetadata.forEach(entry => {
  console.log(`Type: ${entry.type}`);
  console.log(`Data:`, entry.data);
  if (entry.trace) {
    console.log(`Stack trace available: ${entry.trace.length} frames`);
  }
});

// Filter metadata by type
const versionMetadata = allMetadata.filter(m => m.type === "version");
console.log(versionMetadata[0].data); // "1.2.3"

// Null/undefined data is ignored
database.node.addMetadata("ignored", null);
database.node.addMetadata("alsoIgnored", undefined);
console.log(database.node.metadata.length); // Still 4, nulls ignored

MetadataEntry Interface

Structure defining metadata entries attached to constructs.

/**
 * An entry in the construct metadata table.
 */
interface MetadataEntry {
  /**
   * The metadata entry type.
   */
  readonly type: string;
  
  /**
   * The data.
   */
  readonly data: any;
  
  /**
   * Stack trace at the point of adding the metadata.
   * Only available if `addMetadata()` is called with `stackTrace: true`.
   * @default - no trace information
   */
  readonly trace?: string[];
}

MetadataOptions Interface

Configuration options for metadata attachment.

/**
 * Options for `construct.addMetadata()`.
 */
interface MetadataOptions {
  /**
   * Include stack trace with metadata entry.
   * @default false
   */
  readonly stackTrace?: boolean;
  
  /**
   * A JavaScript function to begin tracing from.
   * This option is ignored unless `stackTrace` is `true`.
   * @default addMetadata()
   */
  readonly traceFromFunction?: any;
}

Advanced Context Usage

Complex context scenarios including environment-specific configuration and hierarchical overrides.

Usage Examples:

import { Construct, RootConstruct } from "constructs";

// Multi-environment setup
const app = new RootConstruct("MultiEnvApp");

// Global defaults
app.node.setContext("logging", { level: "info", enabled: true });
app.node.setContext("monitoring", { metrics: true, tracing: false });
app.node.setContext("database", { 
  type: "postgresql", 
  version: "13",
  backup: true 
});

// Environment-specific overrides
const production = new Construct(app, "Production");
production.node.setContext("logging", { 
  level: "warn", 
  enabled: true,
  structured: true 
});
production.node.setContext("monitoring", { 
  metrics: true, 
  tracing: true,
  alerting: true 
});

const development = new Construct(app, "Development");  
development.node.setContext("logging", { 
  level: "debug", 
  enabled: true,
  console: true 
});
development.node.setContext("database", { 
  type: "sqlite", 
  version: "latest",
  backup: false 
});

// Service-specific configuration
const prodApi = new Construct(production, "API");
const prodDatabase = new Construct(production, "Database");

// API inherits production context but can access specific values
const apiLogging = prodApi.node.getContext("logging");
console.log(apiLogging.level); // "warn"
console.log(apiLogging.structured); // true

const apiDb = prodApi.node.getContext("database");
console.log(apiDb.type); // "postgresql" (inherited from app)
console.log(apiDb.version); // "13"

// Development services get different inherited context
const devApi = new Construct(development, "API");
const devLogging = devApi.node.getContext("logging");
console.log(devLogging.level); // "debug"
console.log(devLogging.console); // true

const devDb = devApi.node.getContext("database");
console.log(devDb.type); // "sqlite" (overridden in development)
console.log(devDb.backup); // false

Metadata for Tooling Integration

Using metadata to support development tools, deployment systems, and documentation generation.

Usage Examples:

import { Construct, RootConstruct } from "constructs";

const app = new RootConstruct("ToolingApp");
const service = new Construct(app, "UserService");

// Deprecation warnings
service.node.addMetadata("aws:cdk:warning", {
  type: "deprecation",
  message: "UserService construct is deprecated, use UserServiceV2",
  since: "2.0.0"
});

// Source mapping for CloudFormation/Terraform
service.node.addMetadata("aws:cdk:source", {
  file: "src/constructs/user-service.ts",
  line: 42,
  column: 8
}, { stackTrace: true });

// Cost allocation tags
service.node.addMetadata("cost-allocation", {
  project: "user-management",
  team: "identity",
  environment: service.node.tryGetContext("environment") || "unknown"
});

// Compliance and security metadata
service.node.addMetadata("security:classification", "internal");
service.node.addMetadata("compliance:gdpr", { 
  dataProcessing: true,
  retentionPeriod: "7 years",
  lawfulBasis: "contract"
});

// Build and deployment information
service.node.addMetadata("build:info", {
  version: process.env.BUILD_VERSION || "dev",
  commit: process.env.GIT_COMMIT || "unknown",
  timestamp: new Date().toISOString(),
  ci: process.env.CI === "true"
});

// Query metadata for tooling
const buildMetadata = service.node.metadata.find(m => m.type === "build:info");
if (buildMetadata) {
  console.log(`Deployed version: ${buildMetadata.data.version}`);
  console.log(`From commit: ${buildMetadata.data.commit}`);
}

// Security scanning can look for classification metadata
const securityMeta = service.node.metadata.filter(m => 
  m.type.startsWith("security:") || m.type.startsWith("compliance:")
);
console.log(`Found ${securityMeta.length} security/compliance metadata entries`);