Ravindra BagaleCourses & study guides मराठी Track your progress

Project: Build a Serverless App with Lambda, API Gateway and DynamoDB (Source Code)

This project ties the series together: a student Notes to-do app. Front end on S3 + CloudFront (static hosting chapter), API on API Gateway + Lambda (API lab), data in DynamoDB (CRUD chapter). Download the source zip, deploy click by click, test, then clean up so the bill stays near zero.

What you'll learn in this chapter

  • Architecture of a tiny serverless app
  • Create DynamoDB table and Lambda with IAM
  • Wire HTTP API routes
  • Host the UI on S3 + CloudFront
  • Test end to end and clean up

1. What you build

A single-page Notes app: list notes, add a note, delete a note. No login in the lab version (add Cognito later). Primary backend is Node.js Lambda; the zip also has a Python handler twin.

Request pathBrowserCloudFront → S3 HTML/JSJS fetchhttps://api.../notesAPI Gateway HTTP APIJWT optional laterLambdaCRUD on DynamoDBResponse JSONUI updates list
No EC2. No always-on server.

Download: serverless-notes-app.zip — contains public/ (UI), lambda/index.mjs (Node), lambda/handler.py (Python twin), README.md, and a sample IAM policy.

2. Architecture pieces

PieceAWS serviceWhy
Static UIS3 + CloudFrontCheap HTTPS worldwide; no server
HTTP APIAPI Gateway HTTP APIRoutes, CORS, TLS URL
LogicLambda (Node 20.x)Runs only on requests
DataDynamoDB on-demandKeyed notes; pay per request

3. Lab: deploy click by click

How to do it (step by step)

Region ap-south-1. Replace yourname with your short name. Keep the free tier in mind.

A. DynamoDB table

  1. Create table yourname-notes. PK: noteId (String). On-demand. No sort key.
  2. Wait until Active.

B. IAM role for Lambda

  1. IAM → Roles → Create role → Trusted entity AWS service → Use case Lambda.
  2. Attach AWSLambdaBasicExecutionRole. Create role name yourname-notes-lambda-role.
  3. Open the role → Add permissions → Create inline policy → JSON:
{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Action": ["dynamodb:PutItem","dynamodb:GetItem","dynamodb:DeleteItem","dynamodb:Scan"],
    "Resource": "arn:aws:dynamodb:ap-south-1:ACCOUNT_ID:table/yourname-notes"
  }]
}

Replace ACCOUNT_ID and table name. Why Scan: the lab lists all notes with Scan for simplicity (fine under hundreds of items). For production, add userId PK and Query.

C. Lambda function

  1. Create function yourname-notes-api, Runtime Node.js 20.x, use existing role yourname-notes-lambda-role.
  2. Upload from the zip: copy lambda/index.mjs into the editor (or zip the file as index.mjs at the root and upload). Handler: index.handler.
  3. Configuration → Environment variables: TABLE_NAME=yourname-notes.
  4. Memory 256 MB, Timeout 10 s. Deploy.

D. HTTP API

  1. API Gateway → Create HTTP API → name yourname-notes-http → integrate Lambda yourname-notes-api.
  2. Routes: GET /notes, POST /notes, DELETE /notes/{id} → same Lambda.
  3. CORS: Allow origin * (lab), methods GET,POST,DELETE,OPTIONS, headers content-type.
  4. Create. Copy the Invoke URL as API_BASE.
  5. Test: curl -s $API_BASE/notes → []. Then POST one note.
curl -s -X POST "$API_BASE/notes" -H 'content-type: application/json'   -d '{"text":"Finish Lambda chapter"}'
curl -s "$API_BASE/notes"

E. Front end on S3 + CloudFront

  1. In public/app.js set const API_BASE = "https://YOUR_API"; (no trailing slash).
  2. Create bucket yourname-notes-web, Block Public Access on (CloudFront OAC will read it).
  3. Upload index.html, app.js, styles.css from public/.
  4. CloudFront distribution: origin = the bucket (REST endpoint), OAC recommended, Viewer redirect HTTP→HTTPS, default root object index.html. Attach the bucket policy CloudFront shows.
  5. Open the CloudFront URL → add a note → refresh → delete. DevTools Network should show 200 from API.

F. Optional Python twin

Create a second function with lambda/handler.py, same env and role, point a route or stage at it to compare runtimes.

Cleanup (do this)

  1. Disable then delete the CloudFront distribution (waits several minutes).
  2. Empty and delete the S3 website bucket.
  3. Delete the HTTP API. Delete the Lambda function(s).
  4. Delete the DynamoDB table. Delete the IAM role and inline policy.
  5. Confirm Billing → Cost Explorer shows only tiny charges (usually free-tier covered).

4. Interview angles from this project

Q. Why CloudFront in front of S3 for the UI?

HTTPS, caching, private bucket via OAC, faster global reads.

Q. Why HTTP API not REST here?

