You add a todo · Lambda + RDS MySQL

12 steps from your browser to the database and back.

InternetAWS Region · us-east-1VPCPublic subnet · to IGWPrivate subnet · to NATYour browseron the internetAPI GatewayHTTP API · front doorAPI GatewayWebSocket API · live lineCloudWatch Logsapp log linesInternet gatewaythe VPC's doorLambda functionruns once per requestNAT gatewayone-way exitENIfunction's network cardRDS MySQLdatabase: todoioThe live line is already open1You add a todo2Into the front door3Wake up the function4Who are you?5Step into the VPC6Ask the database7The database answers8Tell your other tabs9It shows up instantly10Back to you11Leave a note12

Text version of this diagram.

What sits where:

  • AWS Region · us-east-1: A Region is a part of the world where AWS has data centers. Most things you build live in one Region. us-east-1 is in northern Virginia. contains:
    • VPC: Virtual private cloud: your own private network inside AWS. Nothing gets in or out except the ways you allow. contains:
      • Public subnet · to IGW: A slice of the VPC that can reach the internet directly, through the internet gateway (IGW). contains:
        • NAT gateway (one-way exit): Lets things in a private subnet start connections out to the internet, while nothing outside can start one in.
      • Private subnet · to NAT: A slice of the VPC with no door to the internet. Things here can reach out through the NAT gateway, but nothing outside can reach in. contains:
        • ENI (function's network card): Elastic network interface: a virtual network card. It gives the function an address inside your VPC.
        • RDS MySQL (database: todoio): A MySQL database that AWS runs for you, including the server, patches and backups. todoio is the database's name.
      • Internet gateway (the VPC's door): The door between a VPC and the internet. Without one, nothing in the VPC can talk to the internet directly.
    • API Gateway (HTTP API · front door): The front door for the app's web requests: it receives each one and hands it to your code. An HTTP API is its simple kind for ordinary requests.
    • API Gateway (WebSocket API · live line): The front door for the app's web requests. A WebSocket API keeps a connection open, so the server can push updates to your browser instantly.
    • CloudWatch Logs (app log lines): Collects the lines your app writes about what it's doing (its logs), so you can search them later.
    • Lambda function (runs once per request): Code that AWS runs only when a request arrives. No server to look after, and you pay per request.
  • Your browser (on the internet)

Steps, in order:

  1. The live line is already open. Path: Your browser → API Gateway. When you logged in, your browser opened a WebSocket to API Gateway. It stays open, so AWS can send you messages any time.
  2. You add a todo. Path: Your browser. Your browser sends POST /todos over HTTPS.
  3. Into the front door. Path: Your browser → API Gateway. The request arrives at API Gateway, a managed HTTPS front door. There's no server or firewall for you to look after.
  4. Wake up the function. Path: API Gateway → Lambda function. API Gateway invokes your Lambda function and hands it the request as an event. Checks: iam: Lambda resource policy: API Gateway may invoke todo-api → allow.
  5. Who are you?. Path: Lambda function. The app checks your signed session cookie and finds your user id, u-123. This is the app's own login, not AWS.
  6. Step into the VPC. Path: Lambda function → ENI. The database lives in a private subnet of a VPC, so the function is attached to that VPC. AWS gives it an ENI (a network card) in the private subnet.
  7. Ask the database. Path: ENI → RDS MySQL. The app sends SQL to RDS MySQL in the private subnet on port 3306. It logs in with a database username and password, not with IAM. Checks: security-group: todo-db-sg: TCP 3306 from todo-lambda-sg → allow; mysql: MySQL user todo_app: INSERT on todoio.todos → allow.
  8. The database answers. Path: RDS MySQL → ENI → Lambda function. MySQL says: 1 row inserted, new id 42.
  9. Tell your other tabs. Path: Lambda function → ENI → NAT gateway → Internet gateway → API Gateway. The app looks up your connection id in the database and posts a message to it through the WebSocket API. It's attached to the VPC with no public IP, so it gets out through a NAT gateway and the internet gateway. Checks: iam: todo-app-lambda-role: execute-api:ManageConnections → allow.
  10. It shows up instantly. Path: API Gateway → Your browser. Every Todo.io tab you have open, on any device, shows the change without a refresh.
  11. Back to you. Path: Lambda function → API Gateway → Your browser. You get 201 Created, and "Buy milk" appears in your list.
  12. Leave a note. Path: Lambda function → CloudWatch Logs. Lambda sends what the function printed to CloudWatch Logs, so Todo.io's engineers can see what happened.
Step 1 of 12
Step 1 of 12
  1. When you logged in, your browser opened a WebSocket to API Gateway. It stays open, so AWS can send you messages any time.

    Show me the real thing

    The WebSocket handshake

    GET /prod HTTP/1.1
    Host: ws123.execute-api.us-east-1.amazonaws.com
    Upgrade: websocket
    Connection: Upgrade
    
    → API Gateway assigns connection id Ab3xYzEXAMPLE=

    Exam tipAn API Gateway WebSocket API has $connect, $disconnect and $default routes. Each one can call Lambda or an HTTP backend.

  2. Your browser sends POST /todos over HTTPS.

    Show me the real thing

    The HTTP request

    POST /todos HTTP/1.1
    Host: abc123.execute-api.us-east-1.amazonaws.com
    Cookie: session=eyJ1c2VySWQiOiJ1LTEyMyJ9.Zm9v
    Content-Type: application/json
    
    {"title":"Buy milk"}
  3. The request arrives at API Gateway, a managed HTTPS front door. There's no server or firewall for you to look after.

  4. API Gateway invokes your Lambda function and hands it the request as an event.

    • 🔑IAM Lambda resource policy: API Gateway may invoke todo-apiAllowed
    Show me the real thing

    Resource-based policy on the function: who may call it

    {
      "Effect": "Allow",
      "Principal": {
        "Service": "apigateway.amazonaws.com"
      },
      "Action": "lambda:InvokeFunction",
      "Resource": "arn:aws:lambda:us-east-1:111122223333:function:todo-api",
      "Condition": {
        "ArnLike": {
          "AWS:SourceArn": "arn:aws:execute-api:us-east-1:111122223333:abc123/*"
        }
      }
    }

    Exam tipThis is a resource-based policy. It lives on the function and says who may call it, the opposite of a policy attached to a role.

  5. The app checks your signed session cookie and finds your user id, u-123. This is the app's own login, not AWS.

  6. The database lives in a private subnet of a VPC, so the function is attached to that VPC. AWS gives it an ENI (a network card) in the private subnet.

    Exam tipA Lambda ENI never gets a public IP, so a VPC-attached function can't reach the internet, even from a public subnet. It needs a NAT gateway. Classic exam trap.

  7. The app sends SQL to RDS MySQL in the private subnet on port 3306. It logs in with a database username and password, not with IAM.

    • 🛡Security group todo-db-sg: TCP 3306 from todo-lambda-sgAllowed
    • 🗄MySQL MySQL user todo_app: INSERT on todoio.todosAllowed
    Show me the real thing

    The SQL (real code sends the values as parameters, never pasted into the string)

    INSERT INTO todos (user_id, title, done) VALUES ('u-123', 'Buy milk', false);

    Exam tipA security group rule can use another security group as its source: "anything wearing todo-lambda-sg may connect". Exam favourite.

  8. MySQL says: 1 row inserted, new id 42.

  9. The app looks up your connection id in the database and posts a message to it through the WebSocket API. It's attached to the VPC with no public IP, so it gets out through a NAT gateway and the internet gateway.

    • 🔑IAM todo-app-lambda-role: execute-api:ManageConnectionsAllowed
    Show me the real thing

    Posting to a connection

    POST /prod/@connections/Ab3xYzEXAMPLE= HTTP/1.1
    Host: ws123.execute-api.us-east-1.amazonaws.com
    
    {"type":"todo.created","todoId":"42"}

    Exam tipA Lambda attached to a VPC needs a NAT gateway (or a VPC endpoint) to reach anything outside the VPC, even AWS services like SQS.

  10. Every Todo.io tab you have open, on any device, shows the change without a refresh.

  11. You get 201 Created, and "Buy milk" appears in your list.

    Show me the real thing

    The HTTP response

    HTTP/1.1 201 Created
  12. Lambda sends what the function printed to CloudWatch Logs, so Todo.io's engineers can see what happened.

    Show me the real thing

    The log line

    2026-10-11T18:42:07Z INFO POST /todos user=u-123 status=201 14ms

    Exam tipLambda sends whatever your function prints to CloudWatch Logs automatically, as long as the execution role allows logs:PutLogEvents.

What to notice

  • There is no server at all. API Gateway takes the request and Lambda runs your code only when one arrives.
  • IAM never checked your SQL. MySQL has its own users; IAM only manages the database server itself.
  • The database sits in a private subnet with no route to the internet. Only the security groups you allowed can reach it.
  • The database password lives in the app config here. In production it belongs in Secrets Manager (Level 1).
  • To reach a database in a VPC, the function had to be attached to that VPC.
  • Every Lambda copy opens its own MySQL connection, and a traffic spike can exhaust them. RDS Proxy pools connections (Level 2).
  • A WebSocket stays open, so the server side can send you updates without you asking.
  • A Lambda in a VPC can't reach public AWS services by itself, so this VPC needed a NAT gateway.
  • You, the customer, were never an IAM user. IAM controls the app, not its customers.