<?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: Arya Hegiste</title>
    <description>The latest articles on DEV Community by Arya Hegiste (@arya_hegiste_8528edf8cd29).</description>
    <link>https://dev.to/arya_hegiste_8528edf8cd29</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%2F4166464%2F6ebbf156-6cac-423e-9ce7-78aec5216d12.png</url>
      <title>DEV Community: Arya Hegiste</title>
      <link>https://dev.to/arya_hegiste_8528edf8cd29</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/arya_hegiste_8528edf8cd29"/>
    <language>en</language>
    <item>
      <title>Import CSV Data into DynamoDB with Tables: Validate, Fix, and Retry Invalid Rows</title>
      <dc:creator>Arya Hegiste</dc:creator>
      <pubDate>Wed, 07 Oct 2026 02:16:06 +0000</pubDate>
      <link>https://dev.to/arya_hegiste_8528edf8cd29/import-csv-data-into-dynamodb-with-tables-validate-fix-and-retry-invalid-rows-2534</link>
      <guid>https://dev.to/arya_hegiste_8528edf8cd29/import-csv-data-into-dynamodb-with-tables-validate-fix-and-retry-invalid-rows-2534</guid>
      <description>&lt;p&gt;Importing CSV data into DynamoDB involves more than moving rows from a file into a table. Before writing anything, it is important to understand how source columns map to DynamoDB attributes, how types are inferred, and what happens when a record is missing a required key.&lt;/p&gt;

&lt;p&gt;In this walkthrough, I use Tables by Serverless Creed to import CSV data into DynamoDB and examine how the workflow handles valid records, type inference, and rows with missing partition or sort keys.&lt;/p&gt;

&lt;p&gt;We’ll specifically look at:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Importing a large CSV fixture&lt;/li&gt;
&lt;li&gt;Reviewing type inference&lt;/li&gt;
&lt;li&gt;Testing records with missing DynamoDB keys&lt;/li&gt;
&lt;li&gt;Confirming invalid input is blocked before import&lt;/li&gt;
&lt;li&gt;Correcting the input and retrying&lt;/li&gt;
&lt;li&gt;Verifying the recovered records in DynamoDB
The goal is not simply to see an “import completed” message. We want to verify what actually reached the table and understand how to recover when validation fails.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;1. Prepare the import fixture&lt;/strong&gt;&lt;br&gt;
For this exercise, I used the disposable DynamoDB table:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TC-IMP-002-Test
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The table uses a composite key consisting of a partition key and sort key.&lt;/p&gt;

&lt;p&gt;The main CSV fixture contained 1,000 source rows designed to exercise several import behaviors:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Valid records&lt;/li&gt;
&lt;li&gt;Duplicate-key records&lt;/li&gt;
&lt;li&gt;Records missing required keys&lt;/li&gt;
&lt;li&gt;A value designed to inspect type inference&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After the initial import, the DynamoDB table contained:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;961 items
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This stored-item count should not automatically be interpreted as “961 accepted and 39 rejected.”&lt;/p&gt;

&lt;p&gt;Duplicate DynamoDB keys can overwrite an existing item rather than increase the number of unique items in the table. Also, because the original import completion dialog was not retained, I do not have direct evidence for the exact accepted, rejected, and duplicate counters from that run.&lt;/p&gt;

&lt;p&gt;For that reason, I use the resulting table state as an observation rather than reconstructing import-summary numbers that were not captured.&lt;/p&gt;

&lt;p&gt;Figure 1 — DynamoDB table after the initial CSV import&lt;br&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%2F8edeoh8378lm0ar58kmt.png" 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%2F8edeoh8378lm0ar58kmt.png" alt=" " width="799" height="423"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;2. Inspect type inference instead of assuming a validation error&lt;/strong&gt;&lt;br&gt;
One record in the fixture was intentionally given a value that looked incompatible with a numeric field:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PK: INVALID#001
Value: not-a-number
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A reasonable assumption might be that the importer would reject the record because not-a-number is not numeric.&lt;/p&gt;

