dynamoid-cdk-schema

Generate AWS CDK DynamoDB tables straight from your Dynamoid models.

Your Dynamoid models already declare their keys and indexes. Restating that schema by hand in your CDK stack means two sources of truth that drift the moment you add a GSI. This gem reads a model's schema — partition and sort keys, global and local secondary indexes, attribute types — and builds the matching TableV2, so the model is the single source of truth for both the application and its infrastructure.

It works because the AWS CDK is a first-class Ruby library your infrastructure code can require and reflect over — the same reason it can read your Dynamoid models. There is no separate template to keep in sync.

Installation

bundle add dynamoid-cdk-schema

aws-cdk-lib is intentionally not a dependency — it's your CDK app's, and it's referenced lazily, only when you build a table. So the introspection half (.describe) works anywhere Dynamoid is loaded.

Usage

Inside a CDK stack (where aws-cdk-lib and your Dynamoid models are already loaded):

class MyStack < AWSCDK::Stack
  def initialize(scope, id, **kwargs)
    super

    # Build a TableV2 from the model. Extra keyword args pass straight through to
    # TableV2, so the model owns keys + indexes and you own table-level config.
    posts = Dynamoid::CDK::Schema.table(self, "Posts", Post,
                                        removal_policy: AWSCDK::RemovalPolicy::RETAIN)

    # `posts` is an ordinary AWSCDK::DynamoDB::TableV2 — grant it, reference it, etc.
    posts.grant_read_write_data(my_function)
  end
end

That's it. If Post declares a global_secondary_index or range key, the table gets it, with the right attribute types and projection.

Just the schema, as data

.describe needs only Dynamoid (no CDK) and returns a plain, immutable descriptor — handy for tests, tooling, or feeding another target:

Dynamoid::CDK::Schema.describe(Post)
# => #<data Dynamoid::CDK::Schema::Descriptor
#      partition_key=#<data Attribute name="id", type=:string>,
#      sort_key=nil,
#      global_secondary_indexes=[#<data Index name="by_recency", ...>],
#      local_secondary_indexes=[]>

What it reads

  • Partition and sort keys, with DynamoDB attribute types (:string / :number / :binary) resolved the way Dynamoid resolves them.
  • Every global secondary index: keys, and the projection type — ALL, KEYS_ONLY, or INCLUDE (with its non_key_attributes).
  • Local secondary indexes.

Table-level configuration Dynamoid doesn't model (billing mode, TTL, point-in-time recovery, streams, removal policy) is yours to pass through as keyword arguments — this gem is about the schema, keys and indexes.

Loading your models at synth time

cdk synth has to be able to load the model classes you pass in, so require them from your CDK app. If a model reads application config at load time (e.g. its table name from Rails config), make that available in the synth process — the CDK never uses the physical table name (it assigns one), so a placeholder is enough.

Development

After checking out the repo, run bin/setup to install dependencies, then bundle exec rspec to run the tests and bundle exec rubocop to lint. bin/console gives an interactive prompt.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/omarqureshi/dynamoid-cdk-schema.

License

Available as open source under the terms of the MIT License.