Rails + DynamoDB on AWS


Problem

Sometimes you’ll need to install very specific verion of bundler and run bundle install for example on your Ruby on Rails project.

Super easy way to do that with command line:

Let’s start

Let’s add two gems into our Gemfile and set our AWS credentials:

gem 'dynamoid'
gem 'aws-sdk'

We can use aws.rb in initializers directory:

Aws.config.update({
  region: 'ca-central-1',
  credentials: Aws::Credentials.new('XXX', 'XXX'),
})

Let’s create model which is describes document with one validation:

class OrderLog
  include Dynamoid::Document
  field :order_id, :integer
  field :comment, :string
  field :created, :datetime

  validates_presence_of :comment
end

We’re using interactor gem here, so one more line in AddLog interactor:

class AddLog
  include Interactor

  def call
    # Call new model's create method
    OrderLog.new(comment: "ORDER_WITH_PRICE_#{context[:total_price]}").save!
    Rails.logger.info("ADDING ORDER WITH TOTAL PRICE: #{context[:total_price]}")
  end
end

Testing

As a first step, let’s try to create object from console:

irb(main):005:0> OrderLog.new(comment: "Hello").save!
[Aws::DynamoDB::Client 200 0.126174 0 retries] list_tables(exclusive_start_table_name:nil)

(147.02 ms) LIST TABLES
(147.09 ms) CACHE TABLES
Creating dynamoid__development_orderlogs table. This could take a while.
[Aws::DynamoDB::Client 200 0.061382 0 retries] create_table(table_name:"dynamoid__development_orderlogs",key_schema:[{attribute_name:"id",key_type:"HASH"}],attribute_definitions:[{attribute_name:"id",attribute_type:"S"}],billing_mode:"PROVISIONED",provisioned_throughput:{read_capacity_units:100,write_capacity_units:20})

[Aws::DynamoDB::Client 200 0.034059 0 retries] describe_table(table_name:"dynamoid__development_orderlogs")

Checked table status for dynamoid__development_orderlogs (check {:again=>true, :status=>"CREATING", :counter=>0})
[Aws::DynamoDB::Client 200 0.041893 0 retries] describe_table(table_name:"dynamoid__development_orderlogs")

Checked table status for dynamoid__development_orderlogs (check {:again=>true, :status=>"CREATING", :counter=>1})
[Aws::DynamoDB::Client 200 0.032142 0 retries] describe_table(table_name:"dynamoid__development_orderlogs")

Checked table status for dynamoid__development_orderlogs (check {:again=>false, :status=>"ACTIVE", :counter=>2})
(6181.32 ms) CREATE TABLE
[Aws::DynamoDB::Client 200 0.028371 0 retries] put_item(table_name:"dynamoid__development_orderlogs",item:{"comment"=>{s:"Hello"},"created_at"=>{n:"1646972339.942993"},"updated_at"=>{n:"1646972339.943931"},"id"=>{s:"c898ac71-dd82-49c4-adb5-ba68965161cc"}},expected:{"id"=>{exists:false}})

(29.33 ms) PUT ITEM - ["dynamoid__development_orderlogs", {:comment=>"Hello", :created_at=>0.1646972339942993e10, :updated_at=>0.1646972339943931e10, :id=>"c898ac71-dd82-49c4-adb5-ba68965161cc"}, {}]
=> #<OrderLog:0x0000000119a259f8 @new_record=false, @attributes={:comment=>"Hello", :created_at=>Fri, 11 Mar 2022 04:18:59 +0000, :updated_at=>Fri, 11 Mar 2022 04:18:59 +0000, :id=>"c898ac71-dd82-49c4-adb5-ba68965161cc"}, @associations={}, @attributes_before_type_cast={:comment=>"Hello", :created_at=>Fri, 11 Mar 2022 04:18:59 UTC +00:00, :updated_at=>Fri, 11 Mar 2022 04:18:59 UTC +00:00, :id=>"c898ac71-dd82-49c4-adb5-ba68965161cc"}, @validation_context=nil, @errors=#<ActiveModel::Errors:0x0000000119a24f58 @base=#<OrderLog:0x0000000119a259f8 ...>, @messages={}, @details={}>, @changed_attributes={}, @_touch_record=nil, @previously_changed={"comment"=>[nil, "Hello"], "created_at"=>[nil, Fri, 11 Mar 2022 04:18:59 +0000], "updated_at"=>[nil, Fri, 11 Mar 2022 04:18:59 +0000], "id"=>[nil, "c898ac71-dd82-49c4-adb5-ba68965161cc"]}>

Cool!

Application testing

Now, let’s try to create an Order, and check console:

Started POST "/orders" for ::1 at 2022-03-10 23:25:38 -0500
Processing by OrdersController#create as HTML
  Parameters: {"authenticity_token"=>"Ksy6rbisUpKK3uEjwEyv3piwDR4YB18zcUAe84bQqSUjvOUsDXSLLpBBkPkLAGNNhTeBau6G/GefPpOGX+VDMA==", "order"=>{"title"=>"DYNAMO", "total_price"=>"999"}, "commit"=>"Create Order"}
   (0.2ms)  BEGIN
  ↳ app/interactors/order_init.rb:7:in `call'
  Order Create (0.7ms)  INSERT INTO "orders" ("title", "total_price", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id"  [["title", "DYNAMO"], ["total_price", "999.0"], ["created_at", "2022-03-11 04:25:38.684555"], ["updated_at", "2022-03-11 04:25:38.684555"]]
  ↳ app/interactors/order_init.rb:7:in `call'
   (2.1ms)  COMMIT
  ↳ app/interactors/order_init.rb:7:in `call'
[Aws::DynamoDB::Client 200 0.338816 0 retries] list_tables(exclusive_start_table_name:nil)

(357.38 ms) LIST TABLES
(357.51 ms) CACHE TABLES
[Aws::DynamoDB::Client 200 0.063373 0 retries] put_item(table_name:"dynamoid__development_orderlogs",item:{"comment"=>{s:"ORDER_WITH_PRICE_999"},"created_at"=>{n:"1646972739.051647"},"updated_at"=>{n:"1646972739.052237"},"id"=>{s:"5504f1ea-d04f-4226-9488-c62377a0bebe"}},expected:{"id"=>{exists:false}})

(65.85 ms) PUT ITEM - ["dynamoid__development_orderlogs", {:comment=>"ORDER_WITH_PRICE_999", :created_at=>0.1646972739051647e10, :updated_at=>0.1646972739052237e10, :id=>"5504f1ea-d04f-4226-9488-c62377a0bebe"}, {}]
ADDING ORDER WITH TOTAL PRICE: 999
Completed 200 OK in 441ms (Views: 0.8ms | ActiveRecord: 3.0ms | Allocations: 39941)

Works!

5 mins with AWS console

Let’s go to the AWS console and look at DynamoDB:

Here you can see generated table name and status:

image

Table structure:

image

Our test file was created!

image

Let’s explore our table entry:

image

Conclusion

So, as you can see it’s super easy to connect from your Rails application to the DynamoDB instanse running on AWS.

Happy coding!