DynamoDB with SST: Tables, Linking and API Examples

Written by Mahesh Samarasinghe
Last updated: October 23rd, 2026
Serverless Stack is now SST. In SST v4, create DynamoDB tables with sst.aws.Dynamo, link them to functions, and read the generated table name through Resource from the SST SDK. Current SST uses Pulumi-based infrastructure rather than the CDK constructs shown in older Serverless Stack tutorials.
What changed from the old Serverless Stack?
Old examples importing Table, Api or StackContext from @serverless-stack/resources describe a different architecture. Current projects put infrastructure in sst.config.ts, use primaryIndex.hashKey, and attach resources with link instead of attachPermissions.
SST v4 upgrades its underlying Pulumi AWS provider to v7. An existing v3 project needs the documented state migration for every stage, including sst refresh; it is not a reversible package downgrade. Follow the v3 migration guide for an existing app. A v2 CDK app needs the separate v2 migration path rather than this new-project walkthrough.
SST's development mode can run function code locally while using real AWS infrastructure. That helps test service integrations, but AWS resources still exist and can incur charges. Fully offline DynamoDB testing is a separate setup covered below.
Step 1: initialize a project
Use a current supported Node.js release and AWS CLI v2, configure a development AWS profile, then initialize SST:
Select AWS when initializing and keep the generated TypeScript configuration. The SST CLI reference describes initialization, development, preview and deployment commands. Keep the dependency lock file so local and CI builds use the same versions.
Step 2: define the blog table and API
Use this sst.config.ts for a new project:
fields declares only attributes used by primary or secondary indexes. Do not add title and content there merely because every blog has them. DynamoDB items can contain those ordinary fields without an infrastructure schema declaration. The Dynamo component reference documents the current properties and subscriber API.
link: [blogTable] makes the table's name available to the handler and configures access permissions. The runtime code reads Resource.Blog.name, where Blog is the component's logical name. Linking is not an HTTP authorizer or a per-user access rule. Review the generated permissions if read-only and write-only functions need stricter separation.
The API uses ApiGatewayV2 routes, not the old Api routes map. The five routes preserve the original create, list, get, update and delete operations while using the current handler and linking syntax.
Step 3: implement the CRUD handlers
Create functions/blogs.ts. Start with the shared client, response helper and request validation:
The SDK client is reused between invocations. SST bundles the handler's dependencies; sst is also a runtime dependency because the handler imports Resource. The generated resource type declarations must be included by the project's TypeScript configuration.
Create a blog
Append this export. It uses a string UUID for the table key and stores an ISO timestamp for chronological index ordering:
TTL is enabled on the table, but this handler does not give published posts an expiry. Only items containing a numeric expiresAt value become eligible for deletion. If drafts should expire, explicitly calculate their expiry in epoch seconds and add it when creating the draft.
List blogs with pagination
Append the list handler:
Follow the token until it is null. Scan evaluates a page of items and does not provide sorted results or a consistent snapshot across pages. Adding a sort key does not make a Scan selective. To list an author's posts, Query ByAuthor with equality on author and an optional condition on createdAt; see DynamoDB query examples.
Get a blog by ID
This reads the base table strongly consistently, which is useful when reading a post immediately after an update. GSI queries remain eventually consistent; the same flag cannot make ByAuthor strongly consistent.
Update a blog
The existence condition prevents UpdateItem from creating a partial item for an unknown ID. This example requires all editable fields; for PATCH semantics, build the expression only from validated fields actually supplied. Add a version condition if concurrent edits must not overwrite each other.
Delete a blog
These handlers demonstrate database operations. Before exposing the API to real users, configure authorization and derive the author from a verified identity. A request body naming another author must not grant that caller ownership. Add business validation and limits for the title and content fields as part of that application layer.
Step 4: subscribe to DynamoDB Streams
Create functions/changes.ts, referenced by blogTable.subscribe:
The table must have a stream before you subscribe. new-and-old-images exposes both versions on updates; deletes have only the old image. The example logs identifiers rather than full blog content.
Lambda can deliver stream records more than once. Make subscriber side effects idempotent and configure retry limits, failure destinations and partial batch responses for a production consumer. Those settings belong to the event source mapping, available through the subscriber's documented transforms. A subscriber does not need a table link just to receive records; link the table if its handler also calls the table API. See DynamoDB Streams for failure-handling patterns.
Step 5: develop, preview and deploy
Use a separate stage for each developer. sst dev can provision real tables and routes, then connect invocations to local function code. Stopping the development process is not the same as deleting its AWS resources. Test the five routes, missing IDs, duplicate requests and paginated results before deploying production.
The SST Console provides current application inspection and debugging. Older screenshots of the Live Lambda dashboard describe the retired interface and are not needed to operate this example. Use the API URL returned from the current deployment and its logs to verify behavior.
For fully offline tests, run DynamoDB Local and inject its endpoint and dummy credentials into a test SDK client. Linking alone does not redirect requests to localhost. Test IAM, replica behavior and backups in AWS, where those features actually run.
Capacity, backups and production retention
The Dynamo component creates an on-demand table. For advanced provider settings, use transform.table deliberately. The PITR transform above uses the underlying Pulumi table's pointInTimeRecovery object; warm throughput and maximum on-demand throughput similarly use provider argument names, not CloudFormation property names.
PITR gives a recovery window of 1 to 35 days and restores to a new table. A shorter period does not lower the price. Test restoration and application cutover. TTL deletes are asynchronous and cannot replace backup retention or authorization checks.
The configuration uses three separate protections in production: SST protect blocks sst remove for that stage; removal: "retain" retains data resources removed from configuration; DynamoDB deletionProtection blocks service deletion. Read the SST configuration reference before changing them. Retention changes need a deployment to take effect, and retained tables keep incurring charges.
If you need explicit low-level table control, the Pulumi examples show the provider underneath SST. If your organization deploys through CloudFormation, compare AWS CDK TableV2. Neither approach should take ownership of an SST-managed table without an import and state-migration plan.
Frequently Asked Questions
Is Serverless Stack still based on AWS CDK?
@serverless-stack/resources describe the older architecture and need migration rather than a package-name replacement.How do I create a DynamoDB table in SST?
new sst.aws.Dynamo with key fields and primaryIndex.hashKey. Only fields used in primary or secondary indexes belong in the fields declaration.How does a Lambda get the SST table name?
Resource.Blog.name from the SST SDK when the component is named Blog. Linking also configures resource permissions.Is sst dev fully offline?
Can SST react to DynamoDB changes?
table.subscribe to attach a handler. Make its side effects idempotent because Lambda can process records more than once.Dynobase is a Professional GUI Client for DynamoDB
Start your 7-day free trial today