Skip to main content

Design Principles

SST is built around a few core design principles that make it perfect for growing teams.

Here we'll look at how SST can be both easy to get started and yet completely customizable.


Zero-config

One of the big reasons we built SST was because the development environment for serverless always felt lacking.

It lacked a tight feedback loop, something the Live Lambda Development addresses. But you also needed to configure multiple plugins, Webpack, Babel, TypeScript, testing frameworks, linters etc. These setups were often brittle and relied on separate project maintainers to keep them up to date.

This might've been fine if you are an individual developer who has a ton of experience with serverless. But if you are a part of a larger team or are just getting started, it can be very challenging just to get your dev environment up and running.

Hence, one of the design principles of SST is to make sure that it works out of the box. It comes batteries included, making it easy to get started.

You can learn more about this if you check out our guide.


Progressive disclosure

A big issue with building on AWS is that it is very complicated. It can be really intimidating to learn AWS. But SST's design makes this a lot easier.

SST's constructs are based on the idea of progressive disclosure. This means that the basic configuration for these constructs are simple, easy to understand, and readable. But they still allow you to progressively customize them for more complex use cases.

Let's look at two areas where we apply this idea.


Configuring constructs

The Api construct for example, in its simplest form only needs the routes and the function handlers as strings.

new Api(stack, "Api", {
routes: {
"GET /notes": "src/list.main",
"POST /notes": "src/create.main",
},
});

This format makes it easy to understand what is being defined. But if you wanted to customize the function properties, you could:

new Api(stack, "Api", {
defaults: {
function: {
timeout: 20,
bind: [table],
},
},
routes: {
"GET /notes": "list.main",
"POST /notes": "create.main",
},
});

We could go even further and specifically customize one of the routes.

new Api(stack, "Api", {
defaults: {
function: {
timeout: 20,
bind: [table],
},
},
routes: {
"GET /notes": {
function: {
handler: "src/list.main",
bind: [table],
},
},
"POST /notes": "create.main",
},
});

Finally, you can configure the props of the underlying CDK construct that the Api construct uses.

import { HttpApi } from "aws-cdk-lib/aws-apigatewayv2";

new Api(stack, "Api", {
cdk: {
httpStages: [
{
stageName: "dev",
autoDeploy: false,
},
],
},
});

Attaching permissions

A similar idea can be seen in the pattern SST uses for attaching permissions to functions.

Let's look at the Cron construct as an example.

const cron = new Cron(stack, "Cron", {
schedule: "rate(1 minute)",
job: "src/lambda.main",
});

For simplicity's sake we could give the cronjob function access to everything.

cron.attachPermissions(PermissionType.ALL);

Or just give it access to a type of resource.

cron.attachPermissions(["s3"]);

Or to a specific construct that we created.

const table = new Table(stack, "Table");

cron.attachPermissions(["s3", table]);

Or grant a specific permission for a construct.

const topic = new sns.Topic(stack, "Topic");
const table = new Table(stack, "Table");

cron.attachPermissions([
[topic, "grantPublish"],
[table, "grantReadData"],
]);

Or attach a list of granular IAM policy statements.

import { PolicyStatement, Effect } from "aws-cdk-lib/aws-iam";

cron.attachPermissions([
new PolicyStatement({
actions: ["execute-api:Invoke"],
effect: Effect.ALLOW,
resources: [`arn:aws:execute-api:${region}:${account}:${api.httpApiId}/*`],
}),
]);

This allows you to customize as you go along, as opposed to being overwhelmed by all the options up front.


Having an escape hatch

That said, you might run into cases where you are trying to do something that a construct does not support. In these cases, you can fallback to using the native CDK constructs instead.

This escape hatch ensures that you are not locked into using SST's constructs. This is one of the biggest reasons companies choose SST.

PS: If you have a use case that we should address, jump on our Discord and let us know!