npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@realdb/cdk

v1.3.0

Published

AWS CDK construct for deploying RDB (Real-time Database) infrastructure

Readme

@realdb/cdk

AWS CDK construct library for deploying RDB (Real-time Database) infrastructure with Zod schema validation.

npm version

Overview

RDB is a serverless real-time database built on AWS infrastructure. This CDK construct allows you to easily deploy the complete RDB infrastructure to your AWS account with a single construct.

✨ Features

  • 🚀 Serverless Architecture - Built on AWS Lambda, DynamoDB, and AppSync
  • Real-time Subscriptions - WebSocket connections for instant data updates via AppSync
  • 🔐 Secure by Default - API key authentication with AWS Secrets Manager encryption
  • 📊 Auto-Scaling - DynamoDB on-demand billing scales with your needs
  • 🛠️ Easy to Deploy - Single construct deploys complete infrastructure
  • 🎯 Customizable - Support for existing APIs, custom resource names, and API prefixes
  • 💰 Cost Effective - Serverless pay-per-use pricing model
  • 🔄 Dead Letter Queue - Failed operations are captured for retry
  • 📝 Full Logging - CloudWatch logs with configurable retention
  • 📋 Initial Tables - Define tables in CDK and have them auto-created on deployment

🏗️ Architecture

The RDB construct deploys the following AWS resources:

| Resource | Purpose | |----------|---------| | DynamoDB Tables (2) | Metadata storage for tables (rdb-tables) and API keys (rdb-api-keys) | | AppSync GraphQL API | Real-time subscriptions and GraphQL mutations | | API Gateway REST API | RESTful CRUD operations with Lambda authorizer | | Lambda Functions (7) | Table management, records, API keys, schema sync, decommission, auth, SDK config | | S3 Bucket | Versioned schema configuration storage with EventBridge | | SQS Queue + DLQ | Asynchronous table decommissioning with retry logic | | Secrets Manager | Encrypted API key storage | | EventBridge Rule | Automated schema synchronization from S3 changes | | CloudWatch Logs | Comprehensive logging for all Lambda functions |

📦 Installation

npm install @realdb/cdk aws-cdk-lib constructs

Peer Dependencies:

  • aws-cdk-lib ^2.0.0
  • constructs ^10.0.0

🚀 Quick Start

Basic Example

import * as cdk from 'aws-cdk-lib';
import { RdbConstruct } from '@realdb/cdk';

export class MyStack extends cdk.Stack {
  constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // Deploy complete RDB infrastructure
    const rdb = new RdbConstruct(this, 'Rdb');

    // Output the API endpoint
    new cdk.CfnOutput(this, 'ApiEndpoint', {
      value: rdb.api!.url,
      description: 'RDB API Gateway endpoint',
    });

    // Output the AppSync endpoint for real-time subscriptions
    new cdk.CfnOutput(this, 'AppSyncEndpoint', {
      value: rdb.appSyncApi.graphqlUrl,
      description: 'AppSync GraphQL endpoint',
    });
  }
}

Advanced Configuration

import * as cdk from 'aws-cdk-lib';
import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';
import * as logs from 'aws-cdk-lib/aws-logs';
import { RdbConstruct } from '@realdb/cdk';

export class ProductionStack extends cdk.Stack {
  constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const rdb = new RdbConstruct(this, 'Rdb', {
      // Resource naming
      resourceSuffix: 'prod',              // Creates rdb-tables-prod, rdb-api-keys-prod, etc.
      apiPrefix: 'v1',                     // API routes: /v1/tables, /v1/tables/{name}/records
      
      // Lifecycle
      removalPolicy: cdk.RemovalPolicy.RETAIN, // Prevent accidental deletion in production
      
      // DynamoDB
      billingMode: dynamodb.BillingMode.PAY_PER_REQUEST, // Auto-scaling on-demand
      
      // Monitoring & Tracing
      enableXRayTracing: true,             // Enable AWS X-Ray
      logRetention: logs.RetentionDays.ONE_MONTH,
      enableApiLogging: true,
      
      // CORS
      corsOrigins: [
        'https://app.example.com',
        'https://admin.example.com'
      ],
    });

    // Outputs
    new cdk.CfnOutput(this, 'ApiEndpoint', {
      value: rdb.api!.url,
      exportName: 'RdbApiEndpoint',
    });

    new cdk.CfnOutput(this, 'AppSyncEndpoint', {
      value: rdb.appSyncApi.graphqlUrl,
      exportName: 'RdbAppSyncEndpoint',
    });

    new cdk.CfnOutput(this, 'ConfigBucket', {
      value: rdb.configBucket.bucketName,
      exportName: 'RdbConfigBucket',
    });
  }
}

