Full Stack Deploy

Deploying and Verifying the Full BookNest Stack

With booknest-web in place, deploy the application stack and seed the six books from db/seed.json; the jq 133,477 filter turns each value into DynamoDB 24 's typed form (N, S, BOOL) for one batch-write-item request:

Deploying booknest-app and seeding the catalogYAML
cfn-lint infra/booknest-app.yaml
aws cloudformation deploy --stack-name booknest-app --template-file infra/booknest-app.yaml \
  --capabilities CAPABILITY_NAMED_IAM --tags app=booknest | tail -1
aws dynamodb batch-write-item --query 'length(UnprocessedItems)' --request-items \
  "$(jq '{"booknest-books": [.books[] | {PutRequest: {Item: with_entries(.value |=
  if type == "number" then {N: tostring} elif type == "boolean" then {BOOL: .}
  else {S: .} end)}}]}' db/seed.json)"
Output
Successfully created/updated stack - booknest-app
0

Check each resource with its own service, then call the API over HTTP as the browser will:

Verifying the table, function and role, then calling the API end to endShell
aws dynamodb describe-table --table-name booknest-books --output text \
  --query 'Table.[TableName,TableStatus,ItemCount,BillingModeSummary.BillingMode]'
aws lambda get-function-configuration --function-name booknest-api --output text \
  --query '[Runtime,State,VpcConfig.VpcId,Role]'
aws iam list-role-policies --role-name booknest-api --output text
URL=$(aws lambda get-function-url-config --function-name booknest-api \
  --query FunctionUrl --output text); URL=${URL/:4566/:31566}
curl -s ${URL}books | jq -c 'map(.id)'
curl -s ${URL}books/3 | jq -c '{title, author, price, inStock}'
curl -s -w ' %{http_code}\n' ${URL}books/abc
Output
booknest-books   ACTIVE   6   PAY_PER_REQUEST
nodejs24.x   Active   vpc-bb78d0f1b66453f83   arn:aws:iam::000000000000:role/booknest-api
POLICYNAMES   read-catalog-and-log
[1,2,3,4,5,6]
{"title":"Salt and Saffron","author":"Priya Nair","price":24,"inStock":false}
{"error":"id must be an integer"} 400

Everything exists and the API answers. LocalStack 63,725 's URL names its internal port 4566, hence the rewrite; on AWS 24 it is https://<url-id>.lambda-url.<region>.on.aws/. Finally, point the front end at the live API:

Switching the front end from the snapshot to the live APIShell
echo "{\"booksUrl\": \"${URL}books\"}" > web/config.json
aws s3 cp web/config.json s3://booknest-web-000000000000/ --only-show-errors
curl -s $SITE/config.json | jq -r .booksUrl | cut -d/ -f3-
Output
zgaza6bmu4sc4mmp9youl2lwfqakna58.lambda-url.us-east-1.localhost.localstack.cloud:31566/books

Reloaded in headless Chrome 1 , the page fetches the catalog from the function URL, across origins thanks to EXTRA_CORS_ALLOWED_ORIGINS (on AWS, the URL's Cors setting):

The foot of BookNest's page on S3, showing the catalog now comes from the Lambda function URL
The foot of BookNest's page on S3 24 , showing the catalog now comes from the Lambda 24 function URL

The commit adds infra/booknest-app.yaml: two templates now describe all of BookNest's AWS side.