&lt;p&gt;That is not what happened in this test.&lt;/p&gt;

&lt;p&gt;After the import, I inspected INVALID#001 in Tables. The value had been imported as a DynamoDB String.&lt;/p&gt;

&lt;p&gt;In other words, the importer did not treat this value as a malformed number. Its type inference allowed the value to be represented as text.&lt;/p&gt;

&lt;p&gt;Figure 2 — &lt;code&gt;not-a-number&lt;/code&gt; imported as a String through type inference&lt;br&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%2Fsmd75kebonhumr5mj9nq.png" 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%2Fsmd75kebonhumr5mj9nq.png" alt=" " width="799" height="423"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is an important distinction when investigating an import.&lt;/p&gt;

&lt;p&gt;A value that appears invalid according to an expected application schema is not necessarily invalid according to DynamoDB's data model or the importer's inferred type.&lt;/p&gt;

&lt;p&gt;Required key violations, however, are different.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;3. Create a controlled missing-key test&lt;/strong&gt;&lt;br&gt;
To test a clear validation failure, I used a smaller CSV containing three rows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csvs"&gt;&lt;code&gt;&lt;span class="k"&gt;PK&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;SK&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;Value&lt;/span&gt;
&lt;span class="k"&gt;TEST&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="mf"&gt;001&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;PROFILE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;Valid&lt;/span&gt; &lt;span class="k"&gt;Control&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;
&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;PROFILE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;Missing&lt;/span&gt; &lt;span class="k"&gt;PK&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mf"&gt;2&lt;/span&gt;
&lt;span class="k"&gt;TEST&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="mf"&gt;003&lt;/span&gt;&lt;span class="p"&gt;,,&lt;/span&gt;&lt;span class="k"&gt;Missing&lt;/span&gt; &lt;span class="k"&gt;SK&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mf"&gt;3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first record contains both required keys.&lt;/p&gt;

&lt;p&gt;The second record is missing the partition key (PK).&lt;/p&gt;

&lt;p&gt;The third record is missing the sort key (SK).&lt;/p&gt;

&lt;p&gt;This gives us one valid control record and two deliberately invalid records.&lt;/p&gt;

&lt;p&gt;When this file was loaded into the import workflow, the preview made the missing key values visible before the import was allowed to proceed.&lt;/p&gt;

&lt;p&gt;Figure 3 — Import preview containing missing PK and SK values&lt;br&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%2F8jalvdacqjj09rc3edqx.png" 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%2F8jalvdacqjj09rc3edqx.png" alt=" " width="799" height="423"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;4. Block invalid rows before writing&lt;/strong&gt;&lt;br&gt;
Tables identified the key problem during preview validation.&lt;/p&gt;

&lt;p&gt;The interface reported:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;2 preview rows are missing a required key value. Fix the source data or mapping before importing.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The Start import action was disabled.&lt;/p&gt;

&lt;p&gt;This is an important safety behavior because DynamoDB requires the complete primary key for an item. Rather than beginning the import and discovering the key problem after writes had started, the workflow blocked this controlled import during validation.&lt;/p&gt;

&lt;p&gt;The screen also showed the target table and import configuration, making it possible to confirm where the operation would write before proceeding.&lt;/p&gt;