No API keys/WAF needed for the lab; HTTP API is cheaper and enough for Lambda proxy + CORS.

Q. Why is Scan OK in the lab but not in production lists?

Lab has few items. Production lists by user need a PK like userId and Query.

Q. How would you add login?

Amazon Cognito user pool + JWT authorizer on the HTTP API; store userId on each note.

Q. What did you clean up and why?

CDN, bucket, API, Lambda, table, role — idle resources still cost or clutter.

Try at home

  1. Add an Update (PUT /notes/{id}) path in Lambda and a UI edit button.
  2. Turn on DynamoDB Streams and a second Lambda that logs every change.

In short

  1. Serverless notes = S3/CloudFront + API Gateway + Lambda + DynamoDB.
  2. Wire IAM least privilege to one table.
  3. CORS and API_BASE connect the browser to the API.
  4. Always clean up lab resources.

Practice Tasks

About this scenario

Pandharpur Yatra Tours is a travel business in Pandharpur. Vitthal More looks after its IT and cloud work. They count money in INR and run their AWS resources in the ap-south-1 Region. In the tasks below you do real jobs for them using what this lesson teaches. All names, account IDs, IP addresses and numbers are made up for practice.

0 / 12

Easy List functions

Why: See which functions exist in this Region.

What to do: List the Lambda functions in ap-south-1.

How:
  1. Read the task and decide which AWS service it is about.
  2. Write aws, then the service name, then the operation (the action word).
  3. Add each needed flag (--name) followed by its value from the task.
  4. Flags can be in any order. Click Check answer.

Medium Invoke a function

Why: Invoke runs the function now and saves its answer in a file.

What to do: Invoke pandharpuryatra-orders-fn and save the response to out.json.

How:
  1. Read the task and decide which AWS service it is about.
  2. Write aws, then the service name, then the operation (the action word).
  3. Add each needed flag (--name) followed by its value from the task.
  4. Flags can be in any order. Click Check answer.

Medium Trust policy for a Lambda role

Why: The trust policy says WHO may take the role.

What to do: Write a trust policy that lets lambda.amazonaws.com assume the role.

How:
  1. Start with { "Version": "2012-10-17", "Statement": [ ... ] }.
  2. In each statement write Effect, Action and Resource.
  3. Give only what the task needs (least privilege).
  4. Use double quotes and commas correctly. Click Check answer.

Medium Put an item

Why: DynamoDB JSON shows the type of each value: S for string, N for number.

What to do: Put an item into pandharpuryatra-orders with orderId "o-101" (string) and amount 2500 (number).

How:
  1. Read the task and decide which AWS service it is about.
  2. Write aws, then the service name, then the operation (the action word).
  3. Add each needed flag (--name) followed by its value from the task.
  4. Flags can be in any order. Click Check answer.

Medium Change timeout and memory

Why: More memory also gives more CPU; timeout stops runaway runs.

What to do: Set pandharpuryatra-orders-fn to timeout 30 seconds and memory 512 MB.

How:
  1. Read the task and decide which AWS service it is about.
  2. Write aws, then the service name, then the operation (the action word).
  3. Add each needed flag (--name) followed by its value from the task.
  4. Flags can be in any order. Click Check answer.

Medium Let a function write logs

Why: Without these actions, Lambda logs never appear in CloudWatch.

What to do: Allow logs:CreateLogGroup, logs:CreateLogStream, logs:PutLogEvents on arn:aws:logs:*:*:* only.

How:
  1. Start with { "Version": "2012-10-17", "Statement": [ ... ] }.
  2. In each statement write Effect, Action and Resource.
  3. Give only what the task needs (least privilege).
  4. Use double quotes and commas correctly. Click Check answer.

Hard Let S3 call your function

Why: A resource-based permission is needed before S3 can invoke the function.

What to do: Add permission statement s3invoke to pandharpuryatra-orders-fn so s3.amazonaws.com can call lambda:InvokeFunction from bucket arn:aws:s3:::pandharpuryatra-uploads.

How:
  1. Read the task and decide which AWS service it is about.
  2. Write aws, then the service name, then the operation (the action word).
  3. Add each needed flag (--name) followed by its value from the task.
  4. Flags can be in any order. Click Check answer.

Hard Quick-create an HTTP API for Lambda

Why: Quick create builds an HTTP API, a default route and stage pointing at your function.

What to do: Create an HTTP API named pandharpuryatra-api whose target is arn:aws:lambda:ap-south-1:128642229481:function:pandharpuryatra-orders-fn.

How:
  1. Read the task and decide which AWS service it is about.
  2. Write aws, then the service name, then the operation (the action word).
  3. Add each needed flag (--name) followed by its value from the task.
  4. Flags can be in any order. Click Check answer.

Hard Create a Lambda function

Why: Lambda needs the code zip, a runtime, a handler name and an execution role.

What to do: Create function pandharpuryatra-orders-fn: runtime python3.12, handler app.handler, role arn:aws:iam::128642229481:role/lambda-basic, code in function.zip.

