Skip to content

Examples

A collection of example apps for reference.

Below are a collection of example SST apps. These are available in the examples/ directory of the repo.

The descriptions for these examples are generated using the comments in the sst.config.ts of the app.

Contributing

To contribute an example or to edit one, submit a PR to the repo. Make sure to document the sst.config.ts in your example.


API Gateway auth

Enable IAM and JWT authorizers for API Gateway routes.

sst.config.ts
const api = new sst.aws.ApiGatewayV2("MyApi", {
domain: {
name: "api.ion.sst.sh",
path: "v1",
},
});
api.route("GET /", {
handler: "route.handler",
});
api.route("GET /foo", "route.handler", { auth: { iam: true } });
api.route("GET /bar", "route.handler", {
auth: {
jwt: {
issuer:
"https://cognito-idp.us-east-1.amazonaws.com/us-east-1_Rq4d8zILG",
audiences: ["user@example.com"],
},
},
});
api.route("$default", "route.handler");
return {
api: api.url,
};

View the full example.


Bucket notifications

Create an S3 bucket and subscribe to its events with a function.

sst.config.ts
const bucket = new sst.aws.Bucket("MyBucket");
bucket.subscribe("subscriber.handler", {
events: ["s3:ObjectCreated:*"],
});
return {
bucket: bucket.name,
};

View the full example.


DynamoDB streams

Create a DynamoDB table, enable streams, and subscribe to it with a function.

sst.config.ts
const table = new sst.aws.Dynamo("MyTable", {
fields: {
id: "string",
},
primaryIndex: { hashKey: "id" },
stream: "new-and-old-images",
});
table.subscribe("subscriber.handler", {
filters: [
{
dynamodb: {
NewImage: {
message: {
S: ["Hello"],
},
},
},
},
],
});
const app = new sst.aws.Function("MyApp", {
handler: "publisher.handler",
link: [table],
url: true,
});
return {
app: app.url,
table: table.name,
};

View the full example.


IAM permissions boundaries

Use permissions boundaries to set the maximum permissions for all IAM roles that’ll be created in your app.

In this example, the Function has the s3:ListAllMyBuckets and sqs:ListQueues permissions. However, we create a permissions boundary that only allows s3:ListAllMyBuckets. And we apply it to all Roles in the app using the global $transform.

As a result, the Function is only allowed to list S3 buckets. If you open the deployed URL, you’ll see that the SQS list call fails.

Learn more about AWS IAM permissions boundaries.

sst.config.ts
// Create a permission boundary
const permissionsBoundary = new aws.iam.Policy("MyPermissionsBoundary", {
policy: aws.iam.getPolicyDocumentOutput({
statements: [
{
actions: ["s3:ListAllMyBuckets"],
resources: ["*"],
},
],
}).json,
});
// Apply the boundary to all roles
$transform(aws.iam.Role, (args) => {
args.permissionsBoundary = permissionsBoundary;
});
// The boundary automatically applies to this Function's role
const app = new sst.aws.Function("MyApp", {
handler: "index.handler",
permissions: [
{
actions: ["s3:ListAllMyBuckets", "sqs:ListQueues"],
resources: ["*"],
},
],
url: true,
});
return {
app: app.url,
};

View the full example.


Current AWS account

You can use the aws.getXXXXOutput() provider functions to get info about the current AWS account. Learn more about provider functions.

sst.config.ts
return {
region: aws.getRegionOutput().name,
account: aws.getCallerIdentityOutput({}).accountId,
};

View the full example.


AWS monorepo

A full-stack TypeScript monorepo template that deploys a frontend, a database, and an API to AWS.

While, drop-in mode is great for simple projects, we recommend using this template for projects that are going to have multiple packages.

Project structure

The app is split into the separate packages/ and an infra/ directory.

my-sst-app
├─ sst.config.ts
├─ package.json
├─ packages
│  ├─ functions
│  ├─ frontend
│  ├─ scripts
│  └─ core
└─ infra

The packages/ directory includes the following:

  • core/

    This directory includes shared code that can be used by other packages. These are defined as modules. For example, we have an Example module.

    packages/core/src/example/index.ts
    export module Example {
    export function hello() {
    return "Hello, world!";
    }
    }

    We exports this using the following in the package.json:

    packages/core/package.json
    "exports": {
    "./*": [
    "./src/*/index.ts",
    "./src/*.ts"
    ]
    }

    This will allow us to import the Example module by doing:

    import { Example } from "@aws-monorepo/core/example";
    Example.hello();
  • functions/

    This directory includes our Lambda funcitons. It imports from the core/ package by using it as a local dependency.

  • frontend/

    This directory includes a simple Vite app. It references environment variables from our app and is started locally using.

    packages/frontend/package.json
    "scripts": {
    "dev": "sst dev vite dev"
    }
  • scripts/

    This directory includes scripts that you can run on your SST app using the sst shell CLI and tsx. For example, to the run the example scripts/src/example.ts, run the following from packages/scripts/.

    Terminal window
    npm run shell src/example.ts

Infrastructure

The infra/ directory allows you to logically split the infrastructure of your app into separate files. This can be helpful as your app grows.

In the template, we have an api.ts, database.ts, and frontend.ts. These export resources that can be used in the other infrastructure files.

They are also re-exported in the infra/index.ts file.

infra/index.ts
export * from "./api";
export * from "./database";
export * from "./frontend";

And to use them in your sst.config.ts we do an dynamic import. This ensures that the infrastructure is only created in the run function.