&lt;p&gt;Figure 4 — Import blocked because two rows are missing required key values&lt;br&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%2F3rz41xr6izcqxkd8ephw.png" 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%2F3rz41xr6izcqxkd8ephw.png" alt=" " width="799" height="423"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At this point, the correct recovery was not to bypass the validation. The source data or mapping needed to be corrected.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;5. Correct the source and retry&lt;/strong&gt;&lt;br&gt;
I corrected the three-row CSV so that every record had both required key values:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csvs"&gt;&lt;code&gt;&lt;span class="k"&gt;PK&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;SK&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;Value&lt;/span&gt;
&lt;span class="k"&gt;TEST&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="mf"&gt;001&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;PROFILE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;Valid&lt;/span&gt; &lt;span class="k"&gt;Control&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;
&lt;span class="k"&gt;TEST&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="mf"&gt;002&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;PROFILE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;Fixed&lt;/span&gt; &lt;span class="k"&gt;Missing&lt;/span&gt; &lt;span class="k"&gt;PK&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mf"&gt;2&lt;/span&gt;
&lt;span class="k"&gt;TEST&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="mf"&gt;003&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;PROFILE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;Fixed&lt;/span&gt; &lt;span class="k"&gt;Missing&lt;/span&gt; &lt;span class="k"&gt;SK&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mf"&gt;3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After loading the corrected input, the preview changed to:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Preview validation passed. The streaming import is ready to run.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The previously blocked import could now proceed.&lt;/p&gt;

&lt;p&gt;Figure 5 — Corrected input passes validation and is ready for retry&lt;br&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%2Fkbvvif19uzdytxukcd6f.png" 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%2Fkbvvif19uzdytxukcd6f.png" alt=" " width="799" height="423"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This creates a clear recovery sequence:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Missing required keys
        ↓
Preview validation fails
        ↓
Import blocked
        ↓
Correct source/mapping
        ↓
Preview validation passes
        ↓
Retry import
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No special workaround was needed. The recovery consisted of correcting the data so it satisfied the target table's key requirements and then rerunning the normal import workflow.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;6. Verify the recovered data in DynamoDB&lt;/strong&gt;&lt;br&gt;
An import completion message is useful, but I also wanted to verify that the corrected data actually existed in DynamoDB.&lt;/p&gt;

&lt;p&gt;After the retry, I read back:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PK: TEST#002
SK: PROFILE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The resulting item contained:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;Name:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Fixed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Missing&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;PK&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;Value:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The table count had also increased from:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;961
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;964
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;which is consistent with the three corrected test records being added as unique items.&lt;/p&gt;




&lt;p&gt;Figure 6 — Corrected &lt;code&gt;TEST#002&lt;/code&gt; item read back after the successful retry&lt;br&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%2F2dmcxgmgcz4yht0jni37.png" 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%2F2dmcxgmgcz4yht0jni37.png" alt=" " width="799" height="423"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This final read-back is useful because it verifies the resulting table state rather than relying solely on the import UI.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;Type inference and key validation solve different problems&lt;/strong&gt;&lt;br&gt;
One of the more useful lessons from this exercise is the difference between type inference and required-key validation.&lt;/p&gt;

&lt;p&gt;Consider:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Value = not-a-number
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The importer could represent that value as a string, so it was not necessarily invalid for DynamoDB.&lt;/p&gt;

&lt;p&gt;But this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PK = &amp;lt;missing&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;cannot produce a valid item for a table whose primary key requires &lt;code&gt;PK&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That is why the second case produced a blocking validation error while the first did not.&lt;/p&gt;

&lt;p&gt;When investigating rejected or unexpected CSV imports, it helps to separate these questions:&lt;br&gt;
&lt;strong&gt;1. Can the source value be represented as a DynamoDB type?&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;2. Does the resulting item satisfy the target table's required key structure?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;They are related to import validation, but they are not the same check.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;Why read-back verification matters&lt;/strong&gt;&lt;br&gt;
Import tools can tell us whether an operation started, completed, or encountered validation errors.&lt;/p&gt;

&lt;p&gt;But when correctness matters, it is useful to verify the resulting DynamoDB state independently.&lt;/p&gt;

&lt;p&gt;For this exercise, the strongest recovery evidence was not simply that the corrected preview said:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Preview validation passed.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It was that the corrected item could subsequently be read from the table with the expected key and values.&lt;/p&gt;