How:
  1. Read the task and decide which AWS service it is about.
  2. Write aws, then the service name, then the operation (the action word).
  3. Add each needed flag (--name) followed by its value from the task.
  4. Flags can be in any order. Click Check answer.

Hard Set environment variables

Why: Keep settings like table names out of the code.

What to do: Set the environment variable TABLE=pandharpuryatra-orders on pandharpuryatra-orders-fn.

How:
  1. Read the task and decide which AWS service it is about.
  2. Write aws, then the service name, then the operation (the action word).
  3. Add each needed flag (--name) followed by its value from the task.
  4. Flags can be in any order. Click Check answer.

Hard Invoke with a JSON payload

Why: In CLI v2, raw JSON needs --cli-binary-format raw-in-base64-out.

What to do: Invoke pandharpuryatra-orders-fn with payload {"orderId":"o-101"} (raw JSON) and save the reply in out.json.

How:
  1. Read the task and decide which AWS service it is about.
  2. Write aws, then the service name, then the operation (the action word).
  3. Add each needed flag (--name) followed by its value from the task.
  4. Flags can be in any order. Click Check answer.

Hard Lambda GB-seconds

Why: Lambda bills memory × time.

What to do: 1,000,000 invocations, 512 MB memory, 200 ms each. How many GB-seconds?

How:
  1. Write down the numbers the task gives you.
  2. Work it out step by step on paper.
  3. Type only the final answer in the box.
  4. Click Check answer.

Revision, comparison and interview cards

Revise this lesson in a few minutes. Save the cards on your phone or download them as PNG files. The QR code on each card opens this lesson.

Revision card: Serverless notes architecture. Pieces must fit: UI, API, compute, data. S3+CloudFront UI → API Gateway → Lambda → DynamoDB. Draw the boxes first; then create table, role, function, API, CDN. Browser lists notes via GET /notes; never talks to DynamoDB directly.
Revision card: Serverless notes architecture · Download PNG · Open card page
Revision card: Least-privilege IAM for the lab. AdministratorAccess on a student role is a foot-gun. Basic logging policy + inline DynamoDB actions on one table ARN. PutItem GetItem DeleteItem Scan (lab list) only on yourname-notes. Wrong table name in policy → AccessDenied in CloudWatch.
Revision card: Least-privilege IAM for the lab · Download PNG · Open card page
Revision card: Wire API, CORS and API_BASE. UI and API are different origins. One wrong URL breaks the demo. HTTP API routes + CORS; set const API_BASE in app.js (no trailing slash). GET/POST /notes, DELETE /notes/{id}; paste invoke URL into app.js. Add note in UI → Network 201 → list refreshes.
Revision card: Wire API, CORS and API_BASE · Download PNG · Open card page
Revision card: Always clean up lab resources. Forgotten CloudFront and tables still cost or clutter. Delete CDN, empty S3, delete API, Lambda, table, IAM role. Disable CloudFront first (waits), then delete; confirm Cost Explorer. Bill near zero after cleanup — free tier covered the lab.
Revision card: Always clean up lab resources · Download PNG · Open card page
Comparison: S3 + CloudFront UI vs EC2-hosted UI. S3 + CloudFront UI: Static files; no OS to patch for the UI. EC2-hosted UI: You manage a web server OS.
Comparison: S3 + CloudFront UI vs EC2-hosted UI · Download PNG · Open card page
Comparison: API Gateway HTTP API vs Lambda Function URL. API Gateway HTTP API: Routes, stages, JWT authorizers, CORS config. Lambda Function URL: Direct HTTPS endpoint on one function.
Comparison: API Gateway HTTP API vs Lambda Function URL · Download PNG · Open card page
Interview card: Why is Scan OK in the lab list but not for production user lists?. Lab has few items; production must Query by user key. Scan cost and latency grow with table size. Add userId PK (or GSI) before real users arrive.
Interview card: Why is Scan OK in the lab list but not for production user lists? · Download PNG · Open card page
Interview card: How would you add login next?. Amazon Cognito user pool + JWT authorizer on the HTTP API. Store userId on each note; Query that user's items only. UI gets tokens from Cognito; API verifies JWT.
Interview card: How would you add login next? · Download PNG · Open card page
Interview card: Why CloudFront OAC with a private S3 bucket?. Bucket stays private; only CloudFront can read objects. Users get HTTPS and caching without public bucket ACLs. Attach the bucket policy CloudFront shows when you create OAC.
Interview card: Why CloudFront OAC with a private S3 bucket? · Download PNG · Open card page
Interview card: Name the four AWS pieces in the notes app.. S3+CloudFront (UI), API Gateway (HTTP), Lambda (logic), DynamoDB (data). No EC2 required for the lab path. Draw it in interviews — shows you see the whole path.
Interview card: Name the four AWS pieces in the notes app. · Download PNG · Open card page

More cards: Ravi Sir's Cards.