Skip to content

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.

  1. Install the Nx Console VSCode Plugin if you haven't already
  2. Open the Nx Console in VSCode
  3. Click Generate (UI) in the "Common Nx Commands" section
  4. Search for @aws/nx-plugin - ts#dynamodb
  5. Fill in the required parameters
    • Click Generate
    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.

    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

    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/constructs/src
      • Directoryapp
        • Directorydynamodb
          • <name>.ts Infrastructure specific to your table
      • Directorycore
        • dynamodb.ts Generic DynamoDB table construct

    The generator configures a dev target that starts a DynamoDB Local instance and creates the table. Use the project’s dev target:

    Terminal window
    pnpm nx dev <project-name>

    This automatically:

    1. Pulls the DynamoDB Local image (pull-image target)
    2. Starts a container
    3. Creates a local table with the indexes defined in config.json

    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:

    packages/my-table/src/entities/example.ts
    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.

    The generated src/client.ts exports two key utilities:

    • getDynamoDBClient() — returns a cached singleton DynamoDBClient. When LOCAL_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. When LOCAL_DEV=true, returns the local table name constant; otherwise fetches the name from AWS AppConfig using the RUNTIME_CONFIG_APP_ID environment variable and caches it for subsequent calls.

    Stopping dev (e.g. with Ctrl+C) automatically removes the DynamoDB Local container, but preserves the named volume so your data persists across restarts.

    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:

    config.json
    {
    ...
    "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 developmentdev reads config.json and creates or updates the local table to match the GSI list
    • CDK — the construct reads config.json at synth time, so GSI changes are reflected on the next cdk deploy
    • Terraform — the module reads config.json at plan/apply time

    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();

    The DynamoDB generator creates CDK or Terraform infrastructure based on your selected iac.

    The CDK construct is created in common/constructs. Example usage:

    packages/infra/src/stacks/application-stack.ts
    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) and sk (sort key), both String type
    • 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 dynamodb namespace in AWS AppConfig

    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:

    packages/infra/src/stacks/application-stack.ts
    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);
    });

    Deletion protection is enabled by default to prevent accidental table deletion.

    Disable it for environments where table deletion is expected, such as short-lived development or preview stacks.

    packages/infra/src/stacks/application-stack.ts
    import { MyTable } from ':my-scope/common-constructs';
    const table = new MyTable(this, 'Table', {
    deletionProtection: false,
    });

    The table defaults to on-demand (PAY_PER_REQUEST) billing. Switch to provisioned capacity for predictable, high-throughput workloads.

    packages/infra/src/stacks/application-stack.ts
    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,
    });

    Point-in-time recovery is enabled by default, allowing you to restore the table to any point in the last 35 days.

    packages/infra/src/stacks/application-stack.ts
    import { MyTable } from ':my-scope/common-constructs';
    const table = new MyTable(this, 'Table', {
    pointInTimeRecoverySpecification: { pointInTimeRecoveryEnabled: false },
    });

    The KMS key used to encrypt the table has automatic key rotation enabled by default. Disable it if your security policy manages rotation externally.

    packages/infra/src/stacks/application-stack.ts
    import { MyTable } from ':my-scope/common-constructs';
    const table = new MyTable(this, 'Table', {
    enableKeyRotation: false,
    });

    Use the connection generator to integrate this project with others in your workspace. The following connections involve this project:

    tRPC Amazon DynamoDB
    tRPC API to TypeScript DynamoDB Connect a tRPC API to a DynamoDB table
    Smithy Amazon DynamoDB
    Smithy API to TypeScript DynamoDB Connect a Smithy API to a DynamoDB table
    Strands Agents TypeScript Amazon DynamoDB
    TypeScript Agent to TypeScript DynamoDB Connect a TypeScript Agent to a DynamoDB table
    Model Context Protocol Amazon DynamoDB
    MCP Server to TypeScript DynamoDB Connect a TypeScript MCP Server to a DynamoDB table