&lt;p&gt;A useful validation workflow is therefore:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CSV source
   ↓
Preview and mapping
   ↓
Validation
   ↓
Import
   ↓
DynamoDB read-back
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If validation fails, correct the source or mapping and repeat the same path rather than assuming the failed records were written.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;br&gt;
CSV imports into DynamoDB become easier to troubleshoot when validation is treated as part of the workflow rather than just a gate before the import.&lt;/p&gt;

&lt;p&gt;Using Tables, I was able to inspect inferred values, reproduce a controlled missing-key failure, see the import blocked before proceeding, correct the affected records, retry the operation, and verify the recovered data in DynamoDB.&lt;/p&gt;

&lt;p&gt;The test also demonstrated why apparent data-type problems and required-key problems should be investigated separately. A value such as &lt;code&gt;not-a-number&lt;/code&gt; may still be valid as a DynamoDB string, while a missing required partition or sort key prevents the item from satisfying the table's key schema.&lt;/p&gt;

&lt;p&gt;Most importantly, don't stop verification at “Import completed.”&lt;/p&gt;

&lt;p&gt;For repeatable CSV-to-DynamoDB workflows, a stronger process is:&lt;br&gt;
&lt;strong&gt;preview → validate → import → read back.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That final read-back provides evidence of what actually reached DynamoDB.&lt;/p&gt;




&lt;p&gt;Give it a try on &lt;a href="https://tables.serverlesscreed.com/" rel="noopener noreferrer"&gt;https://tables.serverlesscreed.com/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>database</category>
      <category>serverless</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Exporting DynamoDB Data Safely to CSV with Tables</title>
      <dc:creator>Arya Hegiste</dc:creator>
      <pubDate>Tue, 06 Oct 2026 18:14:53 +0000</pubDate>
      <link>https://dev.to/arya_hegiste_8528edf8cd29/exporting-dynamodb-data-safely-to-csv-with-tables-ddj</link>
      <guid>https://dev.to/arya_hegiste_8528edf8cd29/exporting-dynamodb-data-safely-to-csv-with-tables-ddj</guid>
      <description>&lt;p&gt;Exporting DynamoDB data to CSV sounds straightforward until the dataset contains values that spreadsheets interpret differently from plain text.&lt;/p&gt;

&lt;p&gt;Formula-like strings such as =2+3, Unicode text, commas, quotes, embedded newlines, nested DynamoDB values, binary data, booleans, and nulls can all make a seemingly simple export more interesting.&lt;/p&gt;

&lt;p&gt;In this walkthrough, I used Tables by Serverless Creed to export a controlled DynamoDB dataset to CSV and verified both the raw file and its behavior when opened in Microsoft Excel.&lt;/p&gt;

&lt;p&gt;The goal was to answer a simple question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Can a DynamoDB result be exported to CSV without losing its structure or allowing formula-like strings to execute unexpectedly in a spreadsheet?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Test setup&lt;/strong&gt;&lt;br&gt;
I created a disposable DynamoDB table named:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TC-EXP-002-Blog
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The table used a composite primary key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PK  — String
SK  — String
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It was created in &lt;code&gt;ap-south-1&lt;/code&gt; using on-demand billing.&lt;/p&gt;

&lt;p&gt;I inserted eight controlled items covering several CSV edge cases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Formula-like strings such as =2+3, +1+2, @formula-test, and -10+5&lt;/li&gt;
&lt;li&gt;Unicode text: नमस्ते 日本語 🚀&lt;/li&gt;
&lt;li&gt;Commas inside values&lt;/li&gt;
&lt;li&gt;Double quotes&lt;/li&gt;
&lt;li&gt;An embedded newline&lt;/li&gt;
&lt;li&gt;Nested maps and lists&lt;/li&gt;
&lt;li&gt;A null value&lt;/li&gt;
&lt;li&gt;Binary data&lt;/li&gt;
&lt;li&gt;Numbers and booleans&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;AWS CLI verification confirmed that the table contained exactly eight items:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Count: 8
ScannedCount: 8
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fcsshv9wei3m93xup9rq5.png" 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%2Fcsshv9wei3m93xup9rq5.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Configuring the CSV export&lt;/strong&gt;&lt;br&gt;
From the table's data workspace, I opened Export and selected CSV as the output format.&lt;/p&gt;

