DynamoDB localのDescribeTable APIのレスポンスを補完することで、Terraform経由でDynamoDB localのテーブルを作成できるようにパッチするためのプロキシを書きました。
使い方
Docker Compose経由で使う
↓こういう感じで、お手元の docker-compose.yml*1に書き足しつつ、endpoint-url を http://localhost:8888 に向けるだけで使えます。
services: dynamodb-local: command: -jar DynamoDBLocal.jar -sharedDb -dbPath ./data image: amazon/dynamodb-local ports: - "8000:8000" volumes: - "./docker/dynamodb:/home/dynamodblocal/data" working_dir: /home/dynamodblocal networks: - dynamodb dynamodb-local-proxy: image: ghcr.io/utgwkk/dynamodb-local-proxy environment: - DYNAMODB_LOCAL_ADDR=dynamodb-local:8000 - HOST=0.0.0.0 - PORT=8888 ports: - "8888:8888" networks: - dynamodb networks: dynamodb: driver: bridge
GitHub Actionsで使う
GitHub Actions runner上でDocker Composeの bridge ネットワークモードを使うと、DynamoDB localに対するAPIコールが詰まってタイムアウトする問題があります。GitHub Actions workflowの services フィールドを使って起動することをおすすめします。
on: push: branches: - main pull_request: jobs: test: runs-on: ubuntu-latest services: dynamodb-local: image: amazon/dynamodb-local dynamodb-local-proxy: image: ghcr.io/utgwkk/dynamodb-local-proxy env: DYNAMODB_LOCAL_ADDR: dynamodb-local:8000 HOST: 0.0.0.0 PORT: 8888 ports: - 8888:8888 steps: - run: aws --endpoint-url=http://localhost:8888 dynamodb list-tables
おわりに
どうぞご利用ください。こういうプロキシを書く経験はそんなにないので変なコードがあったらどしどし直してください。
DynamoDB localが修正されたらめでたくお役御免になります。
余談 (実装テクニック)
せっかくなので実装テクニックを書いておきます。
HTTPレスポンスをシリアライズし、モックサーバーのレスポンスとして再利用
net/http/httputilパッケージの DumpResponse 関数を使うことで、以下のようなテキストが得られます。まさにHTTP/1.1のレスポンスという感じで都合がいいですね。
HTTP/1.1 400 Bad Request
Content-Length: 128
Content-Type: application/x-amz-json-1.0
Date: Mon, 30 Mar 2026 11:45:44 GMT
Server: Jetty(12.0.31)
X-Amzn-Requestid: 3b2e4c07-1565-4350-9017-8c67b57dc82d
{"__type":"com.amazonaws.dynamodb.v20120810#ResourceNotFoundException","Message":"Cannot do operations on a non-existent table"}
さて、これをデシリアライズする (*http.Response に復元する) にはどうすればいいか? これはnet/httpパッケージの ReadResponse 関数を使うことで実現できます。あとは得られたレスポンスを返すようにnet/http/httptestの Server を作ってやればよいです。簡単ですね。
……ということがテストコードに全部書いてあります。
graceful shutdown
github.com/ne-sachirou/go-gracefulを使って実現しています。ありものを使うことで「正しいgraceful shutdownができているか」の議論を回避することを狙っています。
ログにリクエストIDを入れる
リクエストを一気通貫してログを眺めたいとき、ログにリクエストを一意に識別するIDが入っていると何かと便利です。
リクエストIDは適当に生成したらよいでしょう。UUID v6とかにしておきます。
現代ではlog/slogを使って構造化ログを吐いたらいいので、あとはcontextの中身をログに出せそうなライブラリを探します。ちょうどそのような記事を読んでおり、github.com/PumpkinSeed/slog-contextを採用できました。
リクエストをcopy as curlできるようにする
github.com/thinkgos/httpcurlを使うと *http.Request をcurlコマンド形式の文字列に変換できます。デバッグなど、何かとcurl経由で気軽に叩きたいことが多いので便利に使っています。
スタックトレースのあるerror
エラーにスタックトレースがないと暮らしていけないと思っているので、そういうライブラリを探しておきます。github.com/cockroachdb/errorsには WithStack 関数があり、エラーにスタックトレースを乗せられるのでちょうどよいでしょう。
環境変数から設定値を注入する
github.com/caarlos0/envを使っています。いつの間にかジェネリクス対応しているようでした。
JSONの一部のフィールドだけ書き換えてシリアライズする
2026/3/31 1:03 追記: 急に github.com/itchyny/gojq のことを思い出したので書き直しました。jqでフィールドの書き換えができるのでGoで書くよりは取り回しがよさそう、可読性がいいかどうかは諸説ある??
ここだけ実装テクというよりは泥臭い仕草って感じなんですが、JSONの構造をふわっと定めつつ関心のある一部のフィールドだけ書き換えるために map[string]any 型を使っています。配列は any[] 型にデシリアライズされるのでキャストしてアクセスしたらよい、ということをついさっき知りました。forループに渡すスライスのキャストがノーガードなので、変なレスポンスが返ってきたら壊れると思います。
E2Eテスト
実際にterraform-provider-awsを使ってDynamoDB localにテーブルを作成できるE2Eテストを用意しました。
……ぐらいで終わったらよかったんですが、GitHub Actions上で terraform plan がタイムアウトして一生終わらないのでひたすらデバッグとか、GSIがない場合のハンドリングが漏れていたので直すとか、それはもう色々なことがありました。GSIの配列を返すフィールドがあったら空配列が返ってくると思うじゃん……。
何はともあれ真に実用できるようになってきたので、E2Eテスト書いてよかった〜
*1:最近は docker-compose.yml じゃなくて compose.yaml が使われていそう