SST - Theory, Architecture & Deep Dive

29.11.2023Tom Trapp
Tech Serverless sst Amazon Web Services Function as a Service Pay As You Go

banner.png Source: https://sst.dev/

What is SST?

Serverless Stack (SST) is an open-source framework that allows developers to quickly and efficiently create and manage Serverless applications on AWS. SST supports Live Lambda Development, meaning changes to the code are immediately reflected in the cloud without the need for manual deployment. The framework integrates seamlessly with AWS services and offers features such as environment variables, permissions, and authentication to simplify the development of Serverless applications.

Under the hood, AWS CDK and ultimately CloudFormation are used. Therefore, it’s not surprising that SST only supports AWS as a cloud provider. In simple terms, SST can be described as an abstraction of an abstraction. Hence, SST is supposed to be particularly easy to use. They heavily advertise their Live Lambda Development, their Web Dashboard, and generally the simple deployment cycle.

As a short interjection, fortunately, there is a Fireship video that explains SST very well.

Since SST is quite comprehensive, we will skip hands-on examples this time. But, stay tuned for a full SST hands-on experience in the next TechUp! 🤓

Infrastructure with Code?

SST is an Infrastructure with Code framework that uses TypeScript to create and manage numerous resources from the Serverless world. Generally, SST follows the imperative approach, thus Infrastructure with Code.

A quick clarification of the differences between IaC and IwC, thanks to ChatGPT for the explanation:

“Infrastructure as Code” (IaC) and “Infrastructure with Code” are two approaches to managing and deploying infrastructure, differing in their methodology and focus.

Infrastructure as Code (IaC):

Definition:

IaC defines and manages IT infrastructure in code form, automating the entire lifespan of the infrastructure - from creation to update to deletion.

Features:

  • Declarative: IaC descriptions set the desired end state of the infrastructure. Tools then ensure that the current infrastructure converges to this state.
  • Version control: Since the infrastructure is described in code, it can be stored in version control systems like Git.
  • Idempotence: Applying IaC repeatedly results in the same outcome. > Examples: Terraform, Ansible, CloudFormation, SAM, Serverless Framework.

Infrastructure with Code:

Definition:

“Infrastructure with Code” refers to approaches where programming logic and code are used to dynamically define and adjust infrastructure.

Features:

  • Imperative: The developer specifies how something is achieved, e.g., by writing loops, conditions, and more logic.
  • Flexibility: Allows developers to define complex logics and processes.
  • Language-centered: Often uses a common programming language (e.g., Python, JavaScript) instead of learning a domain-specific language (DSL). > Examples: AWS CDK (Cloud Development Kit), Pulumi, SST.

Which type of tool to use depends much on subjective factors and the use case. For example, an imperative approach may be more suitable in the context of an API-First Self-Service approach since the code can be directly implemented in the REST endpoint.

Back to SST! 🚀

Architecture

Prerequisites

To use SST, you need NodeJS and NPM, as it’s a TypeScript framework. Since SST only supports AWS as a cloud provider, you also need an AWS account including CLI access.

Components

Now, let’s get to know the building blocks of SST and understand how they are connected.

An SST project generally consists of the following components:

  • Stacks

    • Stacks are the actual CloudFormation stacks generated by SST, containing the infrastructure.
    • A stack consists of several constructs.
    • A project can contain multiple stacks, not just one.
  • Constructs

    • Constructs are the building blocks forming the infrastructure.
    • They are the actual CloudFormation resources that SST generates and abstracts.
    • A construct can represent a simple AWS resource or a combination of several AWS resources.
    • Constructs can be, for example, a simple API, an RDS database, or a complete Serverless application.
    • Constructs are imported directly into the stacks for use.
    • It’s sometimes not entirely clear which AWS resources are behind a construct due to the high level of abstraction.
    • All constructs can be found here.
  • Packages/Functions

    • Packages contain the functions to be deployed in the infrastructure, i.e., the Lambda functions.
    • Ultimately, this is also a construct that includes a Lambda function.
    • SST supports JavaScript, TypeScript, Python, Go, C#, Rust, and Container runtimes.
    • SST defines certain default values, such as 1024 MB memory.
    • An example of four different functions could be the four CRUD commands needed in an API.
  • Packages/Core

    • The Core Packages contain the business logic used in the functions.
    • The Core package can be seen as a type of library. This allows modularization between Lambda functions.
    • In the previous example with the four CRUD functions, for instance, the database connection could be implemented here.
  • Clients

    • Clients allow us to reference resources from our stacks.
    • They are referred to by SST as typesafe Node.js clients for your AWS infrastructure.
    • Clients can read properties of resources, like a bucket name or an API URL.
    • With clients, we can also use handlers, which we can wrap around our Lambda functions. This provides us with type safety and initializes an SST context, allowing us to use hooks.
    • With hooks, we can call certain methods on the current request of our clients. This way, we can use the currently logged-in user, the entire session, the request JSON body, cookies, or query parameters in our core package.
    • All clients can be found here.

Now that we’ve learned about the most important building blocks, let’s take a closer look at the architecture of SST.

Detailed Architecture

The following graphic exemplifies the interaction between the various architectural components:

sst_architecture.png

Development

SST enjoys support in popular IDEs like VS Code, IntelliJ, and WebStorm. To start an SST project, you can choose from different templates or start in standalone mode.

It’s immediately noticeable that everything is in one repository, SST adopts a so-called Mono-Repo approach. SST offers several very useful tools intended to simplify the development of Serverless applications. Here, SST stands out from other frameworks by offering features not found in other frameworks.

Live Lambda Development

With Live Lambda Development, we can develop our Lambda functions locally and control them using our ‘real’ AWS resources. This allows us to test something locally while, for example, using the ‘real’ AWS API Gateway.

