diff --git a/examples/aws-bun-elysia/sst.config.ts b/examples/aws-bun-elysia/sst.config.ts index ee5179f85..6d2c73603 100644 --- a/examples/aws-bun-elysia/sst.config.ts +++ b/examples/aws-bun-elysia/sst.config.ts @@ -35,7 +35,7 @@ * This example lets you upload a file to S3 and then download it. * * ```bash - * curl --F file=@elysia.png http://localhost:3000/ + * curl -F file=@elysia.png http://localhost:3000/ * curl http://localhost:3000/latest * ``` * diff --git a/examples/aws-bun-file-upload/.dockerignore b/examples/aws-bun-file-upload/.dockerignore new file mode 100644 index 000000000..490ef0c2a --- /dev/null +++ b/examples/aws-bun-file-upload/.dockerignore @@ -0,0 +1,5 @@ +node_modules +.git +.gitignore +README.md +Dockerfile* diff --git a/examples/aws-bun-file-upload/.gitignore b/examples/aws-bun-file-upload/.gitignore new file mode 100644 index 000000000..1426860d4 --- /dev/null +++ b/examples/aws-bun-file-upload/.gitignore @@ -0,0 +1,178 @@ +# Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore + +# Logs + +logs +_.log +npm-debug.log_ +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Caches + +.cache + +# Diagnostic reports (https://nodejs.org/api/report.html) + +report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json + +# Runtime data + +pids +_.pid +_.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover + +lib-cov + +# Coverage directory used by tools like istanbul + +coverage +*.lcov + +# nyc test coverage + +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) + +.grunt + +# Bower dependency directory (https://bower.io/) + +bower_components + +# node-waf configuration + +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) + +build/Release + +# Dependency directories + +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) + +web_modules/ + +# TypeScript cache + +*.tsbuildinfo + +# Optional npm cache directory + +.npm + +# Optional eslint cache + +.eslintcache + +# Optional stylelint cache + +.stylelintcache + +# Microbundle cache + +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history + +.node_repl_history + +# Output of 'npm pack' + +*.tgz + +# Yarn Integrity file + +.yarn-integrity + +# dotenv environment variable files + +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) + +.parcel-cache + +# Next.js build output + +.next +out + +# Nuxt.js build / generate output + +.nuxt +dist + +# Gatsby files + +# Comment in the public line in if your project uses Gatsby and not Next.js + +# https://nextjs.org/blog/next-9-1#public-directory-support + +# public + +# vuepress build output + +.vuepress/dist + +# vuepress v2.x temp and cache directory + +.temp + +# Docusaurus cache and generated files + +.docusaurus + +# Serverless directories + +.serverless/ + +# FuseBox cache + +.fusebox/ + +# DynamoDB Local files + +.dynamodb/ + +# TernJS port file + +.tern-port + +# Stores VSCode versions used for testing VSCode extensions + +.vscode-test + +# yarn v2 + +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + +# IntelliJ based IDEs +.idea + +# Finder (MacOS) folder config +.DS_Store + +# sst +.sst diff --git a/examples/aws-bun-file-upload/Dockerfile b/examples/aws-bun-file-upload/Dockerfile new file mode 100644 index 000000000..481eb996c --- /dev/null +++ b/examples/aws-bun-file-upload/Dockerfile @@ -0,0 +1,40 @@ +# https://bun.sh/guides/ecosystem/docker + +# use the official Bun image +# see all versions at https://hub.docker.com/r/oven/bun/tags +FROM oven/bun:1 AS base +WORKDIR /usr/src/app + +# install dependencies into temp directory +# this will cache them and speed up future builds +FROM base AS install +RUN mkdir -p /temp/dev +COPY package.json bun.lockb /temp/dev/ +RUN cd /temp/dev && bun install --frozen-lockfile + +# install with --production (exclude devDependencies) +RUN mkdir -p /temp/prod +COPY package.json bun.lockb /temp/prod/ +RUN cd /temp/prod && bun install --frozen-lockfile --production + +# copy node_modules from temp directory +# then copy all (non-ignored) project files into the image +FROM base AS prerelease +COPY --from=install /temp/dev/node_modules node_modules +COPY . . + +# [optional] tests & build +ENV NODE_ENV=production +# RUN bun test +RUN bun run build + +# copy production dependencies and source code into final image +FROM base AS release +COPY --from=install /temp/prod/node_modules node_modules +COPY --from=prerelease /usr/src/app/index.ts . +COPY --from=prerelease /usr/src/app/package.json . + +# run the app +USER bun +EXPOSE 3000/tcp +ENTRYPOINT [ "bun", "run", "index.ts" ] diff --git a/examples/aws-bun-file-upload/README.md b/examples/aws-bun-file-upload/README.md new file mode 100644 index 000000000..41958527f --- /dev/null +++ b/examples/aws-bun-file-upload/README.md @@ -0,0 +1,15 @@ +# aws-bun + +To install dependencies: + +```bash +bun install +``` + +To run: + +```bash +bun run index.ts +``` + +This project was created using `bun init` in bun v1.1.29. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime. diff --git a/examples/aws-bun-file-upload/bun.lockb b/examples/aws-bun-file-upload/bun.lockb new file mode 100755 index 000000000..d9d29ed93 Binary files /dev/null and b/examples/aws-bun-file-upload/bun.lockb differ diff --git a/examples/aws-bun-file-upload/index.ts b/examples/aws-bun-file-upload/index.ts new file mode 100644 index 000000000..5a1cd5b7a --- /dev/null +++ b/examples/aws-bun-file-upload/index.ts @@ -0,0 +1,58 @@ +import { Resource } from "sst"; +import { + S3Client, + GetObjectCommand, + ListObjectsV2Command, +} from "@aws-sdk/client-s3"; +import { Upload } from "@aws-sdk/lib-storage"; +import { getSignedUrl } from "@aws-sdk/s3-request-presigner"; + +const s3 = new S3Client(); + +const server = Bun.serve({ + async fetch(req) { + const url = new URL(req.url); + + if (url.pathname === "/" && req.method === "GET") { + return new Response("Hello World!"); + } + + if (url.pathname === "/" && req.method === "POST") { + const formData = await req.formData(); + const file = formData.get("file")! as File; + const params = { + Bucket: Resource.MyBucket.name, + Key: file.name, + Body: file, + }; + const upload = new Upload({ + params, + client: s3, + }); + await upload.done(); + + return new Response("File uploaded successfully."); + } + + if (url.pathname === "/latest" && req.method === "GET") { + const objects = await s3.send( + new ListObjectsV2Command({ + Bucket: Resource.MyBucket.name, + }), + ); + const latestFile = objects.Contents!.sort( + (a, b) => + (b.LastModified?.getTime() ?? 0) - (a.LastModified?.getTime() ?? 0), + )[0]; + const command = new GetObjectCommand({ + Key: latestFile.Key, + Bucket: Resource.MyBucket.name, + }); + return Response.redirect(await getSignedUrl(s3, command)); + } + + return new Response("404!"); + }, +}); + +console.log(`Listening on ${server.url}`); diff --git a/examples/aws-bun-file-upload/package.json b/examples/aws-bun-file-upload/package.json new file mode 100644 index 000000000..07cda1990 --- /dev/null +++ b/examples/aws-bun-file-upload/package.json @@ -0,0 +1,22 @@ +{ + "name": "aws-bun-file-upload", + "module": "index.ts", + "type": "module", + "scripts": { + "dev": "bun run --watch index.ts", + "build": "bun build --target bun index.ts" + }, + "devDependencies": { + "@types/aws-lambda": "8.10.145", + "@types/bun": "latest" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "dependencies": { + "@aws-sdk/client-s3": "^3.658.1", + "@aws-sdk/lib-storage": "^3.658.1", + "@aws-sdk/s3-request-presigner": "^3.658.1", + "sst": "latest" + } +} diff --git a/examples/aws-bun-file-upload/sst-env.d.ts b/examples/aws-bun-file-upload/sst-env.d.ts new file mode 100644 index 000000000..99616cc2b --- /dev/null +++ b/examples/aws-bun-file-upload/sst-env.d.ts @@ -0,0 +1,21 @@ +/* This file is auto-generated by SST. Do not edit. */ +/* tslint:disable */ +/* eslint-disable */ +import "sst" +export {} +declare module "sst" { + export interface Resource { + "MyBucket": { + "name": string + "type": "sst.aws.Bucket" + } + "MyService": { + "service": string + "type": "sst.aws.Service" + "url": string + } + "MyVpc": { + "type": "sst.aws.Vpc" + } + } +} diff --git a/examples/aws-bun-file-upload/sst.config.ts b/examples/aws-bun-file-upload/sst.config.ts new file mode 100644 index 000000000..708fb5d40 --- /dev/null +++ b/examples/aws-bun-file-upload/sst.config.ts @@ -0,0 +1,68 @@ +/// + +/** + * ## AWS Bun file upload + * + * Deploys an Bun app to AWS. + * + * You can get started by running. + * + * ```bash + * mkdir aws-bun-file-upload && cd aws-bun-file-upload + * bun init -y + * bunx sst init + * ``` + * + * Now you can add a service. + * + * ```ts title="sst.config.ts" + * cluster.addService("MyService", { + * public: { + * ports: [{ listen: "80/http", forward: "3000/http" }], + * }, + * dev: { + * command: "bun dev", + * }, + * link: [bucket], + * }); + * ``` + * + * Start your app locally. + * + * ```bash + * bun sst dev + * ``` + * + * This example lets you upload a file to S3 and then download it. + * + * ```bash + * curl -F file=@package.json http://localhost:3000/ + * curl http://localhost:3000/latest + * ``` + * + * Finally, you can deploy it using `bun sst deploy --stage production`. + */ +export default $config({ + app(input) { + return { + name: "aws-bun-file-upload", + removal: input?.stage === "production" ? "retain" : "remove", + home: "aws", + }; + }, + async run() { + const bucket = new sst.aws.Bucket("MyBucket"); + const vpc = new sst.aws.Vpc("MyVpc"); + + const cluster = new sst.aws.Cluster("MyCluster", { vpc }); + cluster.addService("MyService", { + public: { + ports: [{ listen: "80/http", forward: "3000/http" }], + }, + dev: { + command: "bun dev", + }, + link: [bucket], + }); + }, +}); diff --git a/examples/aws-bun-file-upload/tsconfig.json b/examples/aws-bun-file-upload/tsconfig.json new file mode 100644 index 000000000..238655f2c --- /dev/null +++ b/examples/aws-bun-file-upload/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + // Enable latest features + "lib": ["ESNext", "DOM"], + "target": "ESNext", + "module": "ESNext", + "moduleDetection": "force", + "jsx": "react-jsx", + "allowJs": true, + + // Bundler mode + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "noEmit": true, + + // Best practices + "strict": true, + "skipLibCheck": true, + "noFallthroughCasesInSwitch": true, + + // Some stricter flags (disabled by default) + "noUnusedLocals": false, + "noUnusedParameters": false, + "noPropertyAccessFromIndexSignature": false + } +} diff --git a/examples/aws-bun/.dockerignore b/examples/aws-bun/.dockerignore index 490ef0c2a..8c3bacfca 100644 --- a/examples/aws-bun/.dockerignore +++ b/examples/aws-bun/.dockerignore @@ -3,3 +3,7 @@ node_modules .gitignore README.md Dockerfile* + + +# sst +.sst \ No newline at end of file diff --git a/examples/aws-bun/Dockerfile b/examples/aws-bun/Dockerfile index 481eb996c..a50d40e26 100644 --- a/examples/aws-bun/Dockerfile +++ b/examples/aws-bun/Dockerfile @@ -1,5 +1,3 @@ -# https://bun.sh/guides/ecosystem/docker - # use the official Bun image # see all versions at https://hub.docker.com/r/oven/bun/tags FROM oven/bun:1 AS base diff --git a/examples/aws-bun/bun.lockb b/examples/aws-bun/bun.lockb index d9d29ed93..4aeeb5e58 100755 Binary files a/examples/aws-bun/bun.lockb and b/examples/aws-bun/bun.lockb differ diff --git a/examples/aws-bun/index.ts b/examples/aws-bun/index.ts index 5a1cd5b7a..0168d4522 100644 --- a/examples/aws-bun/index.ts +++ b/examples/aws-bun/index.ts @@ -1,54 +1,25 @@ import { Resource } from "sst"; -import { - S3Client, - GetObjectCommand, - ListObjectsV2Command, -} from "@aws-sdk/client-s3"; -import { Upload } from "@aws-sdk/lib-storage"; -import { getSignedUrl } from "@aws-sdk/s3-request-presigner"; - -const s3 = new S3Client(); +import { Cluster } from "ioredis"; + +const redis = new Cluster( + [{ host: Resource.MyRedis.host, port: Resource.MyRedis.port }], + { + dnsLookup: (address, callback) => callback(null, address), + redisOptions: { + tls: {}, + username: Resource.MyRedis.username, + password: Resource.MyRedis.password, + }, + } +); const server = Bun.serve({ async fetch(req) { const url = new URL(req.url); if (url.pathname === "/" && req.method === "GET") { - return new Response("Hello World!"); - } - - if (url.pathname === "/" && req.method === "POST") { - const formData = await req.formData(); - const file = formData.get("file")! as File; - const params = { - Bucket: Resource.MyBucket.name, - Key: file.name, - Body: file, - }; - const upload = new Upload({ - params, - client: s3, - }); - await upload.done(); - - return new Response("File uploaded successfully."); - } - - if (url.pathname === "/latest" && req.method === "GET") { - const objects = await s3.send( - new ListObjectsV2Command({ - Bucket: Resource.MyBucket.name, - }), - ); - const latestFile = objects.Contents!.sort( - (a, b) => - (b.LastModified?.getTime() ?? 0) - (a.LastModified?.getTime() ?? 0), - )[0]; - const command = new GetObjectCommand({ - Key: latestFile.Key, - Bucket: Resource.MyBucket.name, - }); - return Response.redirect(await getSignedUrl(s3, command)); + const counter = await redis.incr("counter"); + return new Response(`Hit counter: ${counter}`); } return new Response("404!"); diff --git a/examples/aws-bun/package.json b/examples/aws-bun/package.json index 1059398f7..5d47973af 100644 --- a/examples/aws-bun/package.json +++ b/examples/aws-bun/package.json @@ -3,8 +3,8 @@ "module": "index.ts", "type": "module", "scripts": { - "dev": "bun run --watch index.ts", - "build": "bun build --target bun index.ts" + "build": "bun build --target bun index.ts", + "dev": "bun run --watch index.ts" }, "devDependencies": { "@types/aws-lambda": "8.10.145", @@ -14,9 +14,7 @@ "typescript": "^5.0.0" }, "dependencies": { - "@aws-sdk/client-s3": "^3.658.1", - "@aws-sdk/lib-storage": "^3.658.1", - "@aws-sdk/s3-request-presigner": "^3.658.1", - "sst": "latest" + "ioredis": "^5.4.1", + "sst": "3.1.65" } } diff --git a/examples/aws-bun/sst-env.d.ts b/examples/aws-bun/sst-env.d.ts index 99616cc2b..261f14f5b 100644 --- a/examples/aws-bun/sst-env.d.ts +++ b/examples/aws-bun/sst-env.d.ts @@ -5,9 +5,12 @@ import "sst" export {} declare module "sst" { export interface Resource { - "MyBucket": { - "name": string - "type": "sst.aws.Bucket" + "MyRedis": { + "host": string + "password": string + "port": number + "type": "sst.aws.Redis" + "username": string } "MyService": { "service": string @@ -15,6 +18,7 @@ declare module "sst" { "url": string } "MyVpc": { + "bastion": string "type": "sst.aws.Vpc" } } diff --git a/examples/aws-bun/sst.config.ts b/examples/aws-bun/sst.config.ts index 8f428f9db..15431c0c4 100644 --- a/examples/aws-bun/sst.config.ts +++ b/examples/aws-bun/sst.config.ts @@ -9,18 +9,18 @@ export default $config({ }; }, async run() { - const bucket = new sst.aws.Bucket("MyBucket"); - const vpc = new sst.aws.Vpc("MyVpc"); - + const vpc = new sst.aws.Vpc("MyVpc", { bastion: true }); + const redis = new sst.aws.Redis("MyRedis", { vpc }); const cluster = new sst.aws.Cluster("MyCluster", { vpc }); + cluster.addService("MyService", { + link: [redis], public: { ports: [{ listen: "80/http", forward: "3000/http" }], }, dev: { command: "bun dev", }, - link: [bucket], }); - }, + } }); diff --git a/examples/aws-express-file-upload/package.json b/examples/aws-express-file-upload/package.json index 9cf78d523..097dbf1d3 100644 --- a/examples/aws-express-file-upload/package.json +++ b/examples/aws-express-file-upload/package.json @@ -1,5 +1,5 @@ { - "name": "aws-express", + "name": "aws-express-file-upload", "version": "1.0.0", "description": "", "main": "index.js", diff --git a/examples/aws-express-file-upload/sst.config.ts b/examples/aws-express-file-upload/sst.config.ts index 003429d60..95a1c5d40 100644 --- a/examples/aws-express-file-upload/sst.config.ts +++ b/examples/aws-express-file-upload/sst.config.ts @@ -8,7 +8,7 @@ * You can get started by running. * * ```bash - * mkdir aws-express && cd aws-express + * mkdir aws-express-file-upload && cd aws-express-file-upload * npm init -y * npm install express * npx sst@latest init @@ -36,8 +36,8 @@ * This example lets you upload a file to S3 and then download it. * * ```bash - * curl --F file=@elysia.png http://localhost:3000/ - * curl http://localhost:3000/latest + * curl -F file=@package.json http://localhost:80/ + * curl http://localhost:80/latest * ``` * * Finally, you can deploy it using `npx sst deploy --stage production`. @@ -45,7 +45,7 @@ export default $config({ app(input) { return { - name: "aws-express", + name: "aws-express-file-upload", removal: input?.stage === "production" ? "retain" : "remove", home: "aws", }; diff --git a/examples/aws-express/index.mjs b/examples/aws-express/index.mjs index f810c756c..a951c7025 100644 --- a/examples/aws-express/index.mjs +++ b/examples/aws-express/index.mjs @@ -11,7 +11,7 @@ const redis = new Cluster( { dnsLookup: (address, callback) => callback(null, address), redisOptions: { - tls: true, + tls: {}, username: Resource.MyRedis.username, password: Resource.MyRedis.password, }, diff --git a/examples/tsconfig.json b/examples/tsconfig.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/examples/tsconfig.json @@ -0,0 +1 @@ +{} diff --git a/www/src/content/docs/docs/examples.mdx b/www/src/content/docs/docs/examples.mdx index 4c22e587f..578a57390 100644 --- a/www/src/content/docs/docs/examples.mdx +++ b/www/src/content/docs/docs/examples.mdx @@ -247,7 +247,7 @@ bun sst dev This example lets you upload a file to S3 and then download it. ```bash -curl --F file=@elysia.png http://localhost:3000/ +curl -F file=@elysia.png http://localhost:3000/ curl http://localhost:3000/latest ``` @@ -271,6 +271,66 @@ cluster.addService("MyService", { View the [full example](https://github.com/sst/ion/tree/dev/examples/aws-bun-elysia). +--- +## AWS Bun file upload + +Deploys an Bun app to AWS. + +You can get started by running. + +```bash +mkdir aws-bun-file-upload && cd aws-bun-file-upload +bun init -y +bunx sst init +``` + +Now you can add a service. + +```ts title="sst.config.ts" +cluster.addService("MyService", { + public: { + ports: [{ listen: "80/http", forward: "3000/http" }], + }, + dev: { + command: "bun dev", + }, + link: [bucket], +}); +``` + +Start your app locally. + +```bash +bun sst dev +``` + +This example lets you upload a file to S3 and then download it. + +```bash +curl -F file=@package.json http://localhost:3000/ +curl http://localhost:3000/latest +``` + +Finally, you can deploy it using `bun sst deploy --stage production`. +```ts title="sst.config.ts" +const bucket = new sst.aws.Bucket("MyBucket"); +const vpc = new sst.aws.Vpc("MyVpc"); + +const cluster = new sst.aws.Cluster("MyCluster", { vpc }); +cluster.addService("MyService", { + public: { + ports: [{ listen: "80/http", forward: "3000/http" }], + }, + dev: { + command: "bun dev", + }, + link: [bucket], +}); +``` + +View the [full example](https://github.com/sst/ion/tree/dev/examples/aws-bun-file-upload). + + --- ## Subscribe to queues @@ -351,7 +411,7 @@ Deploys an Express app to AWS. You can get started by running. ```bash -mkdir aws-express && cd aws-express +mkdir aws-express-file-upload && cd aws-express-file-upload npm init -y npm install express npx sst@latest init @@ -379,8 +439,8 @@ npx sst dev This example lets you upload a file to S3 and then download it. ```bash -curl --F file=@elysia.png http://localhost:3000/ -curl http://localhost:3000/latest +curl -F file=@package.json http://localhost:80/ +curl http://localhost:80/latest ``` Finally, you can deploy it using `npx sst deploy --stage production`. diff --git a/www/src/content/docs/docs/start/aws/bun.mdx b/www/src/content/docs/docs/start/aws/bun.mdx index f68a4f7e6..0044b3898 100644 --- a/www/src/content/docs/docs/start/aws/bun.mdx +++ b/www/src/content/docs/docs/start/aws/bun.mdx @@ -3,7 +3,7 @@ title: Bun on AWS with SST description: Create and deploy a Bun app to AWS with SST. --- -We are going to build a Bun HTTP server in a container, add an S3 Bucket for file uploads, and deploy it to AWS using SST. +We are going to build a hit counter using Bun and Redis. We'll the deploy it to AWS in a container using SST. :::tip[View source] You can [view the source](https://github.com/sst/ion/tree/dev/examples/aws-bun) of this example in our repo. @@ -82,9 +82,9 @@ To deploy our Bun app, let's add an [AWS Fargate](https://aws.amazon.com/fargate ```js title="sst.config.ts" {9-11} async run() { - const vpc = new sst.aws.Vpc("MyVpc"); - + const vpc = new sst.aws.Vpc("MyVpc", { bastion: true }); const cluster = new sst.aws.Cluster("MyCluster", { vpc }); + cluster.addService("MyService", { public: { ports: [{ listen: "80/http", forward: "3000/http" }], @@ -96,36 +96,48 @@ async run() { } ``` -This creates a VPC, uses it for a new ECS Cluster, adds a Fargate service to it, and exposes it through _http_. +This creates a VPC with a bastion host, an ECS Cluster, and adds a Fargate service to it. The `dev.command` tells SST to run our Bun app locally in dev mode. --- -## 3. Add an S3 Bucket +## 3. Add Redis -Let's add an S3 Bucket for file uploads. Update your `sst.config.ts`. +Let's add an [Amazon ElastiCache](https://aws.amazon.com/elasticache/) Redis cluster. Add this below the `Vpc` component in your `sst.config.ts`. ```js title="sst.config.ts" -const bucket = new sst.aws.Bucket("MyBucket"); +const redis = new sst.aws.Redis("MyRedis", { vpc }); ``` -Add this above the `Vpc` component. +This shares the same VPC as our ECS cluster. --- -#### Link the bucket +#### Link Redis -Now, link the bucket to the container. +Now, link the Redis cluster to the container. ```ts title="sst.config.ts" {3} cluster.addService("MyService", { // ... - link: [bucket], + link: [redis], }); ``` -This will allow us to reference the bucket in our Bun app. +This will allow us to reference the Redis cluster in our Bun app. + +--- + +#### Install a tunnel + +Since our Redis cluster is in a VPC, we'll need a tunnel to connect to it from our local machine. + +```bash "sudo" +sudo bun sst tunnel install +``` + +This needs _sudo_ to create a network interface on your machine. You'll only need to do this once on your machine. --- @@ -137,80 +149,47 @@ Start your app in dev mode. bun sst dev ``` -Once started, click on **MyService** in the sidebar for your local Bun app. +This will deploy your app, start a tunnel in the **Tunnel** tab, and run your Bun app locally in the **MyServiceDev** tab. --- -## 4. Upload a file +## 4. Connect to Redis -We want the `/` route of our API to upload a file to our S3 Bucket. Let's start by installing the npm packages we'll use for the upload. +We want the `/` route of our API to increment a counter in our Redis cluster. Let's start by installing the npm packages we'll use. ```bash -bun install @aws-sdk/client-s3 @aws-sdk/lib-storage @aws-sdk/s3-request-presigner +bun install ioredis ``` Add the relevant imports to your `index.ts`. -```ts title="index.ts" -import { Resource } from "sst"; -import { - S3Client, - GetObjectCommand, - ListObjectsV2Command, -} from "@aws-sdk/client-s3"; -import { Upload } from "@aws-sdk/lib-storage"; -import { getSignedUrl } from "@aws-sdk/s3-request-presigner"; - -const s3 = new S3Client(); -``` - -And add the route to your `index.ts` below the _Hello World_ route. - ```ts title="index.ts" {5} -if (url.pathname === "/" && req.method === "POST") { - const formData = await req.formData(); - const file = formData.get("file")! as File; - const params = { - Bucket: Resource.MyBucket.name, - Key: file.name, - Body: file, - }; - const upload = new Upload({ - params, - client: s3, - }); - await upload.done(); - - return new Response("File uploaded successfully."); -} +import { Resource } from "sst"; +import { Cluster } from "ioredis"; + +const redis = new Cluster( + [{ host: Resource.MyRedis.host, port: Resource.MyRedis.port }], + { + dnsLookup: (address, callback) => callback(null, address), + redisOptions: { + tls: {}, + username: Resource.MyRedis.username, + password: Resource.MyRedis.password, + }, + } +); ``` :::tip -We are directly accessing our S3 bucket with `Resource.MyBucket.name`. +We are directly accessing our Redis cluster with `Resource.MyRedis.*`. ::: ---- +Let's update the `/` route. -## 5. Download a file - -We want the `/latest` route of our app to generate a pre-signed URL to download the last uploaded file in our S3 Bucket. Add this below the upload route. - -```ts title="index.ts" -if (url.pathname === "/latest" && req.method === "GET") { - const objects = await s3.send( - new ListObjectsV2Command({ - Bucket: Resource.MyBucket.name, - }), - ); - const latestFile = objects.Contents!.sort( - (a, b) => - (b.LastModified?.getTime() ?? 0) - (a.LastModified?.getTime() ?? 0), - )[0]; - const command = new GetObjectCommand({ - Key: latestFile.Key, - Bucket: Resource.MyBucket.name, - }); - return Response.redirect(await getSignedUrl(s3, command)); +```ts title="index.tjs" +if (url.pathname === "/" && req.method === "GET") { + const counter = await redis.incr("counter"); + return new Response(`Hit counter: ${counter}`); } ``` @@ -218,17 +197,13 @@ if (url.pathname === "/latest" && req.method === "GET") { #### Test your app -Let's try uploading a file from your project root. - -```bash -curl -F file=@package.json http://localhost:80 -``` +Let's head over to `http://localhost:3000` in your browser and it'll show the current hit counter. -Now head over to `http://localhost:80/latest` in your browser and it'll download the file you just uploaded. +You should see it increment every time you refresh the page. --- -## 6. Deploy your app +## 5. Deploy your app To deploy our app we'll first add a `Dockerfile`. This is building our app by running our `build` script from above. @@ -311,4 +286,3 @@ As a next step, you can setup the [SST Console](/docs/console/) to _**git push t ![SST Console Autodeploy](../../../../../assets/docs/start/sst-console-autodeploy.png) You can [create a free account](https://console.sst.dev) and connect it to your AWS account. - diff --git a/www/src/content/docs/docs/start/aws/express.mdx b/www/src/content/docs/docs/start/aws/express.mdx index beff405d2..2c41197af 100644 --- a/www/src/content/docs/docs/start/aws/express.mdx +++ b/www/src/content/docs/docs/start/aws/express.mdx @@ -121,7 +121,7 @@ Since our Redis cluster is in a VPC, we'll need a tunnel to connect to it from o sudo npx sst tunnel install ``` -This needs _sudo_ to create a network interface on your machine. +This needs _sudo_ to create a network interface on your machine. You'll only need to do this once on your machine. --- @@ -156,7 +156,7 @@ const redis = new Cluster( { dnsLookup: (address, callback) => callback(null, address), redisOptions: { - tls: true, + tls: {}, username: Resource.MyRedis.username, password: Resource.MyRedis.password, },