Menyajikan fungsi Lambda ke dunia luar lewat API Gateway: REST dan HTTP API, resource dan method, integrasi proxy Lambda, stage, custom domain, hingga authorizer API key dan JWT.

Di episode 8 kita membuat pesan mengalir antar service. Tapi aplikasi kalian butuh lebih dari itu: butuh pintu masuk yang bisa dipanggil dari browser, aplikasi mobile, atau klien eksternal. Di AWS, pintu itu bernama API Gateway — gerbang tunggal yang menerima HTTP request, menangani autentikasi dan throttling, lalu meneruskan ke Lambda.
Bayangkan API Gateway sebagai resepsionis kantor: semua tamu masuk lewat satu pintu, diverifikasi identitasnya, lalu diarahkan ke ruangan (backend) yang tepat. Di episode ini kalian akan membangun resepsionis itu di LocalStack.
AWS punya dua generasi API Gateway: REST API (lengkap, kaya fitur) dan HTTP API (ringan, murah, cepat). Pilih berdasarkan kebutuhan:
| Aspek | REST API | HTTP API |
|---|---|---|
| Fitur | Lengkap, mapping template, usage plan | Sederhana, cepat launch |
| Authorizer | Custom Lambda, Cognito | JWT built-in, Lambda |
| Custom domain | Ya | Ya |
| Harga | Lebih mahal | Sekitar 70% lebih murah |
| Kapan dipakai | Backend kompleks, kebutuhan enterprise | API modern, cost-sensitive |
LocalStack mendukung keduanya. REST API diakses lewat awslocal apigateway, HTTP API lewat awslocal apigatewayv2.
Alur pembuatan REST API terdiri dari beberapa langkah: buat API, buat resource (path), pasang method, lalu integrasikan ke Lambda. Siapkan fungsi Lambda dulu:
zip -r function.zip handler.py
awslocal lambda create-function --function-name orders \
--runtime python3.12 --role arn:aws:iam::000000000000:role/lambda-role \
--handler handler.handler --zip-file fileb://function.zipSekarang rakit API-nya. Simpan ID API di variabel agar tidak mengetik ulang:
API_ID=$(awslocal apigateway create-rest-api --name orders-api | jq -r '.id')
ROOT_ID=$(awslocal apigateway get-resources --rest-api-id $API_ID | jq -r '.items[0].id')
RES_ID=$(awslocal apigateway create-resource --rest-api-id $API_ID \
--parent-id $ROOT_ID --path-part orders | jq -r '.id')
awslocal apigateway put-method --rest-api-id $API_ID \
--resource-id $RES_ID --http-method GET --authorization-type NONE
awslocal apigateway put-integration --rest-api-id $API_ID \
--resource-id $RES_ID --http-method GET --type AWS_PROXY \
--integration-http-method POST \
--uri arn:aws:apigateway:us-east-1:lambda:path/2015-03-31/functions/arn:aws:lambda:us-east-1:000000000000:function:orders/invocationsTipe AWS_PROXY berarti seluruh request diteruskan mentah-mentah ke Lambda, dan output Lambda dikembalikan ke klien. Ini mode paling populer karena kita mengontrol response sepenuhnya di dalam kode.
Perubahan API belum aktif sampai di-deploy ke sebuah stage. Stage adalah "lingkungan" (dev, staging, prod) dari API yang sama:
awslocal apigateway create-deployment --rest-api-id $API_ID --stage-name devREST API di LocalStack bisa dipanggil lewat URL http://localhost:4566/restapis/<API_ID>/<stage>/<path>:
curl http://localhost:4566/restapis/$API_ID/dev/ordersTip
Jangan lupa bahwa setiap perubahan method atau integrasi perlu awslocal apigateway create-deployment ulang agar stage ikut ter-update — bug klasik saat endpoint tampak "tidak berubah" padahal kode Lambda sudah diganti.
Di produksi, API dipanggil lewat domain sendiri, bukan URL panjang restapis/.... LocalStack mensimulasikan custom domain via header Host:
awslocal apigateway create-domain-name --domain-name api.localhost
awslocal apigateway create-base-path-mapping \
--domain-name api.localhost --rest-api-id $API_ID --stage dev
curl -H 'Host: api.localhost' http://localhost:4566/dev/ordersDengan pola ini, kode klien cukup mengganti base URL ke http://api.localhost:4566 tanpa tahu detail API Gateway.
Untuk membatasi akses, REST API mendukung API key lewat usage plan. Buat key, plan, lalu tautkan keduanya:
KEY_ID=$(awslocal apigateway create-api-key --name dev-key --enabled | jq -r '.id')
PLAN_ID=$(awslocal apigateway create-usage-plan --name dev-plan | jq -r '.id')
awslocal apigateway create-usage-plan-key \
--usage-plan-id $PLAN_ID --key-id $KEY_ID --key-type API_KEYHTTP API menawarkan authorizer JWT built-in. Klien mengirim token di header Authorization, dan API memvalidasi signature serta audience tanpa kode tambahan:
HTTP_ID=$(awslocal apigatewayv2 create-api --protocol-type HTTP \
--name orders-http | jq -r '.ApiId')
awslocal apigatewayv2 create-authorizer --api-id $HTTP_ID \
--authorizer-type JWT --name jwt-auth \
--identity-source '$request.header.Authorization' \
--jwt-configuration '{"Audience":["api"],"Issuer":"https://issuer.example.com"}'
awslocal apigatewayv2 create-stage --api-id $HTTP_ID --stage-name devWarning
String $request.header.Authorization harus di-quote dengan tanda kutip tunggal di shell. Tanpa quote, shell akan mengekspansi $request menjadi string kosong dan authorizer rusak diam-diam.
Integrasi AWS_PROXY mem-forward semuanya mentah. Jika kalian butuh transformasi — misalnya menyembunyikan field internal atau mengubah status code — REST API menyediakan mapping template. Berikut contoh mapping yang membungkus response Lambda dengan shape tertentu:
{
"application/json": "#set($response = $input.path('$.body')){\"envelope\":$response}"
}Mapping ini dipasang lewat put-integration-response pada resource. Fungsinya: middleware tipis sebelum response sampai ke klien, tanpa mengubah kode Lambda sama sekali.
Rangkuman episode ini:
awslocal apigateway) untuk fitur lengkap; HTTP API (awslocal apigatewayv2) untuk API ringan ber-JWT.create-rest-api, create-resource, put-method, put-integration tipe AWS_PROXY, lalu create-deployment.http://localhost:4566/restapis/<id>/<stage>/<path>.Host, misalnya api.localhost.API kalian kini punya wajah publik. Tapi membangun API manual perintah demi perintah tidak akan bertahan — tim butuh infrastruktur yang bisa didokumentasikan dan di-reproduksi. Di episode 10 kita otomatiskan semuanya dengan Infrastructure as Code: CloudFormation, Terraform, dan SAM. Sampai jumpa!