Build a REST API with API Gateway

Focus: build a rest api with api gateway

Sponsored

You've built Lambda functions and touched S3, but when you try to expose your Python backend to the real world, you hit a wall: Lambda has no public URL by default, and wiring up a server feels like reinventing the wheel. That's the pain this lesson solves. You'll learn to build a REST API with API Gateway — the AWS service that gives your Lambda code a secure, scalable HTTP front door. No more manual EC2 setups or fragile custom servers; by the end, you'll have a production-ready REST endpoint you can call from any client.

The problem this lesson solves

Every backend developer eventually needs to share their code as an API. Maybe you've written a Python function that processes data, or a service that needs to be called by a mobile app. Raw Lambda functions are powerful, but they respond to AWS events — not HTTP requests from the internet. Without an API layer, you're stuck: either you provision an EC2 instance, install a web server, manage SSL certificates, and handle scaling yourself, or you expose your Lambda directly (which is insecure and limited).

API Gateway eliminates this entire class of problems. It acts as a managed HTTP endpoint that sits in front of your Lambda functions, handling authentication, rate limiting, request validation, and even caching — all without you writing a single line of infrastructure code. This lesson gives you the exact steps to build a REST API with API Gateway, from a blank console to a working endpoint with a Lambda integration.

Core concept / mental model

Think of API Gateway as a receptionist for your backend. When a client sends an HTTP request (like GET /items or POST /items), the receptionist checks the request against your defined routes, validates it, and then forwards it to the right worker — your Lambda function. The Lambda processes the event and returns a response, which the receptionist sends back to the client in a proper HTTP format.

Key definitions:

  • API Gateway – A fully managed AWS service that creates, publishes, maintains, monitors, and secures APIs at any scale. It supports REST APIs, HTTP APIs, and WebSocket APIs.
  • Resource – A path in your API, like /items or /users.
  • Method – An HTTP verb (GET, POST, PUT, DELETE) applied to a resource.
  • Integration – The backend that handles the request; for us, it's a Lambda function.

Why API Gateway instead of just calling Lambda directly? Lambda functions can be invoked via the AWS SDK, but that requires AWS credentials on the client side — a security nightmare. API Gateway provides a public, credential-free URL that translates HTTP into a Lambda invocation, adding a security boundary and throttling to protect your function from abuse.

Here's a word-picture of the flow:

Client (browser/mobile) --> API Gateway (REST endpoint) --> Lambda (Python) --> DynamoDB/other
        <--------------------------------------------- response <-----------------------------------

How it works step by step

Creating a REST API with API Gateway involves a logical sequence of decisions and actions. Here's the mental flow you'll follow every time you build one:

  1. Design your API surface – Decide what resources and methods you need. For a simple items API: GET /items to list, POST /items to create, GET /items/{id} to fetch one.
  2. Create a Lambda function – Write a Python handler that accepts an event and context and returns a JSON response. This is your business logic.
  3. Create the API in API Gateway – Choose REST or HTTP API (more on this in the compare section). REST offers more features; HTTP is lighter.
  4. Define resources and methods – Add the paths and verbs you designed.
  5. Set up the integration – Point the method to your Lambda function. API Gateway will invoke the Lambda on each request.
  6. Deploy the API – Create a deployment and associate it with a stage (like prod). This gives you a public URL.
  7. Test and iterate – Call your endpoint, check logs in CloudWatch, and adjust as needed.

Why each step matters:

  • Design ensures your API is intuitive.
  • Lambda isolates your logic and scales automatically.
  • API Gateway handles the HTTP details and security.
  • Deployment is crucial; without a stage, your API isn't publicly accessible.

Hands-on walkthrough

Let's build a real REST API with API Gateway and a Python Lambda function. We'll create a simple GET /hello endpoint that returns a friendly message. This exercise covers both objectives: understanding the concept and applying it.

Step 1: Create the Lambda function

In the AWS Console, go to Lambda > Create function. Choose Author from scratch, name it hello-api, runtime Python 3.12, and create the function. Replace the default code with:

