Open-source project
Dynamoid/dynamoid avatar
Dynamoid/dynamoid

Dynamoid: an ActiveRecord-shaped ORM for Amazon DynamoDB

Ruby ORM for Amazon's DynamoDB.

612 stars192 forksRubyMIT

At a glance

What is it?
Dynamoid gives Ruby programmers the interface they already know for a key-value database without fixed columns or a query planner. A model includes a document module and declares its fields, and after that the create, where, and update methods behave the way they do in a relational object mapper, with associations, callbacks, validations, dirty tracking, optimistic locking, and transactions on top.
Who is it for?
Dynamoid fits a Ruby team that has decided to keep its data in DynamoDB, or a team migrating an application from a relational database and hoping to keep the model layer unchanged. It does not fit a project that needs relational queries, because the data model underneath is a key-value store and the interface cannot invent joins that the store does not have.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 3 days ago.
What is it written in?
Mainly Ruby, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 24, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What it means to look like ActiveRecord

Dynamoid is described as a feature-rich and powerful Ruby ORM for Amazon DynamoDB, designed to provide a familiar ActiveRecord-like experience for Ruby applications.

The word familiar is doing the work, and it is the reason the project exists. DynamoDB is a key-value store with a handful of query shapes, and the object mapper for it in most languages is a different thing from the one for a relational database. A Ruby developer arriving at DynamoDB from a relational background has to learn a new set of idioms, and that is a cost paid per developer and per project.

So the feature list is a list of the familiar things, restated for the new backend. There is an ActiveRecord-style domain specific language, implementing an interface and a configuration similar to the framework it is imitating. There are methods for finding, querying, and updating models. There are the advanced features an object mapper is expected to have: associations, callbacks, validations, the dirty API for tracking unsaved changes, optimistic locking, and type casting.

And then there is the one that is specific to the backend. DynamoDB natively supports a small set of attribute types, and the library supports types that are not among them, with date and time types named as examples.

That last one is a real design decision rather than a convenience. A document store that only stores what the database natively understands forces every date into a string, and a string date is a string date for the rest of the project's life, with parsing in every place it is read. A library that stores a time as a number and reads it back as a time keeps the rest of the application honest.

And the transactions. DynamoDB has a transactional operation, and a library that supports it lets an application keep the consistency it had on a relational database, which is the other half of the reason people leave relational databases reluctantly.

The project is MIT licensed, the last push was on 2026-10-22, and the release history shows a steady minor line with a version in July 2026 after two in the spring.

Defining a model

A model is a class that includes the document module and declares its fields, and the declarations are what an ActiveRecord user would expect.

ruby
class User
  include Dynamoid::Document

  field :name                        # Type defaults to :string
  field :email
  field :age, :integer
  field :active, :boolean, default: true

  validates :name, presence: true
  validates :email, format: { with: /@/ }
end

The field declaration takes a name, an optional type, and an optional default, and the type defaults to a string when it is not given. The comment in the example says so, and that is the sort of detail the declaration syntax gets right: the common case is short and the uncommon case is still one line.

The validations are the ActiveModel ones, which is worth pausing on. The library depends on the ActiveModel component of the framework, so the validation syntax is not a reimplementation, it is the real thing. A validation written for a relational model in the same application works here, including the custom ones, and the error messages behave the same way.

The readme also mentions automatic timestamps, so a model that includes the document module and declares no timestamps still gets them, which is the same default the framework's own models have.

What is not visible in the snippet is the mapping to the storage layer, and that is the interesting part of the design. In a relational database, a class maps to a table and a field maps to a column, and the schema is declared separately. In DynamoDB there is no table to declare, so the question is what the key of the item looks like and what the attributes are called.

The name of the field is used in the declaration, and the library derives the storage representation from the declared type rather than from a separate schema file. That is what makes the model the only artefact you write, and it is the same trick Mongoid plays with a document database, which is not a coincidence, as the credits section explains.

Querying, and the interface you already have

The usage examples are the ones from the framework, and that is the point of the whole project.

ruby
# Create and Save
user = User.create(name: 'Josh', email: '[email protected]')

# Find and Update
user = User.where(email: '[email protected]').first
user.update_attributes(age: 30)

# Querying
users = User.where(active: true).all.to_a

Create takes the attributes as keyword arguments and returns the object. The where chain takes conditions, first takes the first match, and all takes the whole set, with a conversion to an array at the end because the underlying result is lazy.

That last detail is the one that deserves a second look. The all method returns something you convert to an array, which means the query is not executed when you call it. On a relational database that is the normal contract and nobody notices it. On DynamoDB, where a query can be a direct key lookup or a full scan of a partition, the difference between a lazy and an eager result is the difference between reading one item and reading a million, and the library has inherited the lazy contract from the framework it imitates.

That is a design choice with a real cost, and it is worth being explicit about it: an application that calls all and then counts the result has asked for a scan whether it meant to or not, and the interface will not stop it.

The update method is named after the older framework method, and the readme uses it, which is a small sign of how closely the project tracks the interface it is emulating. The dirty API, which is what makes the update method able to write only the attributes that changed, and optimistic locking, which is how a concurrent write is detected rather than silently overwriting, are both in the feature list and both matter more on a store with no transactions around your reads than they do on a relational database.

The associations are the other big one. The feature list names them without detail, and they are the part of an object mapper that is hardest to do without a schema, because a relationship in DynamoDB is a choice about what to store on each side rather than a foreign key constraint.

So the reading of this library is: it is the ActiveRecord interface with a key-value storage engine underneath, and the places where the abstraction leaks are the places where the key-value model shows through, chiefly in the cost of a query that is not a key lookup.

Configuration, and a local database for development

The configuration is three settings: the access key, the secret key, and the region. The readme says the minimal connection settings are required, and it shows two places to put them, an initializer file in a Rails application or a block in your own setup code.

That is deliberately a small surface. There is no connection pool to size, no endpoint to override for a local database, no retry policy, and no timeout to tune. A library that offered all of those would be modelling a relational client rather than a DynamoDB client, and the interface it is imitating has none of them either.

The interesting omission is the local endpoint, and the repository explains how the development environment works instead: there is a compose file, and it is four lines long.

yaml
version: '2'

services:
  dynamodb:
    image: amazon/dynamodb-local
    ports:
      - 8000:8000

So the local database is the official local implementation, run in a container, with its port published. That is a much better answer than a mock, because the local implementation speaks the same protocol as the service, so the code that talks to it in development is the code that talks to the service in production, with only the endpoint and the credentials different.

That is why the configuration block in the readme uses obvious placeholders rather than real looking keys. That is a small piece of security hygiene worth copying: a configuration example that looks like a real credential is a configuration example that ends up in a repository somewhere with a real credential in it.

The rest of the repository supports the development story. There is a directory of gem files, which is the conventional way to test a library against several dependency versions, and a file for the appraisals tool that generates them. There is a Vagrant file, which dates the project, and a directory for the development container, which is its replacement. There is a test configuration, a coverage configuration, a lint configuration for the Ruby linter with four separate override files for the gem specification, the performance rules, the test rules, and the thread safety rules.

That last one is worth noticing. Thread safety gets its own lint configuration in a library that talks to a database from a Ruby process, where the concurrency model is a set of fibers and threads sharing a client.

The support window, and what the badges say

The compatibility section is unusually detailed, and it says two different things.

The first is the dependency floor: the library relies on the third generation of the AWS SDK for the DynamoDB service and on the ActiveModel component of the framework, at version 4.2 or newer. The second is the official support: Ruby 2.3 and newer, and Rails 4.2 and newer.

And then the tested matrix: Ruby from 2.3 to 4.0, including JRuby 10, and Rails from 4.2 to 8.1.

That last line is the interesting one. A library that officially supports Ruby 2.3, a version released in 2015, while testing against Ruby 4.0 is making a statement about how far back its users are. In a Ruby ecosystem, the installed base of old versions is large, and a library that drops them is a library that forces its users to upgrade their runtime before they can upgrade their dependencies. The tested matrix is the compromise, and it is a wide one.

The badge row in the readme tells the same story in a different way. There is the build status for the supported versions, a maintainability rating from a code analysis service, coverage from two different services, a triage status for the issue tracker, a contributor count, the generated documentation, the licence, and badges for the semantic versioning and changelog conventions the project follows.

