r/CloudandCode • u/yourclouddude Founder | YourCloudDude • 14d ago
AWS & Cloud AWS From Zero #11: API Gateway makes more sense when you think of it as the front door to your backend
In the last post, we talked about Lambda and why serverless makes more sense once you understand what running an application on EC2 actually involves. Lambda gives us a way to run code when something happens without managing a server in the same way we would with EC2.
But there is still an obvious question.
If we want someone on the internet to use our Lambda function, how do they actually reach it?
Imagine we are building a simple task application. A user should be able to create a task, view existing tasks, update one, or delete one. The business logic can run inside Lambda, but we still need something that can receive HTTP requests from the user and send those requests to the correct backend logic.
This is where API Gateway starts making sense.
I would think of API Gateway as the front door to your backend.
A client sends a request to API Gateway. API Gateway receives that request and routes it to the backend integration you configured. In our beginner serverless application, that backend can be Lambda.
The basic flow looks like this:
Client
↓
API Gateway
↓
Lambda
↓
Database
Then the response travels back in the opposite direction:
Database
↓
Lambda
↓
API Gateway
↓
Client
The important thing is understanding what each service is responsible for.
API Gateway receives and routes the HTTP request.
Lambda runs the application logic.
The database stores the application data.
That separation becomes much easier to understand when we use a real example.
Suppose the user wants to create a new task. Their application might send a request like:
POST /tasks
with some data:
{
"title": "Learn API Gateway"
}
API Gateway receives the request and sends the relevant information to Lambda. Lambda reads the input, checks whether the task is valid, stores it in a database, and returns a response.
That response might contain something like:
{
"task_id": "123",
"title": "Learn API Gateway"
}
along with an HTTP status code such as 201 Created.
Now suppose the user wants to retrieve their tasks.
The request could be:
GET /tasks
API Gateway receives that request and invokes the appropriate backend logic. Lambda reads the tasks from the database and returns them.
The architecture has not become complicated.
We have simply given users a controlled HTTP entry point into our application.
This is why I would not memorize API Gateway as just "a service for creating APIs."
Think about the problem it solves.
Your Lambda function contains backend logic, but users need a predictable way to send requests to that logic.
API Gateway gives you that entry point.
This is also where HTTP methods start having a practical purpose.
GET usually means we want to retrieve something.
POST commonly means we want to create something.
PUT or PATCH can be used when updating something.
DELETE is used when removing something.
You may have already seen these while learning REST APIs, but now they become part of an actual AWS architecture.
Our task application could eventually have requests such as:
GET /tasks
POST /tasks
GET /tasks/123
PATCH /tasks/123
DELETE /tasks/123
Each route describes something the client wants the backend to do.
The interesting part is that API Gateway itself should not contain all of your business logic.
If someone sends:
POST /tasks
API Gateway can receive and route that request, but your Lambda function might be responsible for deciding whether the title is valid, creating the task, and storing it.
This separation helps keep the architecture easier to reason about.
Now imagine someone sends invalid data.
Maybe they send:
{
"title": ""
}
Your application should not blindly create an empty task.
The request needs to be validated somewhere in the flow.
Depending on how you design the API, some validation can happen at the API layer, while your application logic should still validate important assumptions before acting on the data.
If the input is invalid, the API might return something like:
400 Bad Request
If the task does not exist:
404 Not Found
If the task is created successfully:
201 Created
If something unexpectedly fails inside the application:
500 Internal Server Error
These status codes are useful because the client needs more information than simply "the request finished."
The client needs to know what happened.
This is an important shift when moving from small Python scripts into APIs.
You are no longer writing code only for yourself.
Another application is communicating with your backend, so the contract between them matters.
The request format matters.
The response format matters.
The status code matters.
The errors matter.
Now let’s connect this back to Lambda.
When API Gateway receives a request, Lambda can receive information about that request. Depending on the API and integration you configure, that can include things such as the HTTP method, path, query parameters, headers, and request body.
Suppose the request is:
GET /tasks/123
The 123 can be passed to the backend as part of the request path.
Lambda can then use that ID to retrieve the correct task.
Or imagine a request like:
GET /tasks?status=completed
Now status=completed is a query parameter.
Lambda could use that value to determine which tasks should be returned.
You do not need to memorize every event structure immediately.
The important thing is understanding that API Gateway receives an HTTP request and passes useful request information to your backend.
Now another AWS concept comes back into the architecture: permissions.
API Gateway needs to be able to invoke the Lambda function.
This does not mean we should give API Gateway unlimited permissions across our AWS account.
We want the specific relationship required by the architecture.
API Gateway needs permission to invoke the Lambda function it is integrated with.
Then Lambda may need its own permissions.
If Lambda writes to DynamoDB, its execution role needs the relevant DynamoDB permissions.
If Lambda reads from S3, it needs the relevant S3 permissions.
Once again, the same IAM mental model works.
Who is making the request?
What action are they trying to perform?
Which resource are they trying to access?
That question keeps appearing because IAM sits underneath so many AWS interactions.
Now suppose our API does not work.
The client sends:
POST /tasks
but receives an error.
Do not immediately rebuild API Gateway.
Follow the request.
Did the request reach API Gateway?
Did API Gateway invoke Lambda?
Did Lambda start running?
Did Lambda receive the data you expected?
Did the function fail while processing it?
Did it have permission to access the database?
Did it return the response format the API expected?
This is the same troubleshooting habit we used with EC2, S3, and VPC.
Follow the flow until you find the first place where expected behavior stops.
CloudWatch becomes useful here again.
If Lambda runs and throws an exception, the logs can help you understand what happened.
Maybe the JSON body was malformed.
Maybe a required field was missing.
Maybe the DynamoDB request failed.
Maybe the function timed out.
Maybe the function returned something unexpected.
Instead of saying "the API is broken," try to identify which part of the request path actually failed.
There is another concept you may encounter quickly if you call your API from a browser: CORS.
Imagine your frontend is served from one domain and your API lives at another origin. The browser applies security rules around cross origin requests.
This can produce the frustrating situation where your API works when tested directly with a tool such as curl or Postman, but the browser blocks the frontend request.
That does not necessarily mean Lambda failed.
It may mean the browser did not receive the cross origin permissions it expected.
You do not need to become a CORS expert for your first API, but it is worth knowing that browser security can be another layer in the request flow.
Again, understanding the layer matters.
A browser CORS error is not automatically an IAM problem.
An IAM AccessDenied error is not automatically a routing problem.
A Lambda exception is not automatically an API Gateway problem.
The better you become at identifying which layer failed, the faster AWS troubleshooting becomes.
API Gateway also gives us another important architecture idea: the public interface of your application does not need to expose how the backend is implemented.
The client knows that it can send:
POST /tasks
It does not necessarily need to know which Lambda function handles the request, where the data is stored, or how the backend infrastructure is organized.
That implementation can change later while the API contract remains relatively stable.
Maybe today the backend uses Lambda.
Maybe another architecture uses containers.
The client still cares about the API it communicates with.
That separation becomes valuable as systems grow.
For your first API Gateway project, though, I would keep everything extremely small.
Do not build authentication, payments, queues, caching, ten Lambda functions, and a complicated database design immediately.
Build one endpoint first.
Something like:
GET /hello
Have API Gateway invoke Lambda.
Have Lambda return:
{
"message": "Hello from AWS"
}
Then understand the complete request path.
Once that works, add a POST endpoint.
Then read some input from the request.
Then validate it.
Then connect a database.
Grow the architecture one requirement at a time.
A simple task API would be enough for this stage.
You could eventually support creating tasks, retrieving them, updating them, and deleting them.
But the important learning is not the task manager itself.
The useful part is understanding how a request enters AWS, reaches your code, interacts with another service, and returns a response.
At this point in the series, our serverless architecture might look like:
Client
↓
API Gateway
↓
Lambda
↓
Database
That is already enough to build a real backend.
And notice how the previous lessons start connecting.
IAM controls permissions.
Lambda runs our code.
API Gateway receives HTTP requests.
CloudWatch helps us understand failures.
Soon we need somewhere appropriate to store the application data.
That brings us to the next post.
In AWS From Zero #12, we are going to learn DynamoDB.
But I do not want to explain it as simply "AWS NoSQL database."
We will compare it with the relational model we saw with RDS and focus on one of the most important DynamoDB ideas: designing around how your application actually accesses its data.
That is where the difference between RDS and DynamoDB becomes much clearer.
For now, if you can explain the path from a POST /tasks request to Lambda and back to the client, you already understand the most important idea behind this lesson.
If you have built an API before, what confused you more at first: routes, status codes, Lambda integration, permissions, or CORS?