sst.config.ts
const infra = await import("./infra");
return {
api: infra.api.url,
};

View the full example.


Subscribe to queues

Create an SQS queue, subscribe to it, and publish to it from a function.

sst.config.ts
const queue = new sst.aws.Queue("MyQueue");
queue.subscribe("subscriber.handler");
const app = new sst.aws.Function("MyApp", {
handler: "publisher.handler",
link: [queue],
url: true,
});
return {
app: app.url,
queue: queue.url,
};

View the full example.


Router and function URL

Creates a router that routes all requests to a function with a URL.

sst.config.ts
const api = new sst.aws.Function("MyApi", {
handler: "api.handler",
url: true,
});
const router = new sst.aws.Router("MyRouter", {
domain: "router.ion.sst.sh",
routes: {
"/*": api.url,
},
});
return {
router: router.url,
};

View the full example.


Simple static site

Deploy a simple HTML file as a static site with S3 and CloudFront.

sst.config.ts
// Deploys the current directory as a static site
new sst.aws.StaticSite("MySite");

View the full example.


Swift on Lambda

Deploys a simple Swift application to Lambda using the al2023 runtime.

Check out the README in the repo for more details.

sst.config.ts
const swift = new sst.aws.Function("Swift", {
runtime: "provided.al2023",
architecture: process.arch === "arm64" ? "arm64" : "x86_64",
bundle: build("app"),
handler: "bootstrap",
url: true,
});
const router = new sst.aws.Router("SwiftRouter", {
routes: {
"/*": swift.url,
},
domain: "swift.dev.sst.dev",
});
return {
url: router.url,
};

View the full example.


Subscribe to topics

Create an SNS topic, publish to it from a function, and subscribe to it with a function and a queue.

sst.config.ts
const queue = new sst.aws.Queue("MyQueue");
queue.subscribe("subscriber.handler");
const topic = new sst.aws.SnsTopic("MyTopic");
topic.subscribe("subscriber.handler", {});
topic.subscribeQueue(queue.arn);
const app = new sst.aws.Function("MyApp", {
handler: "publisher.handler",
link: [topic],
url: true,
});
return {
app: app.url,
topic: topic.name,
};

View the full example.


Store and search for vector data using the Vector component. Includes a seeder API that uses an LLM to generate embeddings for some movies and optionally their posters.

Once seeded, you can call the search API to query the vector database.

sst.config.ts
const OpenAiApiKey = new sst.Secret("OpenAiApiKey");
const vector = new sst.aws.Vector("MyVectorDB", {
dimension: 1536,
});
const seeder = new sst.aws.Function("Seeder", {
handler: "index.seeder",
link: [OpenAiApiKey, vector],
copyFiles: [
{ from: "iron-man.jpg", to: "iron-man.jpg" },
{
from: "black-widow.jpg",
to: "black-widow.jpg",
},
{
from: "spider-man.jpg",
to: "spider-man.jpg",
},
{ from: "thor.jpg", to: "thor.jpg" },
{
from: "captain-america.jpg",
to: "captain-america.jpg",
},
],
url: true,
});
const app = new sst.aws.Function("MyApp", {
handler: "index.app",
link: [OpenAiApiKey, vector],
url: true,
});
return { seeder: seeder.url, app: app.url };

View the full example.


React SPA with Vite

Deploy a React single-page app (SPA) with Vite to S3 and CloudFront.

sst.config.ts
new sst.aws.StaticSite("Web", {
build: {
command: "pnpm run build",
output: "dist",
},
});

View the full example.


Cloudflare KV

This example creates a Cloudflare KV namespace and links it to a worker. Now you can use the SDK to interact with the KV namespace in your worker.

sst.config.ts
const storage = new sst.cloudflare.Kv("MyStorage");
const worker = new sst.cloudflare.Worker("Worker", {
url: true,
link: [storage],
handler: "index.ts",
});
return {
url: worker.url,
};

View the full example.


You might have multiple secrets that need to be used across your app. It can be tedious to create a new secret and link it to each function or resource.

A common pattern to addresses this is to create an object with all your secrets and then link them all at once. Now when you have a new secret, you can add it to the object and it will be automatically available to all your resources.

sst.config.ts
// Manage all secrets together
const secrets = {
secret1: new sst.Secret("Secret1", "some-secret-value-1"),
secret2: new sst.Secret("Secret2", "some-secret-value-2"),
};
const allSecrets = Object.values(secrets);
const bucket = new sst.aws.Bucket("MyBucket");
const api = new sst.aws.Function("MyApi", {
link: [bucket, ...allSecrets],
handler: "index.handler",
url: true,
});
return {
url: api.url,
};

View the full example.


Default function props

Set default props for all the functions in your app using the global $transform.

sst.config.ts
$transform(sst.aws.Function, (args) => {
args.runtime = "nodejs14.x";
args.environment = {
FOO: "BAR",
};
});
new sst.aws.Function("MyFunction", {
handler: "index.ts",
});

View the full example.


Vercel domains

Creates a router that uses domains purchased through and hosted in your Vercel account. Ensure the VERCEL_API_TOKEN and VERCEL_TEAM_ID environment variables are set.

sst.config.ts
const router = new sst.aws.Router("MyRouter", {
domain: {
name: "ion.sst.moe",
dns: sst.vercel.dns({ domain: "sst.moe" }),
},
routes: {
"/*": "https://sst.dev",
},
});
return {
router: router.url,
};

View the full example.