Skip to content

Usage

The ExpressStack extends the cdk.Stack class and has a very similar signature, only taking an extra stage parameter. There are multiple ways to build your pipeline, it involves creating the Pipeline, adding Waves, Stages and Stacks to your Stages and then calling .synth() on the Pipeline.

First, define your stacks by extending ExpressStack:

class StackA extends ExpressStack {
constructor(scope: Construct, id: string, stage: ExpressStage, stackProps?: StackProps) {
super(scope, id, stage, stackProps);
new cdk.aws_sns.Topic(this, 'MyTopic');
// ... more resources
}
}
class StackB extends ExpressStack {
//... similar to StackA
}
class StackC extends ExpressStack {
//... similar to StackA
}

It is possible to also extend the ExpressStack classes and customize its behavior, if required. The example below adds My to each of the stack IDs and allows for other logic that might be needed.

class MyExpressBaseStack extends ExpressStack {
constructor(scope: Construct, id: string, stage: MyExpressStage, stackProps?: StackProps) {
super(scope, 'My' + id, stage, stackProps);
// Custom logic can be added here
}
}

Then use this base class to define your stacks:

class StackA extends MyExpressBaseStack {
constructor(scope: Construct, id: string, stage: MyExpressStage, stackProps?: StackProps) {
super(scope, id, stage, stackProps);
new cdk.aws_sns.Topic(this, 'MyTopic');
// ... more resources
}
}

There are three main patterns for defining your pipeline. Choose the one that best fits your project structure and preferences.

This is the most straightforward approach using the builder pattern:

bin/your-app.ts
const app = new App();
const expressPipeline = new CdkExpressPipeline();
// === Wave 1 ===
const wave1 = expressPipeline.addWave('Wave1');
// --- Wave 1, Stage 1---
const wave1Stage1 = wave1.addStage('Stage1');
const stackA = new StackA(app, 'StackA', wave1Stage1);
const stackB = new StackB(app, 'StackB', wave1Stage1);
stackB.addExpressDependency(stackA);
// === Wave 2 ===
const wave2 = expressPipeline.addWave('Wave2');
// --- Wave 2, Stage 1---
const wave2Stage1 = wave2.addStage('Stage1');
new StackC(app, 'StackC', wave2Stage1);
expressPipeline.synth([
wave1,
wave2,
]);

For more object-oriented approach, you can nest stacks within stage classes:

lib/wave1/index.ts
class Wave1 extends ExpressWave {
constructor() {
super('Wave1');
}
}
lib/wave1/stage1/index.ts
class Wave1Stage1 extends ExpressStage {
constructor(wave1: Wave1) {
super('Stage1', wave1);
const stackA = new StackA(app, 'StackA', this);
const stackB = new StackB(app, 'StackB', this);
stackB.addExpressDependency(stackA);
}
}
lib/wave2/index.ts
class Wave2 extends ExpressWave {
constructor() {
super('Wave2');
}
}
lib/wave2/stage1/index.ts
class Wave2Stage1 extends ExpressStage {
constructor(wave2: Wave2) {
super('Stage1', wave2);
new StackC(app, 'StackC', this);
}
}
bin/your-app.ts
const app = new App();
const expressPipeline = new CdkExpressPipeline();
const wave1 = new Wave1();
new Wave1Stage1(wave1);
const wave2 = new Wave2();
new Wave2Stage1(wave2);
expressPipeline.synth([wave1, wave2]);

Use addExpressDependency() to define dependencies between stacks:

const stackA = new StackA(app, 'StackA', stage);
const stackB = new StackB(app, 'StackB', stage);
stackB.addExpressDependency(stackA); // StackB depends on StackA

Stacks IDs follow the pattern: {Wave}_{Stage}_{Stack}

  • Wave1_Stage1_StackA
  • Wave1_Stage1_StackB
  • Wave2_Stage1_StackC

This naming convention enables selective deployment, see Stack IDs, Names & Selection.

Stack names are never changed. In the example above, the stack names will be StackA, StackB, and StackC as seen in CloudFormation.