import json

def lambda_handler(event, context):
    """A simple Lambda that returns a greeting."""
    return {
        'statusCode': 200,
        'headers': {'Content-Type': 'application/json'},
        'body': json.dumps({'message': 'Hello from API Gateway!'})
    }

Click Deploy. Test it by creating a test event with input {} and you should see the response body.

Step 2: Create the API Gateway REST API

  1. Go to API Gateway service > Create API.
  2. Choose REST API (not Private) and click Build.
  3. Name it hello-api, use Regional endpoint (appropriate for most use cases), and create.

After creation, you'll see the Resources pane. A root resource (/) already exists.

Step 3: Create a resource and method

  • Select the root resource (/), click Actions > Create Resource.
  • Name it hello, resource path /hello, and enable API Gateway CORS if needed (we'll skip for now).
  • With /hello selected, click Actions > Create Method, choose GET, and save.

Step 4: Set up Lambda integration

For the GET method: - Integration type: Lambda Function - Use Lambda proxy integration: Checked (recommended) - Lambda Region: your region (e.g., us-east-1) - Lambda Function: hello-api

Click Save and confirm that you grant API Gateway permission to invoke the function.

Step 5: Deploy the API

  • Click Actions > Deploy API.
  • New stage: name it prod.
  • After deployment, you'll get an Invoke URL like https://xxxxxxxxx.execute-api.us-east-1.amazonaws.com/prod.

Now test it by opening the URL + /hello in your browser:

curl https://xxxxxxxxx.execute-api.us-east-1.amazonaws.com/prod/hello

Expected output:

{"message": "Hello from API Gateway!"}

Bonus: Handle path parameters

Extend the Lambda to read a name from the path. Create a resource /greet/{name}, set it up as before, and update the Lambda:

import json

def lambda_handler(event, context):
    name = event.get('pathParameters', {}).get('name', 'stranger')
    return {
        'statusCode': 200,
        'body': json.dumps({'message': f'Hello, {name}!'})
    }

Deploy again and call:

curl https://your-api-url/prod/greet/Alex

Output:

{"message": "Hello, Alex!"}

Compare options / when to choose what

API Gateway offers two primary API types for HTTP workloads: REST API and HTTP API. Here's how they stack up:

Feature REST API HTTP API
Cost Higher ($3.50 per million requests) Lower ($1.00 per million requests)
Features Full: API keys, usage plans, WAF integration, custom domain, caching Basic: Lambda integration, JWT authorization, CORS
Performance Slightly higher latency Lower latency, optimized for Lambda
Use case Enterprise, monetized APIs, complex requirements Simple backends, microservices, cost-sensitive projects

When to choose REST: You need API keys for client authentication, usage plans to throttle by customer, or advanced features like request validation and caching. It's the classic choice for production APIs.

When to choose HTTP: You're building a lightweight service with Lambda + Python, and you only need JWT auth or none. It's cheaper and simpler, ideal for internal microservices.

Variations on integration:

  • Proxy integration (what we used) passes the entire HTTP request to Lambda, including headers, query string, and body. Your Lambda must return the correct format, as shown.
  • Non-proxy integration requires you to map request fields to Lambda input manually via templates. More control but more work. For most use cases, proxy is the way to go.

For this lesson, REST API is the right choice because it's the standard for learning and offers the full feature set you'll need as you grow.

Troubleshooting & edge cases

Even with a simple setup, you'll hit common issues. Here's how to fix them.

1. 502 Bad Gateway / Internal Server Error

  • Cause: Lambda crashed or returned an invalid response format.
  • Fix: Check CloudWatch Logs for the Lambda. Ensure your handler returns a dict with statusCode and body, and that body is a JSON string (use json.dumps).

2. 403 Forbidden

  • Cause: API Gateway doesn't have permission to invoke Lambda, or the Lambda resource policy is missing.
  • Fix: Go to Lambda > Permissions > Resource policy and ensure API Gateway is allowed. Or in API Gateway, re-save the integration and confirm the permission prompt.

3. Model validation errors in the console

  • Cause: Accidentally set up a request validator with a model that doesn't match your request.
  • Fix: In method settings, remove the request validator or define a proper JSON schema. For this tutorial, you don't need it.

4. CORS errors when calling from a browser

  • Cause: The API doesn't include CORS headers, so a frontend on a different domain can't read the response.
  • Fix: Enable CORS on the resources by going to Actions > Enable CORS, which automatically adds the necessary headers. For proxy integration, ensure your Lambda includes Access-Control-Allow-Origin in the headers, as in this snippet:
return {
    'statusCode': 200,
    'headers': {'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*'},
    'body': json.dumps({'message': 'Hello'})
}

5. Deployed API still uses old code

  • Cause: You updated the Lambda but didn't redeploy the API stage.
  • Fix: After any Lambda change, go to API Gateway > Actions > Deploy API again to update the stage.

Pro tip: Always enable logging in API Gateway to trace issues. You can set up CloudWatch logs by creating a log group and enabling CloudWatch logging in the stage settings. This gives you a full request/response log for debugging.

What you learned & what's next

You now know how to build a REST API with API Gateway: from creating a Lambda function with Python, to defining resources and methods, to deploying a public stage. You understand the mental model of API Gateway as a front door, the value of proxy integration, and how to troubleshoot the most common errors. You met both learning objectives: explaining the core idea and completing a practical exercise.

Keep these concepts in mind: API Gateway is your HTTP handshake with the world — it handles security, throttling, and scaling so your Lambda can focus on logic.

Next step: In the next lesson, you'll connect your API Gateway to DynamoDB, turning your hello endpoint into a full CRUD API that persists data. You'll extend the same pattern — API Gateway + Lambda — to create, read, update, and delete records, which is the backbone of most real-world backends. That's where your journey into serverless web APIs truly takes off.

Practice recap

Go back to your console and create a new resource /status that returns {'status': 'ok'}. Then deploy it to a test stage and call it with curl. This short exercise solidifies the deployment cycle and gives you extra muscle memory for the next lesson on DynamoDB CRUD APIs.

Common mistakes

  • Forgetting to deploy the API after changing the Lambda — the stage still points to the old version, causing 'works locally but not on the URL' confusion.
  • Returning invalid Lambda response format (e.g., body is a Python dict instead of a JSON string), which triggers 502 errors.
  • Skipping resource policy permissions: API Gateway gets 403 because it lacks invoke access to Lambda — fix by checking Lambda's resource policy.
  • Not enabling CORS when calling from a browser, leading to unexpected client-side blocking.

Variations

  1. Use HTTP API instead of REST API when you need lower cost and a simpler feature set — it's cleaner for microservices.
  2. Use non-proxy (custom mapping) integration when you need to control exactly how HTTP requests map to Lambda input, e.g., to enforce a strict schema.
  3. Add API keys and usage plans in REST API for monetized or tiered APIs, giving each client a separate throttle limit.

Real-world use cases

  • Expose a machine learning inference function as a public REST endpoint for a web app.
  • Build a serverless contact form backend that processes submissions and sends emails via Lambda.
  • Create a mobile app login API that calls Lambda for password verification and returns a JWT token.

Key takeaways

  • API Gateway sits between clients and Lambda, translating HTTP requests into invocations.
  • REST API offers full features (API keys, usage plans), while HTTP API is cost-effective and simple.
  • Lambda proxy integration is the easiest way to connect — your function receives the entire request.
  • Deploying the API to a stage is mandatory; without it, you have no public URL.
  • Common errors (403/502) are usually permission or response-format issues, both easy to fix with logging.
  • Always test with curl and inspect CloudWatch logs when things go wrong.

Sponsored

Sponsored

Discussion

Questions, corrections, and tips help everyone reading this page.

0 comments

Add a comment

Shown publicly with your comment.

Be constructive · max 4,000 characters

No comments yet — start the thread.

Related tutorials, quizzes, and articles for this topic.