Stack
The Stack construct extends cdk.Stack. It automatically prefixes the stack names with the stage and app name to ensure that they can be deployed to multiple regions in the same AWS account. It also ensure that the stack uses the same AWS profile and region as the app. They're defined using functions that return resources that can be imported by other stacks.
Examples
Creating a new stack
import { StackContext } from "sst/constructs";
export function MyStack({ stack }: StackContext) {
// Define your stack
}
Adding to an app
Add it to your app in sst.config.ts
.
stacks(app) {
app.stack(StackA).stack(StackB);
// Add more stacks
}
Here app
is an instance of App
.
Note that, setting the env for an individual stack is not allowed.
app.stack(MyStack, { env: { account: "1234", region: "us-east-1" } });
It will throw this error.
Error: Do not directly set the environment for a stack
This is by design. The stacks in SST are meant to be re-deployed for multiple stages (like Serverless Framework). And so they depend on the region and AWS profile that's passed in through the CLI. If a stack is hardcoded to be deployed to a specific account or region, it can break your deployment pipeline.
Configuring stack name
By default, the name of the CloudFormation stack is the stage name, app name, and the stack function name joined by -
, ie. stage-app-MyStack
.
You can override the stack function name by passing in id
. In this case, the CloudFormation stack name is stage-app-my-stack
.
stacks(app) {
app.stack(MyStack, { id: "my-stack" });
}
Alternatively, you can override the CloudFormation stack name directly by passing in stackName
.
stacks(app) {
app.stack(MyStack, { stackName: `${app.stage}-my-hello-stack` });
}
Note that, stackName
need to be parameterized with the stage name. This ensure an app can be deployed to multiple stages with unique stack names.
Sharing resources between stacks
Resources defined in a stack can be used by other stacks. This allows you to have granular stacks that contain only related resources.
Stack functions can return any resources they want to expose to other stacks.
import { StackContext } from "sst/constructs";
export function MyStack({ stack }: StackContext) {
const table = new Table(stack, "table");
return {
table,
};
}
Other stacks can import these resources by utilizing the use
function
import { StackContext, use } from "sst/constructs";
import { MyStack } from "./MyStack";
export function AnotherStack({ stack }: StackContext) {
const { table } = use(MyStack);
// Use table
}
Async stacks
Asynchronous calls are supported in stack functions but be careful when using this as you can introduce external state that makes your deployments less deterministic
Simple add an async
modifier to your function definition
import { StackContext } from "sst/constructs";
export async function MyStack({ stack }: StackContext) {
const foo = await someAsyncCall();
// Define stack
}
When initializing the stack, make sure you call await
async stacks(app) {
await app.stack(MyStack);
}
Accessing app properties
The stage, region, and app name can be accessed through the app object. In your stacks (for example, stacks/MyStack.js
) you can use.
function MyStack({ stack, app }: StackContext) {
app.stage;
app.region;
app.name;
}
You can use this to conditionally add stacks or resources to your app.
Specifying default function props
You can set some function props and have them apply to all the functions in a stack. This must be called before any functions have been added to the stack; so that all functions will be created with these defaults.
function MyStack({ stack }: StackContext) {
stack.setDefaultFunctionProps({
timeout: 20,
memorySize: 512,
runtime: "go1.x",
environment: { TABLE_NAME: "NOTES_TABLE" },
});
}
It'll also override any props set by the App's setDefaultFunctionProps
, while merging the environment
and permission
props.
Updating default function props
You can also use addDefaultFunctionPermissions
, addDefaultFunctionEnv
, and addDefaultFunctionLayers
to progressively add more permissions, environment variables, and layers to the defaults. These can be called multiple times and from anywhere.
However, they only affect the functions that are created after the call.
function MyStack({ stack }: StackContext) {
new Api(stack, "Api1", {
routes: {
"GET /": "src/hello.main",
},
});
stack.addDefaultFunctionEnv({
TABLE_NAME: "NOTES_TABLE",
});
stack.addDefaultFunctionPermissions(["s3"]);
stack.addDefaultFunctionLayers([mylayer]);
new Api(stack, "Api2", {
routes: {
"GET /": "src/world.main",
},
});
}
So in the above example, the addDefaultFunctionPermissions
and addDefaultFunctionEnv
calls will only impact the functions in Api2
.
Prefixing resource names
You can optionally prefix resource names to make sure they don't thrash when deployed to different stages in the same AWS account.
You can do so in your stacks.
scope.logicalPrefixedName("MyResource"); // Returns "dev-my-sst-app-MyResource"
This invokes the logicalPrefixedName
method in App
that your stack is added to. This'll return dev-my-sst-app-MyResource
, where dev
is the current stage and my-sst-app
is the name of the app.
Adding stack outputs
export function MyStack({ stack }: StackContext) {
const topic = new Topic(stack, "Topic");
const queue = new Queue(stack, "Queue");
stack.addOutputs({
TopicArn: topic.snsTopic.topicArn,
QueueArn: topic.sqsQueue.queueArn,
});
}
Adding stack exports
export function MyStack({ stack }: StackContext) {
const topic = new Topic(stack, "Topic");
stack.addOutputs({
TopicArn: {
value: topic.snsTopic.topicArn,
exportName: "MyTopicArn",
},
});
}
Accessing AWS account info
To access the AWS account and region your app is being deployed to, use the following in your Stack
instances.
stack.region;
stack.account;
The region here is the same as the one you can find in the app
instance in the constructor.
Constructor
new Stack(scope, id, props)
Parameters
- scope Construct
- id string
- props StackProps
Properties
An instance of Stack
has the following properties.
stage
Type : string
The current stage of the stack.
Methods
An instance of Stack
has the following methods.
addDefaultFunctionBinding
addDefaultFunctionBinding(bind)
Parameters
- bind Array<BindingResource>
Binds additional resources to be applied to all Lambda functions in the stack.
app.addDefaultFunctionBinding([STRIPE_KEY, bucket]);
addDefaultFunctionEnv
addDefaultFunctionEnv(environment)
Parameters
- environment Record<string, string>
Adds additional default environment variables to be applied to all Lambda functions in the stack.
stack.addDefaultFunctionEnv({
DYNAMO_TABLE: table.name
});
addDefaultFunctionLayers
addDefaultFunctionLayers(layers)
Parameters
- layers Array<ILayerVersion>
Adds additional default layers to be applied to all Lambda functions in the stack.
stack.addDefaultFunctionLayers(["arn:aws:lambda:us-east-1:123456789012:layer:nodejs:3"]);
addDefaultFunctionPermissions
addDefaultFunctionPermissions(permissions)
Parameters
- permissions Permissions
Adds additional default Permissions to be applied to all Lambda functions in the stack.
stack.addDefaultFunctionPermissions(["sqs", "s3"]);
addOutputs
addOutputs(outputs)
Parameters
- outputs Record<string, undefined | string | CfnOutputProps>
Add outputs to this stack
stack.addOutputs({
TableName: table.name,
});
stack.addOutputs({
TableName: {
value: table.name,
exportName: "MyTableName",
}
});
getAllFunctions
getAllFunctions()
Returns all the Function instances in this stack.
stack.getAllFunctions();
setDefaultFunctionProps
setDefaultFunctionProps(props)
Parameters
- props FunctionProps
The default function props to be applied to all the Lambda functions in the stack.
stack.setDefaultFunctionProps({
srcPath: "backend",
runtime: "nodejs18.x",
});