Multi-Environment Setup

import * as cdk from 'aws-cdk-lib';
import { RdbConstruct } from '@realdb/cdk';

const app = new cdk.App();

// Development environment
new cdk.Stack(app, 'RdbDevStack', {
  env: { region: 'us-east-1' }
});

const devRdb = new RdbConstruct(this, 'RdbDev', {
  resourceSuffix: 'dev',
  removalPolicy: cdk.RemovalPolicy.DESTROY, // Easy cleanup
  corsOrigins: ['*'], // Allow all for testing
});

// Production environment
const prodStack = new cdk.Stack(app, 'RdbProdStack', {
  env: { region: 'us-east-1' }
});

const prodRdb = new RdbConstruct(prodStack, 'RdbProd', {
  resourceSuffix: 'prod',
  removalPolicy: cdk.RemovalPolicy.RETAIN,  // Prevent data loss
  corsOrigins: ['https://app.example.com'],
  enableXRayTracing: true,
});

Using Existing Resources

import * as cdk from 'aws-cdk-lib';
import * as apigateway from 'aws-cdk-lib/aws-apigateway';
import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';
import { RdbConstruct } from '@realdb/cdk';

// Use existing API Gateway
const existingApi = apigateway.RestApi.fromRestApiAttributes(this, 'ExistingApi', {
  restApiId: 'abc123',
  rootResourceId: 'xyz789',
});

// Use existing DynamoDB table for metadata
const existingTable = dynamodb.Table.fromTableName(this, 'ExistingTable', 'my-tables');

const rdb = new RdbConstruct(this, 'Rdb', {
  existingApi: existingApi,           // Add RDB routes to existing API
  existingTablesTable: existingTable, // Use existing metadata table
  apiPrefix: 'rdb',                   // Mount under /rdb/* routes
});

📋 Initial Tables (Recommended)

The best way to use RDB is to define your tables directly in CDK using initialTables. This ensures tables and their AppSync schemas are created automatically during deployment.

import * as cdk from 'aws-cdk-lib';
import * as logs from 'aws-cdk-lib/aws-logs';
import { RdbConstruct } from '@realdb/cdk';

export class MyStack extends cdk.Stack {
  constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const rdb = new RdbConstruct(this, 'Rdb', {
      resourceSuffix: 'dev',
      logRetention: logs.RetentionDays.ONE_WEEK,
      
      // Define tables to be created on deployment
      initialTables: [
        {
          tableName: 'chatEvents',
          fields: [
            { name: 'id', type: 'String', primary: true, required: true },
            { name: 'chatId', type: 'String', required: true, indexed: true },
            { name: 'eventType', type: 'String', required: true, indexed: true },
            { name: 'content', type: 'String' },
            { name: 'userId', type: 'String', required: true },
            { name: 'timestamp', type: 'String', required: true },
          ],
          subscriptions: [
            { filters: [{ field: 'chatId', type: 'String' }] }
          ],
          description: 'Real-time chat events for messaging',
        },
        {
          tableName: 'users',
          fields: [
            { name: 'id', type: 'String', primary: true, required: true },
            { name: 'email', type: 'String', required: true, indexed: true },
            { name: 'name', type: 'String', required: true },
            { name: 'status', type: 'String' },
            { name: 'createdAt', type: 'String', required: true },
          ],
          subscriptions: [
            { filters: [{ field: 'id', type: 'String' }] }
          ],
          description: 'User profiles',
        },
      ],
      // API key used for table namespacing in AppSync
      initialTablesApiKey: 'my-app-dev',
    });

    // Tables are created with full AppSync schema including:
    // - Queries: getTbc3d881a_chatEvents, listTbc3d881a_chatEvents
    // - Mutations: createTbc3d881a_chatEvents, updateTbc3d881a_chatEvents, deleteTbc3d881a_chatEvents
    // - Subscriptions: onTbc3d881a_chatEventsCreate, onTbc3d881a_chatEventsUpdate, onTbc3d881a_chatEventsDelete
    // - Publish mutation: publishTbc3d881a_chatEvents (for real-time streaming without DB write)
  }
}

Field Types

| Type | GraphQL Type | DynamoDB Type | |------|--------------|---------------| | String | String | S | | Int | Int | N | | Float | Float | N | | Boolean | Boolean | BOOL | | Array | [String] | L |

Field Options

| Option | Type | Description | |--------|------|-------------| | name | string | Field name (required) | | type | string | Field type: String, Int, Float, Boolean, Array (default: String) | | primary | boolean | Mark as primary key (one field required) | | required | boolean | Field is required in GraphQL schema | | indexed | boolean | Create GSI for efficient queries |