&lt;p&gt;The export review screen showed several useful details before any file was written:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Export mode: Streaming
Scope: All matching rows across all pages
Format: CSV
Privacy: Original values
File safety: Atomic replacement
Destination: Local file
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The interface also displayed an important spreadsheet-safety message:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Formula-like strings and column names receive a leading apostrophe.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This matters because applications such as Excel can interpret values beginning with characters such as &lt;code&gt;=&lt;/code&gt;, &lt;code&gt;+&lt;/code&gt;, &lt;code&gt;-&lt;/code&gt;, or &lt;code&gt;@&lt;/code&gt; as formulas rather than ordinary data.&lt;br&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%2F8k8vr7l5i99tpehjv88i.png" 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%2F8k8vr7l5i99tpehjv88i.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;Inspecting the raw CSV first&lt;/strong&gt;&lt;br&gt;
After exporting the eight rows, I deliberately inspected the CSV as a raw text file before opening it in Excel.&lt;/p&gt;

&lt;p&gt;This is an important verification step. A spreadsheet application can transform or interpret CSV values while opening the file, so looking only at Excel does not necessarily tell us what the exporter actually wrote.&lt;/p&gt;

&lt;p&gt;The raw file contained entries such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;'=2+3
'+1+2
'@formula-test
'-10+5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The leading apostrophe was present in the exported data.&lt;/p&gt;

&lt;p&gt;That means the current export successfully neutralized the formula-like strings before they reached the spreadsheet.&lt;/p&gt;

&lt;p&gt;The raw CSV also preserved the Unicode value:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;नमस्ते 日本語 🚀
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Other edge cases were visible as well.&lt;/p&gt;

&lt;p&gt;A value containing quotes was CSV-escaped appropriately, while comma-containing values were quoted. The embedded newline remained part of its quoted CSV field.&lt;/p&gt;

&lt;p&gt;Nested DynamoDB data was serialized into string representations suitable for the flattened CSV format.&lt;/p&gt;

&lt;p&gt;Binary data containing &lt;code&gt;Hello&lt;/code&gt; appeared as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;`SGVsbG8=`
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The null attribute was represented as an empty CSV field.&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%2Foifaiqdjci5ahjli4teq.png" 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%2Foifaiqdjci5ahjli4teq.png" alt=" " width="800" height="136"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Opening the export in Excel&lt;/strong&gt;&lt;br&gt;
I then opened the same CSV file directly in Microsoft Excel.&lt;/p&gt;

&lt;p&gt;The most important result was that the formula-like values were no longer executed.&lt;/p&gt;

&lt;p&gt;Instead of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;=2+3 → 5
+1+2 → 3
-10+5 → -5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the exported values remained text.&lt;/p&gt;

&lt;p&gt;This confirms that the spreadsheet-safety transformation visible in the raw CSV prevented these values from being evaluated as formulas in this test.&lt;/p&gt;

&lt;p&gt;Numbers and boolean values were also retained, and the CSV structure remained readable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One important Unicode observation&lt;/strong&gt;&lt;br&gt;
There was, however, a difference between the raw file and Excel.&lt;/p&gt;

&lt;p&gt;The raw CSV contained the expected Unicode text:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;नमस्ते 日本語 🚀
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When I opened the CSV directly in my Excel environment, that text displayed as garbled characters.&lt;/p&gt;

&lt;p&gt;This distinction is important.&lt;/p&gt;

