Menguasai service DynamoDB di LocalStack: membuat tabel dengan PK/SK, put, get, query, scan, secondary index LSI dan GSI, batasan emulasi throughput, dan best practice data modeling.

Di episode 5 kita menguasai S3 — object storage untuk file dan media. Sekarang kita pindah ke sisi lain yang tak kalah penting: DynamoDB, database NoSQL key-value dan document yang menjadi tulang punggung banyak arsitektur serverless.
Perbedaan mendasar dengan S3 sudah kita singgung di episode 0: S3 menyimpan file, DynamoDB menyimpan data terstruktur yang diakses lewat primary key, query, dan scan. Di episode ini kita akan melihat bagaimana LocalStack meng-emulasi DynamoDB secara stateful — tabel, item, dan index benar-benar tersimpan — dan bagaimana pola data modeling yang benar membuat aplikasi kalian cepat dan hemat.
Setiap tabel DynamoDB wajib punya primary key. Primary key bisa sederhana (satu partition key / HASH) atau komposit (partition key + sort key / RANGE). Model komposit inilah yang paling fleksibel, karena satu partition key bisa menampung banyak item yang diurutkan oleh sort key.
awslocal dynamodb create-table \
--table-name users \
--attribute-definitions AttributeName=pk,AttributeType=S AttributeName=sk,AttributeType=S \
--key-schema AttributeName=pk,KeyType=HASH AttributeName=sk,KeyType=RANGE \
--billing-mode PAY_PER_REQUESTTabel users dibuat dengan pk (HASH) dan sk (RANGE). Dengan --billing-mode PAY_PER_REQUEST, kita tidak perlu menyetel kapasitas — emulator mengabaikan limit throughput, tapi sintaksnya tetap valid untuk produksi.
Tabel tidak langsung aktif — butuh beberapa detik hingga berstatus ACTIVE. Verifikasi dengan:
awslocal dynamodb describe-table --table-name users --query 'Table.TableStatus'Setelah ACTIVE, tabel siap menerima item. Cek daftar tabel yang sudah dibuat dengan awslocal dynamodb list-tables.
Item di DynamoDB ditulis dengan format attribute ber-tipe (S untuk string, N untuk number, B untuk binary). Contoh put-item dan get-item:
awslocal dynamodb put-item --table-name users --item '{"pk": {"S": "user#1"}, "sk": {"S": "profile"}, "name": {"S": "Arman"}, "age": {"N": "30"}}'
awslocal dynamodb get-item --table-name users --key '{"pk": {"S": "user#1"}, "sk": {"S": "profile"}}'Perhatikan pola penamaan user#1 pada partition key. Ini bagian dari single table design yang akan kita bahas nanti — memungkinkan banyak jenis entity tinggal dalam satu tabel.
Ini keputusan paling penting dalam pemakaian DynamoDB:
awslocal dynamodb query --table-name users --key-condition-expression "pk = :pk" --expression-attribute-values '{":pk": {"S": "user#1"}}'
awslocal dynamodb scan --table-name usersTip
Jadikan scan sebagai pilihan terakhir. Kalau kalian sering butuh memfilter dengan atribut selain primary key, itu pertanda kalian butuh secondary index atau desain key yang lebih baik.
Secondary index memungkinkan query dengan pola akses tambahan tanpa scan. Ada dua jenis:
| Jenis | Basis | Keterangan |
|---|---|---|
| LSI (Local Secondary Index) | Sort key alternatif pada partition key yang sama | Didefinisikan saat tabel dibuat |
| GSI (Global Secondary Index) | Partition key dan sort key baru | Bisa ditambah kapan saja |
Contoh membuat tabel dengan GSI yang mengindeks customerId dan status:
awslocal dynamodb create-table \
--table-name orders \
--attribute-definitions AttributeName=orderId,AttributeType=S AttributeName=customerId,AttributeType=S AttributeName=status,AttributeType=S \
--key-schema AttributeName=orderId,KeyType=HASH \
--global-secondary-indexes 'IndexName=ByCustomer,KeySchema=[{AttributeName=customerId,KeyType=HASH},{AttributeName=status,KeyType=RANGE}],Projection={ProjectionType=ALL}' \
--billing-mode PAY_PER_REQUESTSetelah GSI aktif, query berdasarkan customerId menjadi cepat:
awslocal dynamodb query --table-name orders --index-name ByCustomer --key-condition-expression "customerId = :c" --expression-attribute-values '{":c": {"S": "cust-1"}}'Di AWS sungguhan, akses item diukur dalam unit kapasitas: provisioned (WCU/RCU) atau on-demand (bayar per request). LocalStack mengabaikan limit throughput — semua akses dianggap berhasil tanpa memperhitungkan kapasitas. Ini menyenangkan untuk development, tapi ada konsekuensinya:
Karena itu, tetap disiplin dengan pola akses yang efisien walau emulator tidak menghukum kalian.
Dua prinsip yang paling berdampak:
user#1 dan order#1. Satu query bisa mengambil banyak jenis data yang terkait, mengurangi jumlah tabel dan round-trip.active) membuat satu partisi panas. Gunakan nilai dengan kardinalitas tinggi, seperti ID pengguna.| Prinsip | Mengapa |
|---|---|
| Desain mengikuti pola akses | Tabel dioptimalkan untuk query yang benar-benar dipakai |
| Hindari scan | Scan mahal di produksi; gunakan key atau index |
| Satu entity, satu pola key | Single table design memudahkan query gabungan |
create-table (PK/SK), put-item, get-item, query, dan scan — utamakan query, hindari scan.Dua service core sudah kalian kuasai. Di episode 7 berikutnya kita naik ke level berikutnya: membuat dan menjalankan fungsi Lambda di LocalStack — dari create-function, invoke, hingga men-trigger-nya dari event S3 dan SQS. Sampai jumpa di episode berikutnya!