Subscription Filters

Subscriptions can be filtered by field values:

subscriptions: [
  { 
    filters: [
      { field: 'chatId', type: 'String' },
      { field: 'userId', type: 'String' }
    ] 
  }
]

This generates subscriptions like:

subscription onChatEventsUpdate($chatId: String, $userId: String) {
  onTbc3d881a_chatEventsUpdate(chatId: $chatId, userId: $userId) {
    id
    chatId
    content
    ...
  }
}

How It Works

  1. On deployment: A CloudFormation Custom Resource triggers the table-init Lambda
  2. Tables created: DynamoDB tables are created with proper key schemas and GSIs
  3. Schema stored: GraphQL schema is stored to S3
  4. EventBridge triggers: S3 event triggers the schema-sync Lambda
  5. AppSync updated: Schema and resolvers are created in AppSync
  6. Idempotent: Safe to run multiple times - existing tables are skipped

Accessing Initial Tables with SDK

import { RdbClient } from '@realdb/client';
import { z } from 'zod';

// The schema matches your initialTables definition
const ChatEventSchema = z.object({
  id: z.string(),
  chatId: z.string(),
  eventType: z.string(),
  content: z.string().optional(),
  userId: z.string(),
  timestamp: z.string(),
});

const client = new RdbClient({
  endpoint: 'https://your-api.execute-api.us-east-1.amazonaws.com/prod',
  apiKey: 'my-app-dev', // Same as initialTablesApiKey
});

const chatEvents = client.tableWithSchema('chatEvents', ChatEventSchema);

// Real-time streaming with publish (no DB write, instant delivery)
await chatEvents.publish({
  id: 'msg-123',
  chatId: 'chat-456',
  eventType: 'message_chunk',
  content: 'Hello...',
  userId: 'user-789',
  timestamp: new Date().toISOString(),
});

// Subscribe to updates
const subscription = chatEvents.subscribe({
  filters: { chatId: 'chat-456' },
  onData: (event) => console.log('Received:', event),
});
subscription.connect();

⚙️ Configuration Options

RdbConstructProps

| Property | Type | Default | Description | |----------|------|---------|-------------| | initialTables | InitialTableConfig[] | undefined | RECOMMENDED Tables to create on deployment with auto AppSync schema sync | | initialTablesApiKey | string | auto-generated | API key for namespacing initial tables in AppSync schema | | resourceSuffix | string | '' | Suffix for all resource names (e.g., 'prod' → 'rdb-tables-prod') | | apiPrefix | string | '' | API route prefix (e.g., 'v1' → '/v1/tables', '/v1/tables/{name}/records') | | existingApi | RestApi | undefined | Use existing API Gateway instead of creating new one | | existingTablesTable | ITable | undefined | Use existing DynamoDB table for metadata | | removalPolicy | RemovalPolicy | DESTROY | Resource removal policy. Use RETAIN for production to prevent data loss | | billingMode | BillingMode | PAY_PER_REQUEST | DynamoDB billing mode (PAY_PER_REQUEST or PROVISIONED) | | enableXRayTracing | boolean | true | Enable AWS X-Ray tracing on AppSync and Lambda | | logRetention | RetentionDays | ONE_WEEK | CloudWatch log retention for Lambda functions | | enableApiLogging | boolean | true | Enable CloudWatch logging for API Gateway | | corsOrigins | string[] | ['*'] | Allowed CORS origins for API Gateway |

📚 Public Properties

After creating the construct, you can access the following resources:

const rdb = new RdbConstruct(this, 'Rdb');

// APIs
rdb.api                          // API Gateway REST API (undefined if existingApi provided)
rdb.appSyncApi                   // AppSync GraphQL API

// Storage
rdb.configBucket                 // S3 bucket for schema storage (versioned)
rdb.tablesTable                  // DynamoDB table for table metadata
rdb.apiKeysTable                 // DynamoDB table for API keys

// Queues
rdb.tableDecommissionQueue       // SQS queue for async table deletion

// Secrets
rdb.apiKeySecret                 // Secrets Manager secret for API keys

// Lambda Functions
rdb.tableManagementFunction      // Table CRUD operations
rdb.recordsManagementFunction    // Record CRUD operations
rdb.apiKeyManagementFunction     // API key management
rdb.schemaSyncFunction           // Schema synchronization from S3
rdb.tableDecommissionFunction    // Async table deletion
rdb.authorizerFunction           // API Gateway Lambda authorizer
rdb.sdkConfigFunction            // SDK configuration endpoint
rdb.tableInitFunction            // Initial table creation (only if initialTables provided)

