TypeScript DynamoDB
This generator creates a new TypeScript DynamoDB project backed by Amazon DynamoDB, using ElectroDB for type-safe entity modelling. It generates the application code and infrastructure needed to provision and manage a DynamoDB table using AWS CDK or Terraform, with single-table design support and built-in local development via DynamoDB Local.
Generate a TypeScript DynamoDB Project
Section titled “Generate a TypeScript DynamoDB Project”- Install the Nx Console VSCode Plugin if you haven't already
- Open the Nx Console in VSCode
- Click
Generate (UI)in the "Common Nx Commands" section - Search for
@aws/nx-plugin - ts#dynamodb - Fill in the required parameters
- Click
Generate
pnpm nx g @aws/nx-plugin:ts#dynamodbyarn nx g @aws/nx-plugin:ts#dynamodbnpx nx g @aws/nx-plugin:ts#dynamodbbunx nx g @aws/nx-plugin:ts#dynamodbYou can also perform a dry-run to see what files would be changed
pnpm nx g @aws/nx-plugin:ts#dynamodb --dry-runyarn nx g @aws/nx-plugin:ts#dynamodb --dry-runnpx nx g @aws/nx-plugin:ts#dynamodb --dry-runbunx nx g @aws/nx-plugin:ts#dynamodb --dry-runOptions
Section titled “Options”| Parameter | Type | Default | Description |
|---|---|---|---|
| name Required | string | - | Name of the DynamoDB project to generate |
| directory | string | packages | The directory to store the project in. |
| subDirectory | string | - | The sub directory the project is placed in. By default this is the project name. |
| framework | electrodb | electrodb | The framework to use for DynamoDB entities. |
| tableName | string | - | The DynamoDB table name. Auto-generated if not specified. |
| infra | dynamodb | none | dynamodb | Infrastructure to provision for the DynamoDB table. |
| iac | inherit | cdk | terraform | inherit | The preferred IaC provider. By default this is inherited from your initial selection. |
| preferInstallDependencies | boolean | true | Whether to prefer installing dependencies after the generator runs. Set to false to defer installing when batching multiple generators (an install still runs if needed so subsequent generators can compute the Nx project graph); install once at the end. |
Generator Output
Section titled “Generator Output”The generator creates the following project structure in the <directory>/<name> directory:
Directorysrc
- index.ts Project entry point and exports
- client.ts DynamoDB client singleton and table name resolution
Directoryentities
- example.ts Example ElectroDB entity definition
- index.ts Entity exports
- config.json Table configuration including GSI definitions and local development settings
- project.json Project configuration and build targets
The local development scripts are shared across all DynamoDB projects (both TypeScript and Python) and generated once into:
Directorypackages/common/scripts/src/dynamodb
- create-local-table.ts Creates the DynamoDB table in the local DynamoDB Local instance
- pull-image.ts Pulls the DynamoDB Local image
- start-container.ts Starts the DynamoDB Local container
Infrastructure
Section titled “Infrastructure”Since this generator vends infrastructure as code based on your chosen iacProvider, it will create a project in packages/common which includes the relevant CDK constructs or Terraform modules.
The common infrastructure as code project is structured as follows:
Directorypackages/common/constructs
Directorysrc
Directoryapp/ Constructs for infrastructure specific to a project/generator
- …
Directorycore/ Generic constructs which are reused by constructs in
app- …
- index.ts Entry point exporting constructs from
app
- project.json Project build targets and configuration
Directorypackages/common/terraform
Directorysrc
Directoryapp/ Terraform modules for infrastructure specific to a project/generator
- …
Directorycore/ Generic modules which are reused by modules in
app- …
- project.json Project build targets and configuration
Directorypackages/common/constructs/src
Directoryapp
Directorydynamodb
- <name>.ts Infrastructure specific to your table
Directorycore
- dynamodb.ts Generic DynamoDB table construct
Directorypackages/common/terraform/src
Directoryapp
Directorydynamodb
Directory<name>
- <name>.tf Module specific to your table
Directorycore
Directorydynamodb
- dynamodb.tf Generic DynamoDB module
Local Development
Section titled “Local Development”Starting Local DynamoDB
Section titled “Starting Local DynamoDB”The generator configures a dev target that starts a DynamoDB Local instance and creates the table. Use the project’s dev target:
pnpm nx dev <project-name>yarn nx dev <project-name>npx nx dev <project-name>bunx nx dev <project-name>This automatically:
- Pulls the DynamoDB Local image (
pull-imagetarget) - Starts a container
- Creates a local table with the indexes defined in
config.json
Data Modelling
Section titled “Data Modelling”The generated project uses ElectroDB for type-safe entity modelling on a single DynamoDB table, following DynamoDB’s single-table design. Add or update entity files under src/entities/, using the generated example entity as a starting point.
Example entity definition:
import { Entity } from 'electrodb';import { getDynamoDBClient, resolveTableName } from '../client.js';
export const createExampleEntity = async () => new Entity( { model: { entity: 'example', version: '1', service: 'MyTable', }, attributes: { id: { type: 'string', required: true, }, createdAt: { type: 'string', required: true, default: () => new Date().toISOString(), readOnly: true, }, updatedAt: { type: 'string', required: true, default: () => new Date().toISOString(), watch: '*', set: () => new Date().toISOString(), }, }, indexes: { primary: { pk: { field: 'pk', composite: ['id'], }, sk: { field: 'sk', composite: [], }, }, }, }, { client: getDynamoDBClient(), table: await resolveTableName() }, );For more details, see the ElectroDB entity documentation.
Using the DynamoDB Client
Section titled “Using the DynamoDB Client”The generated src/client.ts exports two key utilities:
getDynamoDBClient()— returns a cached singletonDynamoDBClient. WhenLOCAL_DEV=true, connects to the local DynamoDB Local instance; otherwise creates an AWS client using the default credential chain.resolveTableName()— returns the DynamoDB table name. WhenLOCAL_DEV=true, returns the local table name constant; otherwise fetches the name from AWS AppConfig using theRUNTIME_CONFIG_APP_IDenvironment variable and caches it for subsequent calls.
Stopping Local DynamoDB
Section titled “Stopping Local DynamoDB”Stopping dev (e.g. with Ctrl+C) automatically removes the DynamoDB Local container, but preserves the named volume so your data persists across restarts.
Adding/Removing Global Secondary Indexes
Section titled “Adding/Removing Global Secondary Indexes”GSIs are defined in config.json at the project root under the tableConfig.globalSecondaryIndexes key. Add an entry for each GSI, following the single-table design naming convention for GSI keys:
{ ... "tableConfig": { "globalSecondaryIndexes": [ { "indexName": "gsi1pk-gsi1sk-index", "partitionKey": "gsi1pk", "sortKey": "gsi1sk" }, { "indexName": "gsi2pk-gsi2sk-index", "partitionKey": "gsi2pk", "sortKey": "gsi2sk" } ] }}The sortKey field is optional for hash-key-only GSIs.
This config file is the single source of truth read by all consumers:
- Local development —
devreadsconfig.jsonand creates or updates the local table to match the GSI list - CDK — the construct reads
config.jsonat synth time, so GSI changes are reflected on the nextcdk deploy - Terraform — the module reads
config.jsonat plan/apply time
One GSI per Deployment
Section titled “One GSI per Deployment”Connecting to the Table
Section titled “Connecting to the Table”In any TypeScript project, import entity factories from your DynamoDB package and use them directly:
import { createExampleEntity } from ':my-scope/my-table';
const entity = await createExampleEntity();const result = await entity.query.primary({ id: '123' }).go();Deploying your Table
Section titled “Deploying your Table”The DynamoDB generator creates CDK or Terraform infrastructure based on your selected iac.
The CDK construct is created in common/constructs. Example usage:
import { MyTable } from ':my-scope/common-constructs';
export class ApplicationStack extends Stack { constructor(scope: Construct, id: string, props?: StackProps) { super(scope, id, props);
const table = new MyTable(this, 'Table'); }}This provisions a DynamoDB table with:
pk(partition key) andsk(sort key), bothStringtype- Global Secondary Indexes as defined in
config.json - On-demand (
PAY_PER_REQUEST) billing - Customer-managed KMS encryption with automatic key rotation
- Point-in-time recovery enabled
- Deletion protection enabled
- Table name registered in Runtime Config under the
dynamodbnamespace in AWS AppConfig
The Terraform module is created in common/terraform. Example usage:
module "my_table" { source = "../../common/terraform/src/app/dynamodb/my-table"}This provisions a DynamoDB table with:
pk(partition key) andsk(sort key), bothStringtype- Global Secondary Indexes as defined in
config.json - On-demand (
PAY_PER_REQUEST) billing - Customer-managed KMS encryption with automatic key rotation
- Point-in-time recovery enabled
- Deletion protection enabled
- Table name registered in Runtime Config
Granting Access
Section titled “Granting Access”To allow Lambda functions to access the DynamoDB table, grant the necessary permissions in your infrastructure.
Call grantReadWriteData on the table construct. This grants both the DynamoDB and KMS permissions required by the Lambda execution role:
import { MyTable } from ':my-scope/common-constructs';
const table = new MyTable(this, 'Table');
const api = new Api(this, 'Api', { integrations: Api.defaultIntegrations(this).build(),});
Object.entries(api.integrations).forEach(([, integration]) => { table.grantReadWriteData(integration.handler);});Grant the Lambda execution role permission to access the DynamoDB table and its KMS encryption key:
module "my_table" { source = "../../common/terraform/src/app/dynamodb/my-table"}
resource "aws_iam_role_policy" "dynamodb_access" { role = module.my_api.lambda_role_name
policy = jsonencode({ Version = "2012-10-17" Statement = [ { Effect = "Allow" Action = [ "dynamodb:GetItem", "dynamodb:PutItem", "dynamodb:UpdateItem", "dynamodb:DeleteItem", "dynamodb:Query", "dynamodb:Scan", "dynamodb:BatchGetItem", "dynamodb:BatchWriteItem", ] Resource = [ module.my_table.table_arn, "${module.my_table.table_arn}/index/*", ] }, { Effect = "Allow" Action = [ "kms:Encrypt", "kms:Decrypt", "kms:ReEncrypt*", "kms:GenerateDataKey*", "kms:DescribeKey" ] Resource = [module.my_table.kms_key_arn] }, ] })}Deletion Protection
Section titled “Deletion Protection”Deletion protection is enabled by default to prevent accidental table deletion.
Disable Deletion Protection
Section titled “Disable Deletion Protection”Disable it for environments where table deletion is expected, such as short-lived development or preview stacks.
import { MyTable } from ':my-scope/common-constructs';
const table = new MyTable(this, 'Table', { deletionProtection: false,});module "my_table" { source = "../../common/terraform/src/app/dynamodb/my-table" deletion_protection_enabled = false}Billing Mode
Section titled “Billing Mode”The table defaults to on-demand (PAY_PER_REQUEST) billing. Switch to provisioned capacity for predictable, high-throughput workloads.
import { BillingMode } from 'aws-cdk-lib/aws-dynamodb';import { MyTable } from ':my-scope/common-constructs';
const table = new MyTable(this, 'Table', { billingMode: BillingMode.PROVISIONED, readCapacity: 5, writeCapacity: 5,});module "my_table" { source = "../../common/terraform/src/app/dynamodb/my-table" billing_mode = "PROVISIONED"}Point-in-time Recovery
Section titled “Point-in-time Recovery”Point-in-time recovery is enabled by default, allowing you to restore the table to any point in the last 35 days.
Disable Point-in-time Recovery
Section titled “Disable Point-in-time Recovery”import { MyTable } from ':my-scope/common-constructs';
const table = new MyTable(this, 'Table', { pointInTimeRecoverySpecification: { pointInTimeRecoveryEnabled: false },});module "my_table" { source = "../../common/terraform/src/app/dynamodb/my-table" point_in_time_recovery_enabled = false}Encryption Key Rotation
Section titled “Encryption Key Rotation”The KMS key used to encrypt the table has automatic key rotation enabled by default. Disable it if your security policy manages rotation externally.
Disable Encryption Key Rotation
Section titled “Disable Encryption Key Rotation”import { MyTable } from ':my-scope/common-constructs';
const table = new MyTable(this, 'Table', { enableKeyRotation: false,});module "my_table" { source = "../../common/terraform/src/app/dynamodb/my-table" enable_key_rotation = false}Connections
Section titled “Connections”Use the connection generator to integrate this project with others in your workspace. The following connections involve this project: