<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: DynoTable</title>
    <description>The latest articles on DEV Community by DynoTable (@dynotable).</description>
    <link>https://dev.to/dynotable</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4014872%2F0b044873-dd8b-4089-b6ea-b058a67bc3dd.jpg</url>
      <title>DEV Community: DynoTable</title>
      <link>https://dev.to/dynotable</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dynotable"/>
    <language>en</language>
    <item>
      <title>From the Dynamo Paper to DynamoDB</title>
      <dc:creator>DynoTable</dc:creator>
      <pubDate>Sun, 05 Jul 2026 10:07:42 +0000</pubDate>
      <link>https://dev.to/dynotable/from-the-dynamo-paper-to-dynamodb-28oa</link>
      <guid>https://dev.to/dynotable/from-the-dynamo-paper-to-dynamodb-28oa</guid>
      <description>&lt;p&gt;The 2007 "Dynamo: Amazon's Highly Available Key-value Store" paper and the&lt;br&gt;
DynamoDB you call today share a name and a goal — predictable performance at any&lt;br&gt;
scale — but they are not the same system. The paper described an internal,&lt;br&gt;
eventually-consistent store you ran yourself. DynamoDB is a managed service that&lt;br&gt;
kept the lessons and threw out most of the machinery.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is DynamoDB based on the Dynamo paper?
&lt;/h2&gt;

&lt;p&gt;Partly. DynamoDB takes its name and core goals — predictable performance and high availability at scale — from the 2007 Amazon Dynamo paper, and it kept the &lt;a href="https://dynotable.com/docs/glossary#partition-key" rel="noopener noreferrer"&gt;partition-key&lt;/a&gt; hashing idea almost verbatim. But it is a different, managed system: the paper's vector clocks, gossip membership, and tunable read/write quorums are gone, replaced by AWS-owned internals.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The paper solved availability, not ergonomics.&lt;/strong&gt; Its job was to never reject
a write during a holiday-traffic spike, even at the cost of returning a stale read.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DynamoDB kept the shape, replaced the internals.&lt;/strong&gt; Partitioned by a hash of
the key, replicated across AZs, scaled horizontally — but the conflict-resolution
guts (vector clocks, gossip, read-repair) are gone.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You no longer tune the knobs.&lt;/strong&gt; &lt;code&gt;N&lt;/code&gt;, &lt;code&gt;R&lt;/code&gt;, and &lt;code&gt;W&lt;/code&gt; from the paper became one
choice: &lt;code&gt;ConsistentRead&lt;/code&gt; true or false. AWS owns the rest.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The mental model still pays off.&lt;/strong&gt; Knowing the lineage explains why a &lt;code&gt;Scan&lt;/code&gt;
is expensive and why a GSI read can lag — both fall out of the original design.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What the paper was actually solving
&lt;/h2&gt;

&lt;p&gt;Amazon's shopping cart could not go down. A relational database that refused&lt;br&gt;
writes under load — or blocked on a failed replica — was unacceptable. The 2007&lt;br&gt;
Dynamo paper chose &lt;strong&gt;availability over consistency&lt;/strong&gt;: always accept the write,&lt;br&gt;
reconcile disagreements later. That trade is the root of everything below.&lt;/p&gt;

&lt;p&gt;To do that without a single master, Dynamo had to answer two questions on its own:&lt;br&gt;
where does a key live, and how many copies must agree before a read or write counts?&lt;/p&gt;

&lt;h2&gt;
  
  
  Consistent hashing: where a key lives
&lt;/h2&gt;

&lt;p&gt;The paper placed every node on a hash ring. A key's position is the hash of its&lt;br&gt;
key; it's owned by the next node clockwise, and replicated to the following &lt;code&gt;N-1&lt;/code&gt;&lt;br&gt;
nodes. Adding or removing a node only reshuffles its neighbors' keys — not the&lt;br&gt;
whole dataset. That's &lt;strong&gt;consistent hashing&lt;/strong&gt;, and it's the one idea DynamoDB kept&lt;br&gt;
almost verbatim.&lt;/p&gt;

&lt;p&gt;DynamoDB still hashes your &lt;a href="https://dynotable.com/docs/glossary#partition-key" rel="noopener noreferrer"&gt;partition key&lt;/a&gt; to decide which physical partition stores&lt;br&gt;
the item. Pick a low-cardinality partition key — say &lt;code&gt;STATUS&lt;/code&gt; with two values —&lt;br&gt;
and every item with the same value lands in the same partition. That's the &lt;strong&gt;&lt;a href="https://dynotable.com/docs/glossary#hot-partition" rel="noopener noreferrer"&gt;hot partition&lt;/a&gt;&lt;/strong&gt; footgun, and it's a direct consequence of the ring: the hash sends&lt;br&gt;
identical keys to identical homes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quorum: how many copies must agree
&lt;/h2&gt;

&lt;p&gt;The paper's second knob was a &lt;strong&gt;quorum&lt;/strong&gt;. With &lt;code&gt;N&lt;/code&gt; replicas, a write succeeds once&lt;br&gt;
&lt;code&gt;W&lt;/code&gt; of them ack, and a read consults &lt;code&gt;R&lt;/code&gt; of them. Set &lt;code&gt;R + W &amp;gt; N&lt;/code&gt; and any read&lt;br&gt;
overlaps at least one node holding the newest write — strong consistency. Set them&lt;br&gt;
lower and you trade freshness for speed and uptime.&lt;/p&gt;

&lt;p&gt;Dynamo ran "sloppy" quorums: if a target node was down, the write went to a&lt;br&gt;
stand-in and was handed back later (hinted handoff). Conflicting versions were&lt;br&gt;
tagged with &lt;strong&gt;vector clocks&lt;/strong&gt; and reconciled by the application on read.&lt;/p&gt;

&lt;h2&gt;
  
  
  What DynamoDB kept versus changed
&lt;/h2&gt;

&lt;p&gt;DynamoDB inherited the goals and the partitioning, then deleted the parts that&lt;br&gt;
made the original hard to operate.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concern&lt;/th&gt;
&lt;th&gt;2007 Dynamo paper&lt;/th&gt;
&lt;th&gt;DynamoDB today&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Key placement&lt;/td&gt;
&lt;td&gt;Consistent hashing ring&lt;/td&gt;
&lt;td&gt;Hash of partition key → managed partition&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Replication&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;N&lt;/code&gt; nodes, you choose&lt;/td&gt;
&lt;td&gt;3 copies across AZs, fixed by AWS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Consistency knobs&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;R&lt;/code&gt;, &lt;code&gt;W&lt;/code&gt; quorum tuning&lt;/td&gt;
&lt;td&gt;One flag: &lt;code&gt;ConsistentRead&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Conflict resolution&lt;/td&gt;
&lt;td&gt;Vector clocks, app-side merge on read&lt;/td&gt;
&lt;td&gt;None needed in-Region — writes serialize through a leader replica; last-writer-wins only across Regions in global tables&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Membership&lt;/td&gt;
&lt;td&gt;Gossip protocol between peers&lt;/td&gt;
&lt;td&gt;Fully managed; invisible to you&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multi-key ops&lt;/td&gt;
&lt;td&gt;None — pure key-value&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Query&lt;/code&gt;, GSIs, transactions layered on top&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The paper's API was two calls: &lt;code&gt;get(key)&lt;/code&gt; and &lt;code&gt;put(key, value)&lt;/code&gt;. DynamoDB added a&lt;br&gt;
sort key, indexes, and queries on top of the same key-value core — which is why a&lt;br&gt;
&lt;code&gt;Query&lt;/code&gt; is cheap (one partition) and a &lt;code&gt;Scan&lt;/code&gt; is not (it walks every partition the&lt;br&gt;
ring ever created).&lt;/p&gt;

&lt;h2&gt;
  
  
  How a write travels, then and now
&lt;/h2&gt;

&lt;p&gt;The flow below contrasts the paper's quorum write with DynamoDB's managed one. The&lt;br&gt;
shape rhymes; the responsibility moved from your code to AWS.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Diagram: &lt;a href="https://dynotable.com/learn/dynamo-paper-to-dynamodb" rel="noopener noreferrer"&gt;view on dynotable.com&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In the paper you owned the quorum math and the merge; in DynamoDB that whole lower&lt;br&gt;
half is managed, and you only choose &lt;code&gt;ConsistentRead&lt;/code&gt; per request.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the lineage leaks into your code
&lt;/h2&gt;

&lt;p&gt;The eventual-consistency default is the paper showing through. A global secondary&lt;br&gt;
index is replicated asynchronously, so a freshly written item can be missing from&lt;br&gt;
the index for a moment — the same "reconcile later" bargain, just at the index&lt;br&gt;
layer. See &lt;a href="https://dynotable.com/learn/gsi-vs-lsi" rel="noopener noreferrer"&gt;GSI vs LSI&lt;/a&gt; for when that lag matters.&lt;/p&gt;

&lt;p&gt;You buy back strong consistency two ways. Use &lt;code&gt;ConsistentRead: true&lt;/code&gt; on a base-table&lt;br&gt;
read (it routes to the leader copy), or guard a write with a &lt;code&gt;ConditionExpression&lt;/code&gt;&lt;br&gt;
so it only lands if the item's current state matches. Sketch one in the&lt;br&gt;
&lt;a href="https://dynotable.com/tools/dynamodb-expression-builder" rel="noopener noreferrer"&gt;DynamoDB expression builder&lt;/a&gt; — for example&lt;br&gt;
&lt;code&gt;attribute_not_exists(PK)&lt;/code&gt; to make a &lt;code&gt;PutItem&lt;/code&gt; an insert-only operation, the&lt;br&gt;
modern stand-in for the paper's conflict detection.&lt;/p&gt;

&lt;h2&gt;
  
  
  The one thing to remember
&lt;/h2&gt;

&lt;p&gt;The paper optimized for never saying no to a write. DynamoDB inherited that bias,&lt;br&gt;
which is why its defaults favor availability and why strong reads cost more. Model&lt;br&gt;
your keys for single-partition &lt;code&gt;Query&lt;/code&gt;s, as in &lt;a href="https://dynotable.com/learn/single-table-design" rel="noopener noreferrer"&gt;single-table design&lt;/a&gt;,&lt;br&gt;
and reach for a &lt;a href="https://dynotable.com/learn/query-vs-scan" rel="noopener noreferrer"&gt;&lt;code&gt;Scan&lt;/code&gt; only when you truly must&lt;/a&gt; — the ring&lt;br&gt;
makes a full table walk as expensive as it sounds.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dynotable.com/download" rel="noopener noreferrer"&gt;Try DynoTable&lt;/a&gt; to browse your tables and their GSIs, then run JOINs and&lt;br&gt;
GROUP BY over your own data in the SQL Workbench.&lt;/p&gt;

</description>
      <category>dynamodb</category>
      <category>aws</category>
      <category>database</category>
      <category>nosql</category>
    </item>
    <item>
      <title>DynamoDB Data Types</title>
      <dc:creator>DynoTable</dc:creator>
      <pubDate>Sun, 05 Jul 2026 10:07:41 +0000</pubDate>
      <link>https://dev.to/dynotable/dynamodb-data-types-3k45</link>
      <guid>https://dev.to/dynotable/dynamodb-data-types-3k45</guid>
      <description>&lt;p&gt;Every DynamoDB attribute is tagged with a one- or two-letter type code in the wire&lt;br&gt;
format. Knowing the set matters because the type drives both how a value is stored&lt;br&gt;
and how it counts toward an item's size.&lt;/p&gt;

&lt;h2&gt;
  
  
  What data types does DynamoDB support?
&lt;/h2&gt;

&lt;p&gt;DynamoDB supports ten data types across three categories. &lt;strong&gt;Scalars&lt;/strong&gt; are String (&lt;code&gt;S&lt;/code&gt;), Number (&lt;code&gt;N&lt;/code&gt;), Binary (&lt;code&gt;B&lt;/code&gt;), Boolean (&lt;code&gt;BOOL&lt;/code&gt;), and Null (&lt;code&gt;NULL&lt;/code&gt;). &lt;strong&gt;Documents&lt;/strong&gt; are Map (&lt;code&gt;M&lt;/code&gt;) and List (&lt;code&gt;L&lt;/code&gt;), which nest other types. &lt;strong&gt;Sets&lt;/strong&gt; are String Set (&lt;code&gt;SS&lt;/code&gt;), Number Set (&lt;code&gt;NS&lt;/code&gt;), and Binary Set (&lt;code&gt;BS&lt;/code&gt;) — unordered, homogeneous, and non-empty. Only &lt;code&gt;S&lt;/code&gt;, &lt;code&gt;N&lt;/code&gt;, and &lt;code&gt;B&lt;/code&gt; can be a key.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Code&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;JSON / JS equivalent&lt;/th&gt;
&lt;th&gt;Example (DynamoDB-JSON)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;S&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;String&lt;/td&gt;
&lt;td&gt;Scalar&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{"S": "Ada"}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;N&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;Scalar&lt;/td&gt;
&lt;td&gt;number&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{"N": "37"}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;B&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Binary&lt;/td&gt;
&lt;td&gt;Scalar&lt;/td&gt;
&lt;td&gt;Uint8Array / base64&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{"B": "ZGF0YQ=="}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;BOOL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Boolean&lt;/td&gt;
&lt;td&gt;Scalar&lt;/td&gt;
&lt;td&gt;boolean&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{"BOOL": true}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;NULL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Null&lt;/td&gt;
&lt;td&gt;Scalar&lt;/td&gt;
&lt;td&gt;null&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{"NULL": true}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;M&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Map&lt;/td&gt;
&lt;td&gt;Document&lt;/td&gt;
&lt;td&gt;object&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{"M": {"k": {"S": "v"}}}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;L&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List&lt;/td&gt;
&lt;td&gt;Document&lt;/td&gt;
&lt;td&gt;array&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{"L": [{"N": "1"}]}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SS&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;String set&lt;/td&gt;
&lt;td&gt;Set&lt;/td&gt;
&lt;td&gt;— (no JSON type)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{"SS": ["a", "b"]}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;NS&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Number set&lt;/td&gt;
&lt;td&gt;Set&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{"NS": ["1", "2"]}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;BS&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Binary set&lt;/td&gt;
&lt;td&gt;Set&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{"BS": ["ZA=="]}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Scalars
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;S&lt;/code&gt;&lt;/strong&gt; — string (UTF-8; sized by its &lt;strong&gt;byte&lt;/strong&gt; length, not character count).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;N&lt;/code&gt;&lt;/strong&gt; — number, sent as a string for precision; up to 38 digits.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;B&lt;/code&gt;&lt;/strong&gt; — binary, sent base64-encoded.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;BOOL&lt;/code&gt;&lt;/strong&gt; — &lt;code&gt;true&lt;/code&gt; / &lt;code&gt;false&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;NULL&lt;/code&gt;&lt;/strong&gt; — an explicit null marker.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Documents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;M&lt;/code&gt;&lt;/strong&gt; — map (object). Nested attributes each keep their own type tag.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;L&lt;/code&gt;&lt;/strong&gt; — list. Elements may be mixed types.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"profile"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"M"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"S"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Ada"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"age"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"N"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"37"&lt;/span&gt;&lt;span class="p"&gt;}}}}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Sets
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;SS&lt;/code&gt;&lt;/strong&gt; — string set, &lt;strong&gt;&lt;code&gt;NS&lt;/code&gt;&lt;/strong&gt; — number set, &lt;strong&gt;&lt;code&gt;BS&lt;/code&gt;&lt;/strong&gt; — binary set.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sets are unordered, homogeneous, and can't be empty. Crucially, &lt;strong&gt;plain JSON has&lt;br&gt;
no set type&lt;/strong&gt; — an array round-trips as a list (&lt;code&gt;L&lt;/code&gt;), never an &lt;code&gt;SS&lt;/code&gt;/&lt;code&gt;NS&lt;/code&gt;. That's a&lt;br&gt;
real conversion limitation, not a bug; see the&lt;br&gt;
&lt;a href="https://dynotable.com/tools/dynamodb-json-converter" rel="noopener noreferrer"&gt;DynamoDB-JSON converter&lt;/a&gt; note.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which types can be a key?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://dynotable.com/docs/glossary#partition-key" rel="noopener noreferrer"&gt;Partition&lt;/a&gt; and &lt;a href="https://dynotable.com/docs/glossary#sort-key" rel="noopener noreferrer"&gt;sort&lt;/a&gt; keys — on the table and on any index — must be a &lt;strong&gt;scalar&lt;/strong&gt;,&lt;br&gt;
and only &lt;code&gt;S&lt;/code&gt;, &lt;code&gt;N&lt;/code&gt;, or &lt;code&gt;B&lt;/code&gt;. You can't key on a boolean, set, map, or list. Model a&lt;br&gt;
"composite" key by concatenating values into one &lt;code&gt;S&lt;/code&gt; (e.g. &lt;code&gt;ORDER#2026#42&lt;/code&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  Limits worth knowing
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;An item maxes out at &lt;strong&gt;&lt;a href="https://dynotable.com/learn/dynamodb-item-size-limit" rel="noopener noreferrer"&gt;400 KB&lt;/a&gt;&lt;/strong&gt; — every
attribute name plus value, including nested ones.&lt;/li&gt;
&lt;li&gt;Numbers carry up to &lt;strong&gt;38 digits&lt;/strong&gt; of precision (positive or negative).&lt;/li&gt;
&lt;li&gt;Maps and lists nest up to &lt;strong&gt;32 levels&lt;/strong&gt; deep.&lt;/li&gt;
&lt;li&gt;Sets are non-empty and homogeneous — no empty set, no mixing &lt;code&gt;S&lt;/code&gt; and &lt;code&gt;N&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why the type affects cost
&lt;/h2&gt;

&lt;p&gt;Item size is the sum of attribute-name bytes plus value bytes, and each type sizes&lt;br&gt;
differently — numbers are compacted, booleans and nulls are 1 byte, maps and lists&lt;br&gt;
add per-element overhead. That size rounds up to read/write&lt;br&gt;
&lt;a href="https://dynotable.com/learn/query-vs-scan" rel="noopener noreferrer"&gt;capacity units&lt;/a&gt;. Measure a real item with the&lt;br&gt;
&lt;a href="https://dynotable.com/tools/dynamodb-item-size-calculator" rel="noopener noreferrer"&gt;item-size calculator&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Do it in DynoTable
&lt;/h2&gt;

&lt;p&gt;The set-vs-list distinction above is the thing tooling usually hides. DynoTable's&lt;br&gt;
item editor makes it explicit with a format toggle:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Plain JSON&lt;/strong&gt; — primitives stay plain (&lt;code&gt;"age": 30&lt;/code&gt;), but sets keep their type
wrapper so they survive the round-trip: &lt;code&gt;"tags": { "SS": ["a", "b"] }&lt;/code&gt;,
&lt;code&gt;"scores": { "NS": ["1.5", "2.5"] }&lt;/code&gt;. This is the readable form for everyday
editing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DynamoDB JSON&lt;/strong&gt; — the canonical AWS &lt;a href="https://dynotable.com/docs/glossary#marshalling" rel="noopener noreferrer"&gt;marshalled&lt;/a&gt; form, where &lt;em&gt;every&lt;/em&gt; value
carries its type tag: &lt;code&gt;"age": { "N": "30" }&lt;/code&gt;, &lt;code&gt;"name": { "S": "alice" }&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Switching between them shows you exactly how each scalar, document, and set type&lt;br&gt;
is represented on the wire — and because the set types have no plain-JSON&lt;br&gt;
equivalent, the toggle is the only way to author an &lt;code&gt;SS&lt;/code&gt;/&lt;code&gt;NS&lt;/code&gt;/&lt;code&gt;BS&lt;/code&gt; by hand without&lt;br&gt;
hand-marshalling the whole item.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj6mvqafu9mz4zo8cbzq1.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj6mvqafu9mz4zo8cbzq1.webp" alt="DynoTable's item editor showing an item with a number set and a string set, and the Plain-JSON / DynamoDB-JSON toggle." width="800" height="514"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dynotable.com/download" rel="noopener noreferrer"&gt;Try DynoTable&lt;/a&gt; to see every attribute's type and the live byte count&lt;br&gt;
as you edit an item — and to filter or aggregate across typed attributes in the&lt;br&gt;
SQL Workbench, which reads each type tag for you. To convert a marshalled blob&lt;br&gt;
without the app, the &lt;a href="https://dynotable.com/tools/dynamodb-json-converter" rel="noopener noreferrer"&gt;DynamoDB-JSON converter&lt;/a&gt;&lt;br&gt;
does the same round-trip in the browser.&lt;/p&gt;

</description>
      <category>dynamodb</category>
      <category>aws</category>
      <category>database</category>
      <category>nosql</category>
    </item>
    <item>
      <title>How to Copy a DynamoDB Table to Another Account or Region</title>
      <dc:creator>DynoTable</dc:creator>
      <pubDate>Sat, 04 Jul 2026 10:43:21 +0000</pubDate>
      <link>https://dev.to/dynotable/how-to-copy-a-dynamodb-table-to-another-account-or-region-1n9o</link>
      <guid>https://dev.to/dynotable/how-to-copy-a-dynamodb-table-to-another-account-or-region-1n9o</guid>
      <description>&lt;p&gt;DynamoDB has &lt;strong&gt;no one-click "copy table" command&lt;/strong&gt; — not in the AWS CLI, not in the&lt;br&gt;
console. Copying or migrating a table across accounts or regions means picking one of&lt;br&gt;
four approaches, each with different cost, downtime, and fidelity tradeoffs. This guide&lt;br&gt;
covers all four, plus the operational gotchas that bite in production.&lt;/p&gt;
&lt;h2&gt;
  
  
  How do I copy a DynamoDB table to another account or region?
&lt;/h2&gt;

&lt;p&gt;There's no native copy-table command. Pick one of four approaches: a Scan + BatchWriteItem script for small one-off copies, an S3 export-then-import for large tables, AWS Backup copy + restore for cross-account moves with full fidelity, or global tables for ongoing live replication. Each restore or import creates a new table.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Situation&lt;/th&gt;
&lt;th&gt;Best approach&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Small table, one-off, full control&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Scan + BatchWriteItem&lt;/strong&gt; script&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Large table, can tolerate a snapshot&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;S3 export → import&lt;/strong&gt; (creates new table)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cross-account / cross-region with restore&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;AWS Backup&lt;/strong&gt; copy + restore&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ongoing live replication (not a one-time copy)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Global tables&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;No approach is universally "right" — it depends on table size, whether you need a&lt;br&gt;
point-in-time snapshot or live data, and whether the destination is a new or existing&lt;br&gt;
table.&lt;/p&gt;
&lt;h2&gt;
  
  
  Approach 1: Scan + BatchWriteItem (the script)
&lt;/h2&gt;

&lt;p&gt;The lowest-tech option: read every item from the source with &lt;code&gt;Scan&lt;/code&gt;, write it to the&lt;br&gt;
destination with &lt;code&gt;BatchWriteItem&lt;/code&gt;. Works cross-account and cross-region as long as your&lt;br&gt;
script holds credentials for both sides (or assumes a role in the target account).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Sketch — read source, write target (pseudo; use the SDK in real life)&lt;/span&gt;
aws dynamodb scan &lt;span class="nt"&gt;--table-name&lt;/span&gt; SourceTable &lt;span class="nt"&gt;--region&lt;/span&gt; us-east-1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; items.json
&lt;span class="c"&gt;# transform Items[] into BatchWriteItem RequestItems, then:&lt;/span&gt;
aws dynamodb batch-write-item &lt;span class="nt"&gt;--request-items&lt;/span&gt; file://batch.json &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; eu-west-1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The gotchas are real and easy to miss:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;BatchWriteItem&lt;/code&gt; caps at 25 items or 16MB per call&lt;/strong&gt; — you must chunk, and a single
call can return &lt;em&gt;unprocessed items&lt;/em&gt; you have to retry with exponential backoff
(&lt;a href="https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_BatchWriteItem.html" rel="noopener noreferrer"&gt;API reference&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Writes consume write capacity.&lt;/strong&gt; On a &lt;a href="https://dynotable.com/docs/glossary#provisioned" rel="noopener noreferrer"&gt;provisioned&lt;/a&gt; target you'll hit
&lt;code&gt;ProvisionedThroughputExceededException&lt;/code&gt; fast; &lt;a href="https://dynotable.com/docs/glossary#on-demand" rel="noopener noreferrer"&gt;on-demand&lt;/a&gt; absorbs more but still caps
each partition at a hard 1,000 WCU / 3,000 RCU limit. Size the write load against the destination's capacity
before you start.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Scan&lt;/code&gt; reads the whole table&lt;/strong&gt; and meters every item — the classic
&lt;a href="https://dynotable.com/learn/query-vs-scan" rel="noopener noreferrer"&gt;Query-vs-Scan&lt;/a&gt; cost. A big table also means paginating through
&lt;code&gt;LastEvaluatedKey&lt;/code&gt;; see &lt;a href="https://dynotable.com/learn/pagination" rel="noopener noreferrer"&gt;pagination&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It's not atomic.&lt;/strong&gt; Items written while the scan is in flight can be missed — you only
get a consistent snapshot if the source is quiescent.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Best for small tables or when you need to transform/filter during the copy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Approach 2: Export to S3, then import to a new table
&lt;/h2&gt;

&lt;p&gt;For large tables, DynamoDB's managed &lt;strong&gt;export to S3&lt;/strong&gt; plus &lt;strong&gt;import from S3&lt;/strong&gt; avoids&lt;br&gt;
hammering your capacity at all.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Export&lt;/strong&gt; snapshots the table to an S3 bucket&lt;br&gt;
(&lt;a href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/S3DataExport.HowItWorks.html" rel="noopener noreferrer"&gt;how it works&lt;/a&gt;):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Requires &lt;strong&gt;point-in-time recovery (PITR) enabled&lt;/strong&gt; on the source table.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Does not consume read capacity&lt;/strong&gt; and has no impact on table performance —
it reads from continuous backups, not the live table.&lt;/li&gt;
&lt;li&gt;Outputs &lt;strong&gt;DynamoDB JSON or Amazon Ion&lt;/strong&gt; format. (The
&lt;a href="https://dynotable.com/learn/data-types" rel="noopener noreferrer"&gt;DynamoDB-JSON&lt;/a&gt; wire format is what lands in S3, type tags and
all.)&lt;/li&gt;
&lt;li&gt;Can write to an S3 bucket &lt;strong&gt;owned by another account&lt;/strong&gt; and &lt;strong&gt;in a different region&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Supports &lt;strong&gt;full and incremental&lt;/strong&gt; exports (&lt;a href="https://aws.amazon.com/about-aws/whats-new/2023/09/incremental-export-s3-amazon-dynamodb/" rel="noopener noreferrer"&gt;incremental export GA, Sept 2023&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Import&lt;/strong&gt; then builds a fresh table from that S3 data&lt;br&gt;
(&lt;a href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/S3DataImport.HowItWorks.html" rel="noopener noreferrer"&gt;how it works&lt;/a&gt;):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Imports into a &lt;strong&gt;brand-new table only&lt;/strong&gt; — you cannot import into an existing table.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Does not consume write capacity&lt;/strong&gt; on the new table.&lt;/li&gt;
&lt;li&gt;Accepts &lt;strong&gt;CSV, DynamoDB JSON, or Amazon Ion&lt;/strong&gt; (optionally GZIP/ZSTD compressed).&lt;/li&gt;
&lt;li&gt;The source S3 bucket may be in &lt;strong&gt;another account or another region&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;You can define &lt;strong&gt;secondary indexes at import time&lt;/strong&gt;, queryable as soon as the import
completes.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Warning&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Import &lt;strong&gt;overwrites duplicate keys&lt;/strong&gt; silently — if the source has non-unique primary&lt;br&gt;
keys, only the last write survives, and the processed-item count won't match the final&lt;br&gt;
table count. Validate key uniqueness in the source first.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is the cleanest path for a large cross-account/cross-region migration where a&lt;br&gt;
point-in-time snapshot (not live data) is acceptable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Approach 3: AWS Backup copy + restore
&lt;/h2&gt;

&lt;p&gt;If you already use &lt;strong&gt;AWS Backup&lt;/strong&gt;, it copies recovery points across accounts and regions&lt;br&gt;
(&lt;a href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/bp-migrating-table-between-accounts-backup.html" rel="noopener noreferrer"&gt;cross-account migration guide&lt;/a&gt;):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Back up the source table into a backup vault.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Copy&lt;/strong&gt; the backup to a vault in the target account/region.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Restore&lt;/strong&gt; it to a new table in the target.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Key constraints:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cross-account copy requires both accounts to be in the &lt;strong&gt;same AWS Organization&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Restore &lt;strong&gt;always creates a new table&lt;/strong&gt; — you can't restore over an existing one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://dynotable.com/docs/glossary#gsi" rel="noopener noreferrer"&gt;Global secondary indexes&lt;/a&gt; are preserved&lt;/strong&gt; by default (exclude some or all to save on
restore time/cost); you can't &lt;em&gt;add&lt;/em&gt; new indexes at restore.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Encryption gotcha:&lt;/strong&gt; to keep the same KMS key on a cross-region restore you need a
&lt;strong&gt;multi-region key&lt;/strong&gt;; for cross-account you must &lt;strong&gt;share the key&lt;/strong&gt; with the target
account. AWS-owned and AWS-managed keys can't be shared or made multi-region
(&lt;a href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Restore.Tutorial.html" rel="noopener noreferrer"&gt;restore encryption notes&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For a one-off cross-region restore with table-setting changes, AWS Backup is more&lt;br&gt;
operationally complete than a hand-rolled script — it carries GSIs, encryption, and&lt;br&gt;
metadata, not just item data.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Approach 4: Global tables (live replication, not a one-time copy)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Global tables&lt;/strong&gt; replicate a table across regions — and now optionally across accounts&lt;br&gt;
(&lt;a href="https://aws.amazon.com/about-aws/whats-new/2026/02/dynamodb-gt-multi-account/" rel="noopener noreferrer"&gt;multi-account GA, Feb 2026&lt;/a&gt;) —&lt;br&gt;
&lt;strong&gt;continuously&lt;/strong&gt;. Any replica serves reads and writes (multi-active), with asynchronous,&lt;br&gt;
last-writer-wins replication&lt;br&gt;
(&lt;a href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GlobalTables.html" rel="noopener noreferrer"&gt;global tables docs&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;This is &lt;strong&gt;not&lt;/strong&gt; a "copy and walk away" tool — it's ongoing replication. Use it when you&lt;br&gt;
want the destination region to stay in sync indefinitely (DR, low-latency local reads),&lt;br&gt;
not for a clean one-time migration. Add a region to an existing table and DynamoDB&lt;br&gt;
backfills the existing data into the new replica.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Global tables default to multi-region &lt;strong&gt;eventual&lt;/strong&gt; consistency (MREC); a strong-consistency&lt;br&gt;
mode (&lt;a href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/V2globaltables_HowItWorks.html" rel="noopener noreferrer"&gt;MRSC&lt;/a&gt;) exists but is same-account only. If you treat a global-table replica as a&lt;br&gt;
migration target and cut over immediately, replication lag can mean the new region hasn't&lt;br&gt;
caught up yet.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Operational gotchas (all approaches)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GSIs aren't free to recreate.&lt;/strong&gt; A scan+write copy doesn't carry indexes — you define
them on the target and they backfill (and cost) separately. Plan your
&lt;a href="https://dynotable.com/learn/gsi-vs-lsi" rel="noopener noreferrer"&gt;GSI vs LSI&lt;/a&gt; layout on the destination up front; LSIs can only be
created at table-creation time
(&lt;a href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/LSI.html" rel="noopener noreferrer"&gt;LSI docs&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Capacity mode doesn't transfer.&lt;/strong&gt; The new table starts with whatever mode you set,
not the source's. Estimate the write load before a scan+write copy — size a
representative item with the
&lt;a href="https://dynotable.com/tools/dynamodb-item-size-calculator" rel="noopener noreferrer"&gt;item-size calculator&lt;/a&gt; and multiply by item count
to ballpark WCUs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://dynotable.com/docs/glossary#ttl" rel="noopener noreferrer"&gt;TTL&lt;/a&gt;, &lt;a href="https://dynotable.com/docs/glossary#stream" rel="noopener noreferrer"&gt;streams&lt;/a&gt;, auto-scaling, and tags are table settings, not data&lt;/strong&gt; — none of the
copy methods carry all of them. Re-apply them on the target.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DynamoDB JSON ≠ plain JSON.&lt;/strong&gt; Exports and scans emit type-tagged
&lt;a href="https://dynotable.com/learn/data-types" rel="noopener noreferrer"&gt;DynamoDB-JSON&lt;/a&gt;; if you're transforming en route, the
&lt;a href="https://dynotable.com/tools/dynamodb-json-converter" rel="noopener noreferrer"&gt;DynamoDB JSON converter&lt;/a&gt; handles the marshalling.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Is there an AWS CLI command to copy a DynamoDB table?&lt;/strong&gt;&lt;br&gt;
No. There's no native &lt;code&gt;copy-table&lt;/code&gt; command. You combine &lt;code&gt;scan&lt;/code&gt; + &lt;code&gt;batch-write-item&lt;/code&gt;,&lt;br&gt;
or use the managed export/import or AWS Backup features.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do I copy a DynamoDB table to another account?&lt;/strong&gt;&lt;br&gt;
Three options: a scan+write script with credentials for both accounts, an S3&lt;br&gt;
export/import (the bucket can be cross-account), or AWS Backup copy+restore (both accounts&lt;br&gt;
must be in the same AWS Organization).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do I copy a DynamoDB table to another region?&lt;/strong&gt;&lt;br&gt;
S3 export/import and AWS Backup both support cross-region. For &lt;em&gt;ongoing&lt;/em&gt; cross-region&lt;br&gt;
sync rather than a one-time copy, add a global-table replica in the target region.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does copying a table copy its indexes?&lt;/strong&gt;&lt;br&gt;
S3 import and AWS Backup restore let you keep/define secondary indexes; a scan+write&lt;br&gt;
script does not — you create indexes on the target yourself, and they backfill separately.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I import into an existing DynamoDB table?&lt;/strong&gt;&lt;br&gt;
No. DynamoDB's S3 import and AWS Backup restore both create a &lt;strong&gt;new&lt;/strong&gt; table. To merge&lt;br&gt;
into an existing table, use a scan+write script.&lt;/p&gt;




&lt;p&gt;A GUI makes the cutover sane: browse both source and target side by side, verify item&lt;br&gt;
counts and a few sample records after the copy, and run ad-hoc checks without writing a&lt;br&gt;
scan script. &lt;a href="https://dynotable.com/download" rel="noopener noreferrer"&gt;Download DynoTable&lt;/a&gt; to inspect tables across accounts and&lt;br&gt;
regions during a migration.&lt;/p&gt;

</description>
      <category>dynamodb</category>
      <category>aws</category>
      <category>database</category>
      <category>nosql</category>
    </item>
    <item>
      <title>How to Connect to DynamoDB Local and LocalStack</title>
      <dc:creator>DynoTable</dc:creator>
      <pubDate>Sat, 04 Jul 2026 10:41:19 +0000</pubDate>
      <link>https://dev.to/dynotable/how-to-connect-to-dynamodb-local-and-localstack-12d1</link>
      <guid>https://dev.to/dynotable/how-to-connect-to-dynamodb-local-and-localstack-12d1</guid>
      <description>&lt;p&gt;You have a local DynamoDB running and your code talks to it fine — but you want&lt;br&gt;
to &lt;em&gt;see&lt;/em&gt; the tables, not write a &lt;code&gt;scan&lt;/code&gt; script every time. Connecting a client to&lt;br&gt;
a local endpoint is two changes: point at the right URL, and hand it throwaway&lt;br&gt;
credentials. The details below are where people get stuck — region namespacing,&lt;br&gt;
the alphanumeric-key rule, and the &lt;code&gt;8000&lt;/code&gt; vs &lt;code&gt;4566&lt;/code&gt; port split.&lt;/p&gt;
&lt;h2&gt;
  
  
  DynamoDB Local vs LocalStack: what you're connecting to
&lt;/h2&gt;

&lt;p&gt;Both give you a DynamoDB API on &lt;code&gt;localhost&lt;/code&gt; with no AWS account, but they're&lt;br&gt;
different things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;DynamoDB Local&lt;/strong&gt; is the downloadable DynamoDB engine in a single process — AWS ships
it as a JAR and a Docker image
(&lt;a href="https://hub.docker.com/r/amazon/dynamodb-local" rel="noopener noreferrer"&gt;&lt;code&gt;amazon/dynamodb-local&lt;/code&gt;&lt;/a&gt;). It's DynamoDB and
nothing else. Default port &lt;strong&gt;8000&lt;/strong&gt;
(&lt;a href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBLocal.UsageNotes.html" rel="noopener noreferrer"&gt;AWS docs&lt;/a&gt;). See
&lt;a href="https://dynotable.com/learn/dynamodb-local" rel="noopener noreferrer"&gt;running DynamoDB Local with Docker&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LocalStack&lt;/strong&gt; emulates a stack of AWS services behind one endpoint. Its
DynamoDB is itself
&lt;a href="https://docs.localstack.cloud/aws/services/dynamodb/" rel="noopener noreferrer"&gt;powered by DynamoDB Local&lt;/a&gt;,
but everything goes through LocalStack's single
&lt;a href="https://docs.localstack.cloud/aws/capabilities/networking/internal-endpoints/" rel="noopener noreferrer"&gt;edge port &lt;strong&gt;4566&lt;/strong&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the only practical difference for connecting is the endpoint URL: &lt;code&gt;:8000&lt;/code&gt; for&lt;br&gt;
standalone DynamoDB Local, &lt;code&gt;:4566&lt;/code&gt; for DynamoDB-via-LocalStack. Everything else —&lt;br&gt;
the API, the credentials trick, the GUI config — is identical.&lt;/p&gt;
&lt;h2&gt;
  
  
  The endpoint + dummy-credentials setup that trips everyone up
&lt;/h2&gt;

&lt;p&gt;The AWS SDKs and CLI require an access key and a region even when talking to a&lt;br&gt;
local endpoint — but those values &lt;strong&gt;don't have to be real&lt;/strong&gt;. AWS's own docs say&lt;br&gt;
these values "don't have to be valid AWS values to run locally"&lt;br&gt;
(&lt;a href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBLocal.UsageNotes.html" rel="noopener noreferrer"&gt;AWS docs&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Two gotchas that aren't obvious:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The region/access-key silently namespaces your data.&lt;/strong&gt; Without the
&lt;code&gt;-sharedDb&lt;/code&gt; flag, DynamoDB Local writes a separate &lt;code&gt;myaccesskeyid_region.db&lt;/code&gt;
file per access-key-ID + region combination — &lt;a href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBLocal.UsageNotes.html" rel="noopener noreferrer"&gt;AWS's exact naming&lt;/a&gt;.
Connect with a &lt;em&gt;different&lt;/em&gt; key or region than your app used and your tables look
like they've vanished; they're just in another file. Run with &lt;code&gt;-sharedDb&lt;/code&gt; (one
&lt;code&gt;shared-local-instance.db&lt;/code&gt; for every client) or match the exact key + region
your app uses.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The access key ID must be alphanumeric&lt;/strong&gt; on DynamoDB Local — no symbols.
&lt;a href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBLocal.UsageNotes.html" rel="noopener noreferrer"&gt;AWS docs&lt;/a&gt;
state &lt;code&gt;AWS_ACCESS_KEY_ID&lt;/code&gt; can contain only &lt;code&gt;A–Z&lt;/code&gt;, &lt;code&gt;a–z&lt;/code&gt;, and &lt;code&gt;0–9&lt;/code&gt;; AWS introduced
this in DynamoDB Local 2.0.0 (and 1.23.0+), so a key with special characters that
worked on an earlier image now fails
(&lt;a href="https://repost.aws/articles/ARc4hEkF9CRgOrw8kSMe6CwQ/troubleshooting-the-access-key-id-or-security-token-is-invalid-error-after-upgrading-dynamodb-local-to-version-2-0-0-1-23-0-or-greater" rel="noopener noreferrer"&gt;AWS re:Post&lt;/a&gt;).
See the error below.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For LocalStack the safe default is &lt;code&gt;test&lt;/code&gt; / &lt;code&gt;test&lt;/code&gt;: it&lt;br&gt;
&lt;a href="https://docs.localstack.cloud/aws/capabilities/config/credentials/" rel="noopener noreferrer"&gt;ignores the secret key entirely&lt;/a&gt;&lt;br&gt;
and never validates the secret value. Real-looking &lt;code&gt;AKIA…&lt;/code&gt;/&lt;code&gt;ASIA…&lt;/code&gt; keys are&lt;br&gt;
&lt;a href="https://docs.localstack.cloud/aws/capabilities/config/credentials/" rel="noopener noreferrer"&gt;rejected as a safeguard and fall back to the dummy account &lt;code&gt;000000000000&lt;/code&gt;&lt;/a&gt; —&lt;br&gt;
the same account an arbitrary key like &lt;code&gt;test&lt;/code&gt; resolves to. Stick with &lt;code&gt;test&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Whatever dummy values you pick, use the &lt;strong&gt;same&lt;/strong&gt; access key + region across your&lt;br&gt;
app, the CLI, and your GUI. Mismatched values are the single most common reason a&lt;br&gt;
local table "doesn't show up."&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Connecting with the AWS CLI (sanity check)
&lt;/h2&gt;

&lt;p&gt;Before pointing a GUI at it, confirm the endpoint is alive from the CLI. The CLI&lt;br&gt;
has &lt;a href="https://docs.aws.amazon.com/sdkref/latest/guide/feature-ss-endpoints.html" rel="noopener noreferrer"&gt;no built-in default local endpoint&lt;/a&gt;,&lt;br&gt;
so either pass &lt;code&gt;--endpoint-url&lt;/code&gt; per command or set&lt;br&gt;
&lt;code&gt;AWS_ENDPOINT_URL_DYNAMODB=http://localhost:8000&lt;/code&gt; (CLI v2.13+).&lt;/p&gt;

&lt;p&gt;DynamoDB Local:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws dynamodb list-tables &lt;span class="nt"&gt;--endpoint-url&lt;/span&gt; http://localhost:8000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;LocalStack (same command, different port):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws dynamodb list-tables &lt;span class="nt"&gt;--endpoint-url&lt;/span&gt; http://localhost:4566
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you have credentials configured at all (even fake ones in &lt;code&gt;~/.aws/credentials&lt;/code&gt;&lt;br&gt;
or via &lt;code&gt;AWS_ACCESS_KEY_ID&lt;/code&gt;/&lt;code&gt;AWS_SECRET_ACCESS_KEY&lt;/code&gt;), this returns your table list.&lt;br&gt;
An empty list with no error means the endpoint works but you're looking at a&lt;br&gt;
different key/region namespace — see the gotcha above.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Pass dummy creds inline for a one-off check without touching your profile:&lt;/p&gt;


&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;AWS_ACCESS_KEY_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;test &lt;/span&gt;&lt;span class="nv"&gt;AWS_SECRET_ACCESS_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;test &lt;/span&gt;&lt;span class="nv"&gt;AWS_REGION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;us-east-1 &lt;span class="se"&gt;\&lt;/span&gt;
  aws dynamodb list-tables &lt;span class="nt"&gt;--endpoint-url&lt;/span&gt; http://localhost:8000
&lt;/code&gt;&lt;/pre&gt;

&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  DynamoDB Local GUI: browsing and querying local tables in DynoTable
&lt;/h2&gt;

&lt;p&gt;Once the CLI works, a GUI needs the same three values: &lt;strong&gt;endpoint&lt;/strong&gt;, &lt;strong&gt;region&lt;/strong&gt;,&lt;br&gt;
and any &lt;strong&gt;dummy credentials&lt;/strong&gt;. The CLI returns DynamoDB-JSON you read by eye; a&lt;br&gt;
GUI renders the same data as a table you can sort, filter, and edit.&lt;/p&gt;

&lt;p&gt;In DynoTable, add a connection and set a custom endpoint:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Endpoint:&lt;/strong&gt; &lt;code&gt;http://localhost:8000&lt;/code&gt; (DynamoDB Local) or
&lt;code&gt;http://localhost:4566&lt;/code&gt; (LocalStack)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Region:&lt;/strong&gt; whatever your app uses — e.g. &lt;code&gt;us-east-1&lt;/code&gt;. It's a label here, not a
real AWS region, but it must match so you land in the same data namespace.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Access key / secret:&lt;/strong&gt; anything (&lt;code&gt;test&lt;/code&gt; / &lt;code&gt;test&lt;/code&gt; is conventional). Alphanumeric
only for the access key on DynamoDB Local.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;From there you browse items, run a &lt;code&gt;Query&lt;/code&gt; or &lt;code&gt;Scan&lt;/code&gt;, and edit rows visually&lt;br&gt;
instead of &lt;a href="https://dynotable.com/docs/glossary#marshalling" rel="noopener noreferrer"&gt;marshalling&lt;/a&gt; JSON by hand on the CLI. When you load fixtures, the&lt;br&gt;
&lt;a href="https://dynotable.com/tools/dynamodb-json-converter" rel="noopener noreferrer"&gt;DynamoDB-JSON converter&lt;/a&gt; turns plain JSON into&lt;br&gt;
the wire format, and &lt;a href="https://dynotable.com/learn/query-vs-scan" rel="noopener noreferrer"&gt;Query vs Scan&lt;/a&gt; covers which read to&lt;br&gt;
reach for. Same drill for a&lt;br&gt;
&lt;a href="https://dynotable.com/compare/dynamodb-gui" rel="noopener noreferrer"&gt;LocalStack DynamoDB viewer&lt;/a&gt; — only the port changes to&lt;br&gt;
&lt;code&gt;4566&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;DynoTable is local-only desktop software, so pointing it at &lt;code&gt;localhost&lt;/code&gt; keeps&lt;br&gt;
your fixtures on your machine. For a broader look at the GUI landscape, see the&lt;br&gt;
&lt;a href="https://dynotable.com/compare/dynamodb-gui" rel="noopener noreferrer"&gt;DynamoDB GUI comparison&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common errors (region mismatch, port, credentials)
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Warning&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Table not found" / empty list, but you know the table exists.&lt;/strong&gt; Almost always&lt;br&gt;
a region or access-key mismatch creating a separate &lt;code&gt;myaccesskeyid_region.db&lt;/code&gt;&lt;br&gt;
file. Match the key + region your app used, or restart DynamoDB Local with&lt;br&gt;
&lt;code&gt;-sharedDb&lt;/code&gt; so there's one shared database file.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Connection refused.&lt;/strong&gt; Wrong port — &lt;code&gt;8000&lt;/code&gt; is DynamoDB Local, &lt;code&gt;4566&lt;/code&gt; is
LocalStack. Also confirm the container actually published the port
(&lt;code&gt;docker run -p 8000:8000 amazon/dynamodb-local&lt;/code&gt;). For LocalStack, check the
service is up at
&lt;a href="https://docs.localstack.cloud/aws/capabilities/networking/internal-endpoints/" rel="noopener noreferrer"&gt;&lt;code&gt;http://localhost:4566/_localstack/health&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;The Access Key ID or Security Token is Invalid&lt;/code&gt; on DynamoDB Local.&lt;/strong&gt; Since
the 2.0.0 (and 1.23.0+) image, the access key ID must be
&lt;a href="https://repost.aws/articles/ARc4hEkF9CRgOrw8kSMe6CwQ/troubleshooting-the-access-key-id-or-security-token-is-invalid-error-after-upgrading-dynamodb-local-to-version-2-0-0-1-23-0-or-greater" rel="noopener noreferrer"&gt;&lt;strong&gt;alphanumeric only&lt;/strong&gt;&lt;/a&gt;.
A key with symbols that worked on an earlier image now fails — replace it with
letters/numbers (e.g. &lt;code&gt;test&lt;/code&gt;) and update every tool to match.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;The security token included in the request is invalid&lt;/code&gt; against LocalStack.&lt;/strong&gt;
This is almost always an &lt;strong&gt;endpoint&lt;/strong&gt; problem, not a credentials one — your SDK
client dropped the &lt;code&gt;--endpoint-url&lt;/code&gt; / &lt;code&gt;endpoint_url&lt;/code&gt; and hit the real AWS
endpoint, which rejects your dummy key. Confirm the client is actually pointed
at &lt;code&gt;http://localhost:4566&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Credential errors from the SDK/CLI.&lt;/strong&gt; Even local endpoints need &lt;em&gt;some&lt;/em&gt;
credentials present. Set &lt;code&gt;AWS_ACCESS_KEY_ID&lt;/code&gt; / &lt;code&gt;AWS_SECRET_ACCESS_KEY&lt;/code&gt; (or a
fake profile) so the SDK's credential chain resolves.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;http&lt;/code&gt; vs &lt;code&gt;https&lt;/code&gt;.&lt;/strong&gt; Local endpoints are plain &lt;code&gt;http&lt;/code&gt;. An &lt;code&gt;https://&lt;/code&gt; URL will
fail the TLS handshake.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Is DynamoDB Local the same data as my real AWS tables?
&lt;/h2&gt;

&lt;p&gt;No — local and cloud are completely separate stores. DynamoDB Local (and&lt;br&gt;
LocalStack's DynamoDB) keeps data in a local file or in memory; it never touches&lt;br&gt;
your AWS account, and&lt;br&gt;
&lt;a href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBLocal.UsageNotes.html" rel="noopener noreferrer"&gt;AWS Regions/accounts aren't supported at the client level&lt;/a&gt;&lt;br&gt;
locally. That's the point: it's&lt;br&gt;
&lt;a href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBLocal.UsageNotes.html" rel="noopener noreferrer"&gt;for development and testing&lt;/a&gt;.&lt;br&gt;
If you want the same fixtures in the cloud later,&lt;br&gt;
&lt;a href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBLocal.UsageNotes.html" rel="noopener noreferrer"&gt;AWS suggests&lt;/a&gt;&lt;br&gt;
&lt;em&gt;valid-looking&lt;/em&gt; key/region values locally so you only swap the endpoint when you move. To model that schema before&lt;br&gt;
you ship it, &lt;a href="https://dynotable.com/learn/single-table-design" rel="noopener noreferrer"&gt;single-table design&lt;/a&gt; and&lt;br&gt;
&lt;a href="https://dynotable.com/learn/gsi-vs-lsi" rel="noopener noreferrer"&gt;GSI vs LSI&lt;/a&gt; cover the decisions that don't change between&lt;br&gt;
local and prod.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Do I need real AWS credentials?&lt;/strong&gt; No. Both DynamoDB Local and LocalStack accept&lt;br&gt;
dummy values. They just have to be &lt;em&gt;present&lt;/em&gt;, alphanumeric (for DynamoDB Local),&lt;br&gt;
and consistent across your tools.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why do my tables disappear when I switch tools?&lt;/strong&gt; Without &lt;code&gt;-sharedDb&lt;/code&gt;, DynamoDB&lt;br&gt;
Local partitions data by access-key + region into separate &lt;code&gt;myaccesskeyid_region.db&lt;/code&gt;&lt;br&gt;
files. Use &lt;code&gt;-sharedDb&lt;/code&gt; or keep those values identical everywhere.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What's the difference between port 8000 and 4566?&lt;/strong&gt; &lt;code&gt;8000&lt;/code&gt; is standalone&lt;br&gt;
DynamoDB Local's default; &lt;code&gt;4566&lt;/code&gt; is LocalStack's single edge port that fronts all&lt;br&gt;
its emulated services, DynamoDB included.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can one GUI connect to both?&lt;/strong&gt; Yes — they speak the same DynamoDB API. Only the&lt;br&gt;
endpoint URL changes (&lt;code&gt;:8000&lt;/code&gt; vs &lt;code&gt;:4566&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is DynamoDB Local free?&lt;/strong&gt; Yes. AWS distributes DynamoDB Local at no cost as a&lt;br&gt;
JAR and a &lt;a href="https://hub.docker.com/r/amazon/dynamodb-local" rel="noopener noreferrer"&gt;Docker image&lt;/a&gt; — there&lt;br&gt;
are "no provisioned throughput, data storage, or data transfer costs"; it's&lt;br&gt;
&lt;a href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBLocal.UsageNotes.html" rel="noopener noreferrer"&gt;intended for development and testing only&lt;/a&gt;,&lt;br&gt;
not production.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I run SQL against my local tables?&lt;/strong&gt; Local DynamoDB speaks the same API as&lt;br&gt;
the cloud, so the same access-pattern rules apply — and the same limits: DynamoDB's&lt;br&gt;
&lt;a href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ql-reference.select.html" rel="noopener noreferrer"&gt;PartiQL &lt;code&gt;SELECT&lt;/code&gt; grammar&lt;/a&gt;&lt;br&gt;
is &lt;code&gt;SELECT … FROM … WHERE … ORDER BY&lt;/code&gt; only — no &lt;code&gt;JOIN&lt;/code&gt;, no &lt;code&gt;GROUP BY&lt;/code&gt;, and&lt;br&gt;
&lt;a href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ql-functions.html" rel="noopener noreferrer"&gt;no grouping aggregate functions&lt;/a&gt;&lt;br&gt;
like &lt;code&gt;COUNT&lt;/code&gt;/&lt;code&gt;SUM&lt;/code&gt;/&lt;code&gt;AVG&lt;/code&gt; (see &lt;a href="https://dynotable.com/learn/partiql-vs-sql" rel="noopener noreferrer"&gt;PartiQL vs SQL&lt;/a&gt;).&lt;br&gt;
DynoTable's &lt;a href="https://dynotable.com/docs/glossary#workbench" rel="noopener noreferrer"&gt;SQL Workbench&lt;/a&gt; runs those&lt;br&gt;
analytical queries over any connection, local included.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dynotable.com/download" rel="noopener noreferrer"&gt;Try DynoTable&lt;/a&gt; to connect straight to &lt;code&gt;localhost:8000&lt;/code&gt; or&lt;br&gt;
&lt;code&gt;localhost:4566&lt;/code&gt; and browse, query, and edit your local tables with a GUI.&lt;/p&gt;

</description>
      <category>dynamodb</category>
      <category>aws</category>
      <category>database</category>
      <category>nosql</category>
    </item>
    <item>
      <title>The Best DynamoDB GUI Clients in 2026</title>
      <dc:creator>DynoTable</dc:creator>
      <pubDate>Sat, 04 Jul 2026 10:38:10 +0000</pubDate>
      <link>https://dev.to/dynotable/the-best-dynamodb-gui-clients-in-2026-1791</link>
      <guid>https://dev.to/dynotable/the-best-dynamodb-gui-clients-in-2026-1791</guid>
      <description>&lt;p&gt;There is no single "best" DynamoDB GUI — there are tools built for different jobs.&lt;br&gt;
A data modeler is the wrong tool for poking at production rows; a generalist SQL&lt;br&gt;
client is the wrong tool for DynamoDB's key structure. This is an honest roundup of&lt;br&gt;
the main options — free, paid, and open-source, including our own — and what each&lt;br&gt;
is genuinely good at, so you can match the tool to the work instead of the hype.&lt;/p&gt;
&lt;h2&gt;
  
  
  How to choose a DynamoDB GUI
&lt;/h2&gt;

&lt;p&gt;Pick based on the job you actually do most:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Live data ops&lt;/strong&gt; — browse, filter, and edit items in real tables, fast, without
the AWS Console. This is the daily-driver case.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data modeling&lt;/strong&gt; — design tables, sort keys, and &lt;a href="https://dynotable.com/docs/glossary#gsi" rel="noopener noreferrer"&gt;GSIs&lt;/a&gt; around your access
patterns before you write code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Analytics-shaped questions&lt;/strong&gt; — JOINs, &lt;code&gt;GROUP BY&lt;/code&gt;, aggregates. DynamoDB's own
query surface does not do these (more below), so the tool has to compile around
the limitation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A tool that nails one of these is often mediocre at the others. Here's the quick&lt;br&gt;
mapping:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Your main job&lt;/th&gt;
&lt;th&gt;Reach for&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Design tables / GSIs before coding&lt;/td&gt;
&lt;td&gt;NoSQL Workbench&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Browse &amp;amp; edit live data daily&lt;/td&gt;
&lt;td&gt;Dynobase or DynoTable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Just see what's in a &lt;em&gt;local&lt;/em&gt; table&lt;/td&gt;
&lt;td&gt;dynamodb-admin / DynamoIt&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Already live in a JetBrains IDE&lt;/td&gt;
&lt;td&gt;DataGrip&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;JOIN / &lt;code&gt;GROUP BY&lt;/code&gt; / aggregates over your data&lt;/td&gt;
&lt;td&gt;DynoTable (SQL Workbench)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Most teams end up with two tools, not one: a modeler for design time and a&lt;br&gt;
data-ops client for everything after. That split is normal — don't expect a single&lt;br&gt;
GUI to be excellent at both.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  AWS NoSQL Workbench
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Free · macOS / Windows / Linux · modeling-first&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://aws.amazon.com/dynamodb/nosql-workbench/" rel="noopener noreferrer"&gt;NoSQL Workbench&lt;/a&gt; is AWS's own&lt;br&gt;
free, cross-platform visual tool, built around three pillars: a &lt;strong&gt;data modeler&lt;/strong&gt; for&lt;br&gt;
building tables and GSIs (from scratch, imported, or modified from an existing&lt;br&gt;
model), &lt;strong&gt;data visualization&lt;/strong&gt; for previewing access patterns and relationships&lt;br&gt;
against sample data, and an &lt;strong&gt;operation builder&lt;/strong&gt; for exploring datasets and building&lt;br&gt;
data-plane operations — it also generates ready-to-run sample code in several&lt;br&gt;
languages. It connects directly to DynamoDB Local for offline work and can commit&lt;br&gt;
models to a real AWS account. (Features and platforms per the&lt;br&gt;
&lt;a href="https://aws.amazon.com/dynamodb/nosql-workbench/" rel="noopener noreferrer"&gt;AWS NoSQL Workbench page&lt;/a&gt;,&lt;br&gt;
verified 2026-06-10.)&lt;/p&gt;

&lt;p&gt;It's the best free way to &lt;em&gt;design&lt;/em&gt; a table, but it's a development and modeling tool,&lt;br&gt;
not a production table browser. Reach for it when you're modeling your&lt;br&gt;
&lt;a href="https://dynotable.com/learn/single-table-design" rel="noopener noreferrer"&gt;single-table design&lt;/a&gt;, not when you're debugging a row&lt;br&gt;
at 2am. Our &lt;a href="https://dynotable.com/compare/nosql-workbench-alternative" rel="noopener noreferrer"&gt;NoSQL Workbench alternative&lt;/a&gt; page&lt;br&gt;
covers the day-to-day gap.&lt;/p&gt;
&lt;h2&gt;
  
  
  Dynobase
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;$199 one-time or $9/mo ($108/yr) · 7-day trial · macOS / Windows / Linux&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dynobase.dev/" rel="noopener noreferrer"&gt;Dynobase&lt;/a&gt; is the established paid desktop client and the&lt;br&gt;
most feature-complete commercial option. As of 2026-06-10 its&lt;br&gt;
&lt;a href="https://dynobase.dev/" rel="noopener noreferrer"&gt;pricing page&lt;/a&gt; lists a $9/month Solo plan ($108 billed&lt;br&gt;
annually) or a $199 one-time lifetime license (down from $249), with a 7-day free&lt;br&gt;
trial that needs no credit card. It covers fast data exploration, inline editing,&lt;br&gt;
code generation, import/export, a SQL/&lt;a href="https://dynotable.com/docs/glossary#partiql" rel="noopener noreferrer"&gt;PartiQL&lt;/a&gt; console, and DynamoDB Local /&lt;br&gt;
LocalStack support (including Docker distributions).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Dynobase is still sold, and its pricing page lists cross-platform builds with&lt;br&gt;
DynamoDB Local / LocalStack support and a PartiQL console; its last published&lt;br&gt;
version is a 2023 beta. The trade-offs are price, that it's closed-source, and&lt;br&gt;
that its SQL surface is &lt;strong&gt;AWS PartiQL pass-through&lt;/strong&gt; — so it inherits PartiQL's&lt;br&gt;
limits (no cross-table &lt;code&gt;JOIN&lt;/code&gt;, &lt;code&gt;GROUP BY&lt;/code&gt;, or aggregates).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you want a polished, broad commercial GUI and the license cost is fine, it's a&lt;br&gt;
solid pick. Our &lt;a href="https://dynotable.com/compare/dynobase-alternative" rel="noopener noreferrer"&gt;Dynobase alternative&lt;/a&gt; page covers&lt;br&gt;
where DynoTable differs — chiefly the &lt;a href="https://dynotable.com/docs/glossary#workbench" rel="noopener noreferrer"&gt;SQL Workbench&lt;/a&gt; and EUR/EU-tax billing.&lt;/p&gt;
&lt;h2&gt;
  
  
  TablePlus / DataGrip
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Generalist SQL clients with partial-to-no DynamoDB support&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;These are excellent relational clients, but DynamoDB is a second-class citizen — or&lt;br&gt;
absent.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://tableplus.com/" rel="noopener noreferrer"&gt;TablePlus&lt;/a&gt;&lt;/strong&gt; does not list DynamoDB among its
&lt;a href="https://docs.tableplus.com/" rel="noopener noreferrer"&gt;supported databases&lt;/a&gt; — as of 2026-06-10 that list
is relational plus Cassandra, Redis, and MongoDB (Beta), with no DynamoDB driver.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.jetbrains.com/datagrip/" rel="noopener noreferrer"&gt;DataGrip&lt;/a&gt;&lt;/strong&gt; added DynamoDB in
&lt;a href="https://www.jetbrains.com/datagrip/whatsnew/2023-3/" rel="noopener noreferrer"&gt;version 2023.3&lt;/a&gt;, whose
release notes state DynamoDB data is viewable in the data viewer, tables with keys
and indexes are introspected, and PartiQL is supported in the code editor.
It's genuinely useful if you
already live in a JetBrains IDE, but it's a viewer-plus-PartiQL surface, not a
DynamoDB-native modeling or query-planning tool.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If DynamoDB is a primary database for you, a purpose-built client fits the data&lt;br&gt;
model better than a JDBC bridge. See the&lt;br&gt;
&lt;a href="https://dynotable.com/compare/tableplus-datagrip-dynamodb-alternative" rel="noopener noreferrer"&gt;TablePlus &amp;amp; DataGrip comparison&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  dynamodb-admin / DynamoIt
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Free / open-source · local-focused&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For local development there are good free options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/aaronshaf/dynamodb-admin" rel="noopener noreferrer"&gt;dynamodb-admin&lt;/a&gt;&lt;/strong&gt; is a small
open-source web GUI for DynamoDB Local, dynalite, and LocalStack. Install it
globally and point it at your local endpoint:
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  pnpm add &lt;span class="nt"&gt;-g&lt;/span&gt; dynamodb-admin
  dynamodb-admin &lt;span class="nt"&gt;--dynamo-endpoint&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http://localhost:8000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;By default it sets dummy credentials (&lt;code&gt;key&lt;/code&gt;/&lt;code&gt;secret&lt;/code&gt;, region &lt;code&gt;us-east-1&lt;/code&gt;) so it&lt;br&gt;
  connects straight to a local endpoint — the standard answer for "I just need to&lt;br&gt;
  see what's in my local table." (The &lt;code&gt;--dynamo-endpoint&lt;/code&gt; flag and the&lt;br&gt;
  default &lt;code&gt;key&lt;/code&gt;/&lt;code&gt;secret&lt;/code&gt;/&lt;code&gt;us-east-1&lt;/code&gt; credentials are documented in the&lt;br&gt;
  &lt;a href="https://github.com/aaronshaf/dynamodb-admin" rel="noopener noreferrer"&gt;dynamodb-admin README&lt;/a&gt;; the package&lt;br&gt;
  is MIT-licensed. Verified 2026-06-10. The README shows &lt;code&gt;npm install -g&lt;/code&gt;; &lt;code&gt;pnpm&lt;/code&gt;&lt;br&gt;
  installs the same npm package.) For a deeper walkthrough of endpoints and dummy&lt;br&gt;
  creds, see &lt;a href="https://dynotable.com/learn/dynamodb-local" rel="noopener noreferrer"&gt;connecting to DynamoDB Local&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/bykka/dynamoit" rel="noopener noreferrer"&gt;DynamoIt&lt;/a&gt;&lt;/strong&gt; is a free, open-source JavaFX
desktop viewer that reads your AWS CLI profiles for quick browse/edit/create/delete.
It auto-picks scan vs query and supports pagination, local DynamoDB, and index
awareness, and its README notes you need at least Java 17 (and Maven) to build and
run it. (Features, GPL-3.0 license, and Java requirement per the
&lt;a href="https://github.com/bykka/dynamoit" rel="noopener noreferrer"&gt;DynamoIt README&lt;/a&gt;, verified 2026-06-10.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both are great for local and light use and cost nothing — but neither aims to be a&lt;br&gt;
full-featured production workbench.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Warning&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Local-only GUIs like dynamodb-admin and DynamoIt are excellent for development, but&lt;br&gt;
pointing a bare admin UI at a &lt;strong&gt;production&lt;/strong&gt; table is risky: there are no guardrails&lt;br&gt;
around a full-table &lt;a href="https://dynotable.com/learn/query-vs-scan" rel="noopener noreferrer"&gt;&lt;code&gt;Scan&lt;/code&gt;&lt;/a&gt;, and an accidental scan on a&lt;br&gt;
large table burns read capacity and money. Use a client that shows query cost before&lt;br&gt;
you run it.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  DynoTable
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;SQL Workbench: JOIN / GROUP BY / aggregates within DynamoDB's access-pattern rules&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Full disclosure: this is our tool. DynoTable is a desktop DynamoDB GUI client whose&lt;br&gt;
differentiator is a &lt;strong&gt;SQL Workbench&lt;/strong&gt; that compiles SQL — &lt;code&gt;INNER&lt;/code&gt;/&lt;code&gt;LEFT JOIN&lt;/code&gt;,&lt;br&gt;
&lt;code&gt;GROUP BY&lt;/code&gt;, and aggregates — down to DynamoDB's real &lt;code&gt;Query&lt;/code&gt;/&lt;code&gt;Scan&lt;/code&gt; operations.&lt;/p&gt;

&lt;p&gt;That matters because DynamoDB's own SQL surface, PartiQL, can't do those. Per the&lt;br&gt;
&lt;a href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ql-reference.select.html" rel="noopener noreferrer"&gt;PartiQL &lt;code&gt;SELECT&lt;/code&gt; reference&lt;/a&gt;,&lt;br&gt;
its grammar is &lt;code&gt;SELECT … FROM … WHERE … ORDER BY&lt;/code&gt; only — there is no &lt;code&gt;JOIN&lt;/code&gt;,&lt;br&gt;
no &lt;code&gt;GROUP BY&lt;/code&gt;, and no aggregate functions like &lt;code&gt;COUNT&lt;/code&gt;, &lt;code&gt;SUM&lt;/code&gt;, or &lt;code&gt;AVG&lt;/code&gt;. So a tool&lt;br&gt;
that wants to answer relational-shaped questions has to plan and compile them&lt;br&gt;
against your keys and GSIs itself, rather than passing SQL through. The&lt;br&gt;
&lt;a href="https://dynotable.com/learn/partiql-vs-sql" rel="noopener noreferrer"&gt;PartiQL vs SQL guide&lt;/a&gt; walks through exactly where PartiQL&lt;br&gt;
stops and how the Workbench fills the gap.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Runs in the DynoTable SQL Workbench (NOT in PartiQL):&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;revenue&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;
&lt;span class="k"&gt;INNER&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customerId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PK&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;country&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;revenue&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It also covers the daily-driver basics — fast table browser, inline item editing,&lt;br&gt;
query/scan building, and DynamoDB Local for offline work — and runs its AI assistant&lt;br&gt;
on your own AWS Bedrock credentials. It's the right pick when your questions are&lt;br&gt;
analytics-shaped and PartiQL leaves you stuck. See&lt;br&gt;
&lt;a href="https://dynotable.com/compare/dynamodb-gui" rel="noopener noreferrer"&gt;DynoTable as a DynamoDB GUI&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Comparison table
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Cost&lt;/th&gt;
&lt;th&gt;Platforms&lt;/th&gt;
&lt;th&gt;Live data ops&lt;/th&gt;
&lt;th&gt;Modeling&lt;/th&gt;
&lt;th&gt;JOIN / GROUP BY / aggregates&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;NoSQL Workbench&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;td&gt;macOS / Win / Linux&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;td&gt;Strong&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Dynobase&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$199 once / $9·mo&lt;/td&gt;
&lt;td&gt;macOS / Win / Linux&lt;/td&gt;
&lt;td&gt;Strong&lt;/td&gt;
&lt;td&gt;Partial&lt;/td&gt;
&lt;td&gt;No (PartiQL only)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;TablePlus&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;No DynamoDB support&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DataGrip&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Paid IDE&lt;/td&gt;
&lt;td&gt;macOS / Win / Linux&lt;/td&gt;
&lt;td&gt;Viewer + PartiQL&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No (PartiQL only)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;dynamodb-admin&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Free (OSS)&lt;/td&gt;
&lt;td&gt;Any (Node)&lt;/td&gt;
&lt;td&gt;Local only&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DynamoIt&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Free (OSS)&lt;/td&gt;
&lt;td&gt;Any (JVM)&lt;/td&gt;
&lt;td&gt;Basic&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DynoTable&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;See &lt;a href="https://dynotable.com/pricing" rel="noopener noreferrer"&gt;pricing&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;macOS / Win / Linux&lt;/td&gt;
&lt;td&gt;Strong&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes (SQL Workbench)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Pricing and version claims are dated 2026-06-10; re-check the vendor pages before&lt;br&gt;
relying on them.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What is the best DynamoDB GUI client?
&lt;/h2&gt;

&lt;p&gt;There isn't one universal answer — it depends on the job. NoSQL Workbench is the&lt;br&gt;
best free modeling tool, Dynobase is the most feature-complete commercial client,&lt;br&gt;
dynamodb-admin is the standard for local development, and DynoTable adds a SQL&lt;br&gt;
Workbench that runs JOINs, &lt;code&gt;GROUP BY&lt;/code&gt;, and aggregates that DynamoDB's own PartiQL&lt;br&gt;
can't. Start from the work you do most — design, daily data ops, or analytics — and&lt;br&gt;
the choice usually makes itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is there a free DynamoDB GUI?
&lt;/h2&gt;

&lt;p&gt;Yes. AWS NoSQL Workbench is free and cross-platform (and the safest "official"&lt;br&gt;
choice), and dynamodb-admin and DynamoIt are free and open-source. The catch:&lt;br&gt;
dynamodb-admin and DynamoIt are aimed at local development, and NoSQL Workbench is a&lt;br&gt;
modeling tool, not a live-data daily driver. There's no free, full-featured&lt;br&gt;
&lt;strong&gt;production&lt;/strong&gt; data-ops client — that's where the paid clients earn their keep.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's a good DynamoDB GUI for Mac, Windows, or Docker?
&lt;/h2&gt;

&lt;p&gt;All the desktop clients here are cross-platform (macOS, Windows, Linux) —&lt;br&gt;
NoSQL Workbench, Dynobase, DataGrip, and DynoTable all ship for Apple Silicon Macs&lt;br&gt;
and Windows. For &lt;strong&gt;Docker&lt;/strong&gt;-based local setups, dynamodb-admin is the usual pick: it&lt;br&gt;
runs against the &lt;code&gt;amazon/dynamodb-local&lt;/code&gt; container by pointing&lt;br&gt;
&lt;code&gt;--dynamo-endpoint&lt;/code&gt; at the exposed port. DynoTable and Dynobase also connect to a&lt;br&gt;
local endpoint, including LocalStack.&lt;/p&gt;

&lt;h2&gt;
  
  
  Can TablePlus or DataGrip connect to DynamoDB?
&lt;/h2&gt;

&lt;p&gt;TablePlus does not list DynamoDB among its supported databases. DataGrip added&lt;br&gt;
DynamoDB support in 2023.3 as a data viewer with key/index introspection plus a&lt;br&gt;
PartiQL editor, but it's not a DynamoDB-native modeling or query-planning tool — it&lt;br&gt;
inherits PartiQL's limits and can't JOIN, group, or aggregate across tables.&lt;/p&gt;

&lt;h2&gt;
  
  
  Can a GUI run real SQL (JOINs and aggregates) against DynamoDB?
&lt;/h2&gt;

&lt;p&gt;Not through PartiQL — DynamoDB's PartiQL &lt;code&gt;SELECT&lt;/code&gt; has no &lt;code&gt;JOIN&lt;/code&gt;, &lt;code&gt;GROUP BY&lt;/code&gt;, or&lt;br&gt;
aggregate functions, so any client that "supports SQL" via PartiQL (Dynobase,&lt;br&gt;
DataGrip) hits the same wall. A tool has to compile those into DynamoDB's&lt;br&gt;
&lt;code&gt;Query&lt;/code&gt;/&lt;code&gt;Scan&lt;/code&gt; operations itself; DynoTable's SQL Workbench is built to do exactly&lt;br&gt;
that. If you only need to assemble the raw filter/key conditions for a single API&lt;br&gt;
call, the &lt;a href="https://dynotable.com/tools/dynamodb-expression-builder" rel="noopener noreferrer"&gt;DynamoDB Expression Builder&lt;/a&gt;&lt;br&gt;
generates the correct &lt;code&gt;FilterExpression&lt;/code&gt; / &lt;code&gt;KeyConditionExpression&lt;/code&gt; without any SQL&lt;br&gt;
surface at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  Do I still need a GUI if I have the AWS Console?
&lt;/h2&gt;

&lt;p&gt;Many people switch precisely because of the Console's limits — weak filtering,&lt;br&gt;
clumsy pagination, and no real export. A purpose-built GUI adds fast filtering,&lt;br&gt;
inline editing, query/scan building with cost visibility, and one-click export. If&lt;br&gt;
your pain is Console-specific, that's a feature-by-feature gap a native client&lt;br&gt;
closes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Related
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Concepts the right tool should make easy: &lt;a href="https://dynotable.com/learn/query-vs-scan" rel="noopener noreferrer"&gt;query vs scan&lt;/a&gt;,
&lt;a href="https://dynotable.com/learn/single-table-design" rel="noopener noreferrer"&gt;single-table design&lt;/a&gt;, and the
&lt;a href="https://dynotable.com/learn/partiql-vs-sql" rel="noopener noreferrer"&gt;PartiQL vs SQL&lt;/a&gt; gap that the SQL Workbench fills (with
worked &lt;a href="https://dynotable.com/learn/partiql-examples" rel="noopener noreferrer"&gt;PartiQL examples&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;Free tools that run in your browser, no install: the
&lt;a href="https://dynotable.com/tools/dynamodb-json-converter" rel="noopener noreferrer"&gt;DynamoDB JSON converter&lt;/a&gt; and the
&lt;a href="https://dynotable.com/tools/dynamodb-expression-builder" rel="noopener noreferrer"&gt;Expression Builder&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Head-to-head pages: &lt;a href="https://dynotable.com/compare/dynamodb-gui" rel="noopener noreferrer"&gt;DynamoDB GUI client&lt;/a&gt; overview,
&lt;a href="https://dynotable.com/compare/dynobase-alternative" rel="noopener noreferrer"&gt;Dynobase&lt;/a&gt;,
&lt;a href="https://dynotable.com/compare/nosql-workbench-alternative" rel="noopener noreferrer"&gt;NoSQL Workbench&lt;/a&gt;, and
&lt;a href="https://dynotable.com/compare/tableplus-datagrip-dynamodb-alternative" rel="noopener noreferrer"&gt;TablePlus &amp;amp; DataGrip&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dynotable.com/download" rel="noopener noreferrer"&gt;Download DynoTable&lt;/a&gt; and run a JOIN against your own tables.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Last verified 2026-06-10. Product names are trademarks of their respective owners;&lt;br&gt;
referenced here for identification only.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>dynamodb</category>
      <category>aws</category>
      <category>database</category>
      <category>nosql</category>
    </item>
  </channel>
</rss>
