home / skills / gilbertopsantosjr / fullstacknextjs / sst-infra
/skills/sst-infra
This skill guides AWS serverless infrastructure with SST v3, helping you deploy, manage secrets, and configure CI/CD for Next.js apps.
npx playbooks add skill gilbertopsantosjr/fullstacknextjs --skill sst-infraReview the files below or copy the command above to add this skill to your agents.
---
name: sst-infra
description: Guide for AWS serverless infrastructure using SST v3 (Serverless Stack). Use when configuring deployment, creating stacks, managing secrets, setting up CI/CD, or deploying Next.js applications to AWS Lambda with DynamoDB.
---
# SST v3 Infrastructure
## Project Structure
```
project/
├── sst.config.ts # Main SST config
├── stacks/
│ ├── dynamodb.ts # Database stack
│ ├── nextjs.ts # Next.js deployment
│ └── environment.ts # Environment config
└── open-next.config.ts # Lambda streaming config
```
## Main Config
```typescript
// sst.config.ts
export default $config({
app(input) {
return {
name: 'my-app',
removal: input?.stage === 'prod' ? 'retain' : 'remove',
protect: ['prod'].includes(input?.stage ?? ''),
home: 'aws',
providers: { aws: { region: 'us-east-1' } },
}
},
async run() {
const { table } = await import('./stacks/dynamodb')
const { site } = await import('./stacks/nextjs')
return { url: site.url, tableName: table.name }
},
})
```
## DynamoDB Stack
```typescript
// stacks/dynamodb.ts
export const table = new sst.aws.Dynamo('Table', {
fields: {
pk: 'string', sk: 'string',
gsi1pk: 'string', gsi1sk: 'string',
},
primaryIndex: { hashKey: 'pk', rangeKey: 'sk' },
globalIndexes: {
gsi1: { hashKey: 'gsi1pk', rangeKey: 'gsi1sk' },
},
transform: {
table: (args) => { args.billingMode = 'PAY_PER_REQUEST' },
},
})
```
## Next.js Stack
```typescript
// stacks/nextjs.ts
import { table } from './dynamodb'
export const site = new sst.aws.Nextjs('Site', {
path: 'apps/web',
link: [table],
environment: {
TABLE_NAME: table.name,
},
domain: {
name: `${$app.stage === 'prod' ? '' : `${$app.stage}.`}myapp.com`,
dns: sst.aws.dns({ zone: 'myapp.com' }),
},
})
```
## OpenNext Config
```typescript
// open-next.config.ts
import type { OpenNextConfig } from 'open-next/types/open-next'
const config: OpenNextConfig = {
default: {
override: { wrapper: 'aws-lambda-streaming' },
},
}
export default config
```
## Commands
```bash
# Development (local Lambda)
npx sst dev --stage dev
# Deploy
npx sst deploy --stage dev
npx sst deploy --stage prod
# Remove
npx sst remove --stage dev
# Outputs
npx sst outputs --stage dev
# Secrets
npx sst secret set AUTH_SECRET "value" --stage dev
npx sst secret list --stage dev
```
## Environment Stages
| Stage | Domain | Protection | Removal |
|-------|--------|------------|---------|
| dev | dev.app.com | No | Remove |
| test | test.app.com | No | Remove |
| prod | app.com | Yes | Retain |
## CI/CD (GitHub Actions)
```yaml
# .github/workflows/deploy.yml
name: Deploy
on:
workflow_dispatch:
inputs:
stage:
type: choice
options: [dev, test, prod]
jobs:
deploy:
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v3
- uses: actions/setup-node@v4
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: us-east-1
- run: pnpm install --frozen-lockfile
- run: npx sst deploy --stage ${{ inputs.stage }}
```
## Route Protection (proxy.ts)
```typescript
// src/proxy.ts
import { NextRequest, NextResponse } from 'next/server'
import { authServer } from '@saas4dev/auth'
const protectedRoutes = ['/dashboard', '/settings']
export async function proxy(request: NextRequest) {
const { pathname } = request.nextUrl
const isProtected = protectedRoutes.some(r => pathname.startsWith(r))
const session = await authServer.api.getSession({
headers: await headers(),
})
if (isProtected && !session) {
const url = new URL('/sign-in', request.url)
url.searchParams.set('redirect', pathname)
return NextResponse.redirect(url)
}
return NextResponse.next()
}
```
## Local Development
```bash
# DynamoDB Local
docker run -p 8000:8000 amazon/dynamodb-local
# Environment
TABLE_NAME=dev-Table
DYNAMODB_LOCAL=true
```
This skill guides building and deploying AWS serverless infrastructure using SST v3 (Serverless Stack). It focuses on configuring stacks for DynamoDB, deploying Next.js to Lambda with streaming, environment stages, secrets management, and CI/CD automation. The goal is repeatable, stage-aware deployments for production and non-production environments.
It defines reusable stacks: a DynamoDB table with primary and global indexes and a Next.js site deployed to Lambda with environment bindings and domain configuration. OpenNext is configured for Lambda streaming, and SST config drives stage-specific behavior (protection, removal policy, provider region). The workflow includes local development, secrets commands, and GitHub Actions for automated deploys.
How do I protect production resources from accidental removal?
Set removal to 'retain' and enable protect for the prod stage in the SST config so stacks cannot be removed without explicit changes.
Can I run the stack locally and connect to a local DynamoDB?
Yes. Use npx sst dev for local Lambda emulation and run a DynamoDB Local container. Set environment flags (e.g., DYNAMODB_LOCAL=true) to point the app to the local endpoint.