DynamoDB PHP: Query and CRUD Examples with SDK v3

Written by Ravindu Perera
Last updated: October 23rd, 2026
Use the AWS SDK for PHP v3's DynamoDbClient to read and write DynamoDB, and Marshaler to convert PHP arrays into DynamoDB attribute maps. Query and Scan need pagination; successful batch calls can still return unprocessed work that your application must retry. The examples below use SDK 3.402.0 or later, which requires PHP 8.2 or later.
Table of contents
- Setup
- Create table
- Update table
- List tables
- Delete table
- Add item
- Get item
- Update item
- Delete item
- Get multiple items
- Batch writes
- Create a table with a GSI
- Query a GSI
- Query and Scan
- Error handling
Setup
Install the SDK with Composer in your PHP project. The 3.402.0 release notes raise the PHP requirement to 8.2; older installation pages can still mention 8.1. Keep composer.lock in your application to make dependency resolution repeatable.
The SDK also offers PHAR and ZIP distributions, but Composer handles autoloading and transitive dependencies for the examples here. Begin a PHP script with this setup, then copy the operation you need underneath it. The examples are alternatives, not a script to run from table creation through deletion in one pass.
2012-08-10 selects the DynamoDB API model, not an old SDK release. Credentials come from the default provider chain, including a configured AWS profile or the runtime's IAM role. Use AWS_PROFILE for a named local profile; do not embed real access keys in source code.
The table has Number partition key eid and String sort key name. Both are required for a single-item read, update or delete. This preserves the original employee example, although an immutable sort key is usually preferable in a real application: changing a key means writing a new item and deleting the old one.
The DynamoDbClient reference lists operations and supported paginators. The Marshaler reference explains conversion options. With wrap_numbers, decoded numbers remain NumberValue objects so PHP floating-point conversion does not silently lose precision. Cast those objects to strings when serializing exact numeric values for another system.
Create table
An on-demand table needs BillingMode: PAY_PER_REQUEST and no ProvisionedThroughput. Define only key attributes in AttributeDefinitions; including ordinary fields such as age there causes validation errors. The table name must be unique within the AWS account and Region.
Creation returns before the table is ready. The waiter polls until it becomes active or the wait fails. If you need the GSI example later, use the alternative indexed-table creation block instead of creating the same table twice.
Update table
UpdateTable changes table settings, not individual records. To switch this on-demand table to provisioned capacity, explicitly set the new billing mode and its read/write capacities. This example is for the table without a GSI.
For a table with GSIs, supply provisioned throughput for every index when switching to provisioned mode. On an already provisioned table, update its ProvisionedThroughput without changing the billing mode. You can also use UpdateTable for streams or index changes, subject to the operation's rules; it does not rename the table's primary key attributes.
List tables
List table names in the configured account and Region with a paginator. It follows LastEvaluatedTableName automatically. This returns names, not each table's schema; use describeTable for schema and status information.
Delete table
deleteTable removes the entire table and its indexes. Use it only when the table itself should be removed. The waiter is useful before reusing a name in a test environment.
Add item
Use marshalItem to convert PHP values to their DynamoDB types. An integer becomes a Number, a PHP string becomes a String, and a Boolean becomes a BOOL. For precise decimals, pass Marshaler::number() a decimal string rather than a floating-point approximation.
A put replaces the complete existing item at that primary key. For insert-only behavior, add ConditionExpression => 'attribute_not_exists(eid)' to the request. This checks the item at the complete key, not every item sharing the partition key.
Empty non-key strings and binary values are allowed; empty sets and empty table/index key values are not. A raw DynamoDB Number is represented as a string, for example ['N' => '100000'], even though its stored type is Number. marshalItem handles this conversion for ordinary PHP integers.
Get item
Specify the complete key and unmarshal only when an item was returned. A missing item is a successful response without Item; it is not a missing-table exception.
ConsistentRead requests a strongly consistent base-table read. Do not use it on a GSI query, which supports only eventual consistency.
Update item
Use UpdateExpression for selected attributes. This stores job ID 001 as a String so its leading zeros survive, subtracts from the salary and removes the address. Attribute aliases avoid reserved-word conflicts.
The salary must already be numeric. Without a condition, an update can create an item when the key is absent. If the table has DepartmentAddressIndex, removing address also removes this item from that sparse index because it no longer has the complete index key.
A conditional update can instead set the salary only when its current value exceeds a threshold:
ConditionExpression is a request parameter, not a PHP method. The condition and mutation execute atomically. A failed condition leaves the item unchanged and raises DynamoDbException with code ConditionalCheckFailedException.
Delete item
An unconditional delete uses the same complete key as a get:
For a conditional deletion, compare Booleans with equality. A greater-than comparison is not valid for a BOOL. DeleteItem deletes the whole item; use REMOVE in an update expression to delete only an attribute.
The seeded employee has this flag set to false, so this conditional example intentionally fails until the flag becomes true. ALL_OLD returns the deleted attributes when deletion succeeds; it is not an automatic undo facility.
Get multiple items
batchGetItem accepts up to 100 keys and a 16 MB response. Returned items can be unordered and absent items are omitted. A successful response can include UnprocessedKeys; retry those keys rather than resending keys already completed.
The loop bounds and delay are application choices. Keep the partial results and remaining keys if a job must resume later. Do not duplicate a key in one request. Add other table names to RequestItems for a multi-table batch, staying within the combined limit.
Batch write multiple records
batchWriteItem supports puts and deletes, not partial updates or conditions. A put replaces all attributes of the existing item. Each request accepts up to 25 operations and 16 MB; each stored item must fit 400 KB. The complete batch is not atomic.
The SDK's retry policy handles retryable request failures, but a successful response containing UnprocessedItems needs this additional loop. In a durable worker, save outstanding requests before marking the job incomplete. Resending an already successful put can overwrite a newer concurrent write, so retry only the outstanding subset.
Create a table using a global secondary index
This is an alternative to the earlier create-table block. It creates employeeTable with the same base keys and DepartmentAddressIndex, whose partition key is department and sort key is address. Only these four key attributes belong in the definitions. The on-demand index inherits the table's capacity mode.
A global secondary index offers a different key-based access pattern. Items missing an index key attribute do not appear in it. Index updates are asynchronous, so a newly written employee might not appear in an immediate index query.
Query a global secondary index
Set IndexName and express the key condition using the index's keys. Attribute names and values are case-sensitive: the sample data uses dep 1, so querying Dep 1 would not match.
This index projects all attributes. With a narrower projection, an index query cannot fetch unprojected base-table fields. ScanIndexForward controls sort-key order; false reverses it. Leave ConsistentRead unset or false for a GSI.
Query and Scan with pagination
A base-table Query must specify eid equality and can add a condition on the name sort key. This matches one partition and a name prefix; it is not a search across every employee. See Query vs. Scan when choosing the access pattern.
A Scan can filter any attribute, but it reads table items before applying the filter. This returns employees whose salary is at least 60000:
Query and Scan evaluate up to 1 MB before filtering. A request's Limit also counts evaluated items. Keep iterating even if a filtered page contains no items; only the continuation state determines completion. The PHP paginator guide explains lazy iteration, while DynamoDB pagination covers the service cursor.
For one application page, call query or scan once and return LastEvaluatedKey. Pass the entire key map as ExclusiveStartKey on the next call. Bind the cursor to the same authorized query rather than accepting arbitrary client-supplied keys.
Handle service errors
Catch DynamoDbException and inspect getAwsErrorCode(), not a substring of the exception message. For example, this insert-only request treats an existing employee as a conflict and rethrows other failures:
ResourceNotFoundException normally points to the table, index or Region configuration. Throttling can appear as ProvisionedThroughputExceededException, ThrottlingException or RequestLimitExceeded. Let the SDK perform its configured retries, then surface or reschedule a final failure within your application's deadline. The exception reference also exposes request IDs for diagnostics.
Choose the operation by access pattern
Use GetItem for one complete key, Query for one partition or index partition, and Scan for table-wide work. Use updates for changing selected attributes, conditional writes for state checks, batches for independent puts/deletes and transactions when several distinct-item changes must succeed together. Keep pagination and unprocessed-work handling next to the operation so incomplete results do not look like success.
Frequently Asked Questions
Which PHP version does the AWS SDK require?
How do I query DynamoDB with PHP?
DynamoDbClient with a key condition containing partition-key equality. Iterate getPaginator('Query', $params) to read every page, and use Marshaler to decode the items.Can batchWriteItem update selected attributes?
updateItem for partial updates and retry any UnprocessedItems returned by a batch.Why does a PHP DynamoDB Scan return an empty page?
LastEvaluatedKey, so continue through the paginator.How do I catch conditional-write failures in PHP?
Aws\DynamoDb\Exception\DynamoDbException and compare getAwsErrorCode() to ConditionalCheckFailedException. The item was not changed because its current state did not satisfy the condition.Dynobase is a Professional GUI Client for DynamoDB
Start your 7-day free trial today