Wait, how is that possible? 🤔

Technically, a proxying of requests to the local Lambda function is performed. This allows us to test and debug our functions in less than 10 milliseconds using Live Reload. Specifically, SST uses the AWS service AWS IOT over WebSocket to direct the requests to our Lambda functions. This offers several advantages, including:

  • Local development and debugging
  • Live Reload
  • Serverless Approach, PAYG
  • No local mocking necessary

Live Lambda is supported by all common languages, unfortunately, there is currently only community support for JavaScript, TypeScript, and Python to set breakpoints.

Another stumbling block could be that the local Live Lambda instance cannot connect to a VPC. For this, one would have to set up their own VPN. Alternatively, one could directly connect to a local RDS database in the code; the IS_LOCAL environment variable surely helps here.

Side Note: This approach is reminiscent of the approaches of Telepresence, more on this in my TechUp about Telepresence.

Find more info on Live Lambda here.

If you can’t wait for our next TechUp, here is a short demo.

Testing

Another feature of SST is testing. Generally, there are three types of tests.

Domain Tests

With Domain Tests, we can test the business logic. We check whether a Create works correctly and then read all entries to see if the entry was correctly created. Vitest is used in the background for the unit tests.

API Tests

Similar to a domain test of a Lambda function, we test whether a certain input returns a certain output. However, we do not call the Lambda function directly but use the API Gateway to test our Lambda functions. This behavior is very close to the actual user behavior, making this type of test very valuable!

Stack Tests

We can not only test our business logic but also our infrastructure. With Stack Tests, we can test our infrastructure, for example, whether a certain bucket exists, or whether a specific API route exists. This is especially useful for updates or similar to test whether, for example, default values are still correctly set. Very cool! 🤩

Console

SST offers a web console that provides an overview of our stacks and their resources: https://docs.sst.dev/console.

Here, SST hits right on the developer’s need by centrally collecting and displaying logs, metrics, and general info about the project. It’s very helpful that sst console in combination with sst dev, the command for local development, also shows the local log outputs in the console.

It should be noted that the sst-console currently seems to be compatible only with the AWS region eu-east-1.

Deployment

First, we need to build our entire project with sst build.

There’s nothing special to say about the deployment; the project is deployed via sst deploy. Here, you can also directly specify a suitable profile to which the entire project should be deployed. The profile specifically refers to the AWS account that is stored in the CLI credentials. This way, for example, a test account can be supplied with the corresponding resources.

Using sst diff, you can compare the local state with the deployed state in the AWS account in advance. Of course, a complete CI/CD process including the execution of tests etc., is also recommended here.

SEED

At first glance, Seed really looks like it’s the ArgoCD for SST. Here, the focus is on working on a Serverless application with the entire team. Seed itself also advertises features like Incremental Deployments, Zero Config Pipelines, and Real-Time Lambda Alertings.

We won’t delve further into Seed here and might take a closer look at Seed in a separate TechUp! 🔍

Examples

Now that we’ve learned the basic building blocks and features of SST, we want to deepen this knowledge with some examples. As mentioned before, hands-on examples will be provided in the next TechUp, where we’ll set up and deploy our own SST project.

Simple API

Example: https://github.com/sst/sst/tree/master/examples/rest-api

In this example, we immediately see the project structure we’ve seen in the Architecture chapter. In the Stack folder, the complete SST stack is defined in the file ExampleStack.ts, here the API construct, which includes an API Gateway and a Lambda function, is imported. Then, it’s defined which values the stack should output. This is very useful because we directly receive our API URL.

In the Package/Function folder, the actual Lambda function is defined, here the Core package is imported, which contains our business logic. Specifically, we see here that three Lambda functions are defined, one for each HTTP method.

Under Core, the data storage is located. In this simple example, an array is used. Of course, in a real-world use-case, a database would be addressed here. It’s worth mentioning that the simple example project internally consists of three NPM projects, each with its own package.json including dependency management.

Svelte App

SST directly delivers ready-made constructs to deploy different frontend frameworks like Next.js, Svelte, Remix, Astro, etc.

Example: https://github.com/sst/sst/tree/master/examples/quickstart-sveltekit

In this example, we see how we can deploy a Svelte App with SST. It’s immediately noticeable that there’s no Stack folder; the stack is defined directly when binding in the sst.config.ts. Here, we see that the constructs Bucket, SvelteKitSite, and Cron are used.

Under src, we find the complete Svelte project, including the Lambda function for the CronJob.

Pub/Sub

Example: https://github.com/sst/sst/tree/master/examples/pub-sub

In this example, we see how we can implement a Pub/Sub approach with SST. An SNS Topic is used underneath, which forwards the messages to an SQS Queue. In our stack, we directly define the handler functions to be called when a message arrives when creating the SNS Topic. It’s also very useful that we can directly connect our API to the SNS Topic, automatically sending our REST payloads into the SNS Topic.

Conclusion

Go SST! 🚀

Compared to other Serverless Development & Deployment tools, SST seems very comprehensive and mature. The approach of abstracting AWS resources again appeals to me personally, as well as the fact that constructs sometimes comprise several AWS resources. Also, the use of proxying instead of mocking for Lambda development definitely appeals to me.

In general, SST attempts to make Serverless on AWS without really having to know AWS. This makes it very easy for developers to leverage the advantages of Serverless without significant development effort. Additionally, one can be sure that, for example, best practices and default values are sensibly set and can be used in most cases as they are.

In the next TechUp on SST, we’ll get our hands dirty and create our own SST project, stay tuned! 🤓

Tom Trapp

Tom Trapp – Problemlöser, Innovator, Sportler. Am liebsten feilt Tom den ganzen Tag an der moderner Software und legt viel Wert auf objektiv sauberen, leanen Code.