How to Persist Orders to DynamoDB From a Python App on AWS EC2
A Flask orders service on AWS returns 500 on every POST and nothing lands in DynamoDB. The cause is almost always a partition-key mismatch: the boto3 put_item item uses a field like id while the table was created with order_id as the HASH key, so DynamoDB rejects the write with ValidationException. Rename the key to match the table schema and writes start persisting.
Why put_item returns 500 with a ValidationException
A DynamoDB table is schemaless for its non-key attributes, but the partition key
(and sort key, if the table has one) is not optional. Every put_item must include
the partition key attribute, named exactly as it was declared when the table was
created. Miss it and DynamoDB rejects the write:
ValidationException: One or more parameter values were invalid:
Missing the key order_id in the item
In a typical broken orders service the table was created with order_id as the
partition key, but the boto3 write sends id instead. Because the Flask handler
wraps the write in a try/except that returns 500, the client only sees a generic
error and the table stays empty. Confirm the real table schema before touching code:
aws dynamodb describe-table --table-name Orders \
--query 'Table.KeySchema'
That prints [{"AttributeName": "order_id", "KeyType": "HASH"}] - the write has to
supply order_id, not id.
The fix: match the item key to the table key
The bug lives in the put_item call. The item dict names the partition key id,
which the table does not know about:
# BEFORE - write fails with ValidationException, handler returns 500
table.put_item(Item={
"id": body["id"],
"customer": body["customer"],
"amount": body["amount"],
})
Rename the field to the declared partition key. Nothing else changes - the other attributes are free-form:
# AFTER - order_id matches the table's HASH key, write succeeds
table.put_item(Item={
"order_id": body["id"],
"customer": body["customer"],
"amount": body["amount"],
})
Restart the service and re-send the order. A correct write returns 201:
pkill -f order_writer.py; python3 order_writer.py &
curl -sX POST http://localhost:8000/orders \
-H 'Content-Type: application/json' \
-d '{"id":"o-1","customer":"alice","amount":42}'
# {"created":"o-1"} with HTTP 201
Verify the item actually landed in the table instead of trusting the status code:
aws dynamodb scan --table-name Orders --select COUNT --query 'Count'
Keep the writer and the table in sync
This class of bug is silent whenever the exception is swallowed, so the goal is to make a key mismatch impossible rather than to catch it after the fact.
- Single source of truth for key names. Service teams keep the table key names in a shared constants module, or generate them from the same infrastructure-as-code that creates the table (Terraform, CloudFormation) so the writer and the table definition cannot drift apart.
- Validate the item shape before AWS. Type-check the item dict against a
TypedDictor a pydantic model so a wrong key name fails locally, not on the wire. - Do not swallow the write error. If you must catch it, log
str(e)at error level - a rawValidationExceptionnames the missing key and turns a mystery 500 into a one-line fix.
The same pattern applies whether the app runs on AWS EC2, in a container, or on Lambda: the partition key attribute must be present in every write, spelled the same way the table was created.
Want to try it hands-on? HeyDevJob gives you this exact setup in a live cloud workspace in your browser - edit it, run it, and see it work. Free, nothing to install.
Try it in a workspace →What you'll practice
- Inspecting a DynamoDB table's KeySchema with aws dynamodb describe-table
- Naming the partition key attribute in a boto3 put_item item to match the table
- Verifying a write persisted with a scan COUNT instead of trusting the HTTP status
FAQ
Why does my DynamoDB put_item fail with ValidationException Missing the key?
The item you passed does not include the table's partition key attribute, or spells it differently than the table was created with. DynamoDB requires the partition key (and sort key, if any) in every write, named exactly as declared in the KeySchema. Run aws dynamodb describe-table to see the real key name and match it.
How do I find a DynamoDB table's partition key name?
Run aws dynamodb describe-table --table-name Orders --query 'Table.KeySchema'. It returns the attribute names and their KeyType (HASH for the partition key, RANGE for the sort key). Your boto3 item must use those exact attribute names.
Does DynamoDB require a schema for every attribute?
No. DynamoDB is schemaless for non-key attributes, so any write can add new fields freely. The only required attributes are the partition key and the optional sort key, which must appear in every item exactly as named at table-create time.
How do I persist data to DynamoDB from a Python app on AWS EC2?
Create a boto3 resource for dynamodb, get the table, and call table.put_item with an item dict that includes the table's partition key. On an EC2 instance boto3 picks up credentials from the instance role automatically, so you do not hardcode keys - just make sure the item's key attribute names match the table.
Keep learning
Learn it by doing. Open this in a live cloud workspace, make the change yourself, and keep a record of the work you can share.
Open the workspace →