The maintainability and coverage badges are the ones worth noticing, because they are the ones a maintainer chooses to display. A project that puts a maintainability score next to its version badge is a project that has decided its internal structure is a thing to be measured, and one that displays two separate coverage services is a project that treats the coverage number as something a reader should be able to check.

The commit message convention badge and the changelog convention badge are smaller, but they are the ones a contributor notices: the project uses a standard commit format and keeps a changelog in a known format, which means the release notes are generated rather than written, and that a contributor who follows the convention gets their change in the changelog without asking.

Where it came from, and who maintains it

The credits section is the last substantive part of the readme, and it tells a story.

Dynamoid was originally created in 2012, and the original repository and its author are named. Historical issues and discussions from before the project's migration in 2015 are still available in a legacy issue tracker, which is a courtesy that costs one link and saves a lot of confused searching.

And then the sentence that explains the whole design: the project borrows code, structure, and even its name very liberally from Mongoid, and without Mongoid to crib from none of this would have been possible. The author says he hopes they do not mind the reuse of their ideas to make DynamoDB as accessible to the Ruby world as MongoDB.

That is an unusually direct admission, and it explains the shape of the library better than any feature list could. Mongoid is the object mapper for a document database, and DynamoDB is a document store with a different query model. Taking Mongoid's structure and pointing it at a different backend gives you an object mapper whose interface a Ruby developer recognises, and it explains why the interface is a document mapper's interface rather than a relational one's: create, where, first, all, and the dirty tracking, and the associations that store a related document's key rather than a foreign key column.

The contributor list follows, with fifteen named people, and a footnote marks the current maintainers with an asterisk. Listing maintainers explicitly is a small thing that answers the question a reader of any long-lived gem always has, which is whether there is anybody left who can merge a pull request.

The license is MIT, with the standard note about where the official copyright notice lives, and the project has a security policy and a contributing guide, both linked.

So the whole picture is a document object mapper for a key-value store, modelled on a document object mapper for a document store, with a support window that stretches back a decade, a measured maintainability score, and a maintainer list that is public.

And the practical question for anyone evaluating it remains the one the interface hides: how much of your application's data access is a key lookup, and how much is a scan. The first is where this library will feel exactly like the framework you already know.

Editorial conclusion

Dynamoid fits a Ruby team that has decided to keep its data in DynamoDB, or a team migrating an application from a relational database and hoping to keep the model layer unchanged. It does not fit a project that needs relational queries, because the data model underneath is a key-value store and the interface cannot invent joins that the store does not have. Verify first which query patterns your application actually uses, because the object mapper hides the difference until you need a scan, that the local database used in development behaves like the real one, and that the version of the library you pin matches the version of the AWS SDK your application already uses.

Frequently asked questions

What does the Dynamoid library do?

It is a Ruby ORM for Amazon DynamoDB that presents an ActiveRecord-style interface. It provides methods for finding, querying, and updating models, and supports associations, callbacks, validations, the dirty API, optimistic locking, type casting, attribute types the database does not have natively, and transactional operations.

How do I define a model with Dynamoid?

Include the document module in the class and declare the fields with the field method, which takes a name, an optional type that defaults to a string, and an optional default. ActiveModel validations work as they do in the framework, and automatic timestamps are supported.

What does Dynamooid need to connect to Amazon DynamoDB?

Three settings: the access key, the secret key, and the region. They can go in an initializer file in a Rails application or in a configuration block in your own setup code. There is no connection pool to size and no endpoint setting, because the interface it imitates does not have them either.

How do I run Dynamoid against a local database in development?

The repository ships a compose file that runs the official local implementation of DynamoDB in a container and publishes its port. Using the local implementation rather than a mock means the code that talks to it in development speaks the same protocol as the service in production.

Which Ruby and Rails versions does Dynamoid support?

It officially supports Ruby 2.3 and newer and Rails 4.2 and newer, and it is tested against Ruby from 2.3 to 4.0, including JRuby 10, and Rails from 4.2 to 8.1. It relies on the third generation of the AWS SDK and on ActiveModel at version 4.2 or newer.

Official sources

  1. Dynamoid/dynamoid on GitHub
  2. Issues
  3. License: MIT
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/dynamoid-dynamoid.svg)](https://hysenlabs.com/projects/dynamoid-dynamoid)

Was this useful?