&lt;p&gt;The raw-file inspection showed that the Unicode data existed correctly in the exported file. Therefore, the evidence from this test does not show that Tables destroyed the Unicode value during export.&lt;/p&gt;

&lt;p&gt;Instead, the issue appeared during the way this Excel environment interpreted the CSV when opening it directly.&lt;/p&gt;

&lt;p&gt;This is exactly why checking both the raw output and the spreadsheet representation is useful.&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%2Fm5oz7r2ms07ls9zhkxik.png" 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%2Fm5oz7r2ms07ls9zhkxik.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Testing export failure and recovery&lt;/strong&gt;&lt;br&gt;
I also wanted to verify what happened when the destination itself was invalid.&lt;/p&gt;

&lt;p&gt;During another export, I deliberately entered the following Windows filename:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TC-EXP-002:invalid.csv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because &lt;code&gt;:&lt;/code&gt; is not permitted in a normal Windows filename, the operating system rejected it with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;The file name is not valid.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The invalid destination therefore did not produce a final CSV presented as a successful export.&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%2F74vsty22av29jhiypiis.png" 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%2F74vsty22av29jhiypiis.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I then corrected the destination to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csvs"&gt;&lt;code&gt;&lt;span class="k"&gt;TC&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="k"&gt;EXP&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;002&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="k"&gt;Blog&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="k"&gt;Recovery&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="k"&gt;csv&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and retried the export.&lt;/p&gt;

&lt;p&gt;The corrected export downloaded successfully.&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%2F4xaea08jjlvoy18ji0cl.png" 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%2F4xaea08jjlvoy18ji0cl.png" alt=" " width="606" height="55"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This provided a simple failure-and-recovery check without modifying the DynamoDB source data.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;A useful lesson: verify the file at two levels&lt;/strong&gt;&lt;br&gt;
The most useful lesson from this test was not simply that the export completed successfully.&lt;/p&gt;

&lt;p&gt;It was that CSV verification should happen at two different levels.&lt;/p&gt;

&lt;p&gt;First, inspect the raw file to determine what the exporter actually produced.&lt;/p&gt;

&lt;p&gt;Then open the file in the intended consumer, such as Excel, to determine how that application interprets it.&lt;/p&gt;

&lt;p&gt;Those are not always the same thing.&lt;/p&gt;

&lt;p&gt;In this test, the raw CSV proved that formula-like strings were neutralized and Unicode text was present correctly. Excel confirmed the formula-safety behavior, but its direct CSV-opening path displayed the Unicode value incorrectly.&lt;/p&gt;

&lt;p&gt;Without inspecting the raw file first, it would have been easy to incorrectly attribute that display behavior to the exporter.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;br&gt;
Tables provided a straightforward streaming workflow for exporting DynamoDB results to CSV, while also exposing important behaviors before the export was started.&lt;/p&gt;

&lt;p&gt;For this controlled eight-item dataset, the export:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;preserved CSV structure for commas, quotes, and newlines;&lt;/li&gt;
&lt;li&gt;serialized complex DynamoDB values;&lt;/li&gt;
&lt;li&gt;preserved numbers, booleans, nulls, and binary data in appropriate CSV representations;&lt;/li&gt;
&lt;li&gt;neutralized formula-like strings before spreadsheet use;&lt;/li&gt;
&lt;li&gt;preserved the tested Unicode value in the raw file; and&lt;/li&gt;
&lt;li&gt;recovered cleanly after an invalid destination filename was rejected.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Excel test also highlighted an important practical point: a correct CSV file and a correct spreadsheet rendering are two separate things worth verifying.&lt;/p&gt;

&lt;p&gt;For DynamoDB exports that may eventually be opened in spreadsheet software, checking both makes the export much easier to trust.&lt;/p&gt;

&lt;p&gt;Give it a try on &lt;a href="https://tables.serverlesscreed.com/" rel="noopener noreferrer"&gt;https://tables.serverlesscreed.com/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>database</category>
      <category>serverless</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
