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.
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 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"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 ignoredStructure 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[];
}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;
}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); // falseUsing 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`);