// Custom Resources
rdb.tableInitCustomResource      // CloudFormation custom resource for initial tables

Example: Grant Additional Permissions

const rdb = new RdbConstruct(this, 'Rdb');

// Grant an external Lambda access to RDB tables
myLambda.addToRolePolicy(new iam.PolicyStatement({
  actions: ['dynamodb:GetItem', 'dynamodb:Query'],
  resources: [rdb.tablesTable.tableArn],
}));

// Allow external service to read schemas from S3
myService.addToRolePolicy(new iam.PolicyStatement({
  actions: ['s3:GetObject'],
  resources: [`${rdb.configBucket.bucketArn}/*`],
}));

🔌 Using with the SDK

After deploying the infrastructure, use the @realdb/client SDK to interact with your database:

npm install @realdb/client zod

Complete Example

import { RdbClient } from '@realdb/client';
import { z } from 'zod';

// Initialize client with deployed endpoints
const client = new RdbClient({
  endpoint: 'https://your-api-id.execute-api.us-east-1.amazonaws.com/prod',
  apiKey: 'your-api-key',
  apiPrefix: 'v1', // Must match the apiPrefix in CDK deployment
});

// Define schema with Zod
const UserSchema = z.object({
  id: z.string().optional(),
  name: z.string().min(1),
  email: z.string().email(),
  age: z.number().int().positive().optional(),
});

// Create table from schema
await client.createTableFromSchema('users', UserSchema, {
  indexedFields: ['email'],
  subscriptions: [
    {
      filters: [
        { field: 'email', type: 'string' }
      ]
    }
  ]
});

// Get typed table instance
const users = client.tableWithSchema('users', UserSchema);

// Create record (type-safe!)
const result = await users.create({
  name: 'John Doe',
  email: '[email protected]',
  age: 30
});

// Subscribe to real-time updates
const subscription = users.subscribe({
  filters: {},
  onData: (user) => {
    console.log('Real-time update:', user);
    // user is typed!
  },
  onError: (error) => {
    console.error('Subscription error:', error);
  }
});

subscription.connect();

See the SDK documentation for complete API reference.

📤 CloudFormation Outputs

Add outputs to your stack to easily access deployed resources:

const rdb = new RdbConstruct(this, 'Rdb');

new cdk.CfnOutput(this, 'ApiEndpoint', {
  value: rdb.api!.url,
  description: 'API Gateway endpoint',
  exportName: 'RdbApiEndpoint',
});

new cdk.CfnOutput(this, 'AppSyncEndpoint', {
  value: rdb.appSyncApi.graphqlUrl,
  description: 'AppSync GraphQL endpoint for subscriptions',
  exportName: 'RdbAppSyncEndpoint',
});

new cdk.CfnOutput(this, 'AppSyncApiKey', {
  value: rdb.appSyncApi.apiKey || 'N/A',
  description: 'AppSync API key',
});

new cdk.CfnOutput(this, 'ConfigBucket', {
  value: rdb.configBucket.bucketName,
  description: 'S3 bucket for schema storage',
});

new cdk.CfnOutput(this, 'TablesTableName', {
  value: rdb.tablesTable.tableName,
  description: 'DynamoDB table for metadata',
});

API Reference

Lambda Functions

| Function | Purpose | Timeout | Memory | |----------|---------|---------|--------| | table-management | Create/list/delete tables | 15s | 512MB | | records-management | CRUD operations on records | 30s | 512MB | | api-key-management | Create/manage API keys | 15s | 256MB | | schema-sync | Sync schemas to AppSync | 5min | 128MB | | table-decommission | Async table deletion | 10min | 128MB | | table-init | Initial table creation via Custom Resource | 10min | 128MB | | authorizer | API Gateway authorization | 10s | 128MB | | sdk-config | SDK configuration endpoint | 10s | 128MB |

DynamoDB Tables

rdb-tables (or rdb-tables-{suffix}):

  • Partition Key: apiKey (STRING)
  • Sort Key: tableName (STRING)
  • Purpose: Store table metadata and schemas

rdb-api-keys (or rdb-api-keys-{suffix}):

  • Partition Key: apiKeyId (STRING)
  • Purpose: Store API key metadata (keys encrypted in Secrets Manager)

🤝 Contributing

Contributions are welcome! Please:

  1. Fork the repository
  2. Create a feature branch
  3. Submit a pull request

📄 License

MIT License - see LICENSE for details.

🔗 Related Resources

AWS Documentation:

📞 Support

For questions or issues:


Built with ❤️ using AWS CDK, TypeScript, and serverless technologies