A web address that runs a function
An API gateway is the front door that turns a web request into a call to a Lambda function, with no server of your own. It is how most small cloud apps answer the web, and the function must reply in the exact shape the gateway expects.
A shop wants a web address, /orders/ana, that answers with Ana's orders. The function must answer properly even when the request is wrong.
By the end of this lesson
Note
I can write the answer an API gateway expects from a function.
Key words
| Word | What it means | Example |
|---|---|---|
| Request | what a caller sends to a web address | |
| Response | what the function sends back | |
| Status code | a number saying how the request went | 404 |
| Header | a named detail sent beside the body | content-type |
| Body | the main text of a request or response |
The cells students fill in are left blank in this preview.
A request comes in, a response goes out
The gateway turns the web request into an event. The method, such as GET, is at requestContext.http.method. The path is rawPath.
The function returns a dictionary with three parts: statusCode, headers and body. The body must be a string, so use json.dumps. These are the documented shapes, not output from a stack of yours.
Run it: the smallest front door
import json
# The documented shape of an HTTP API request, cut down.
event = {
"requestContext": {"http": {"method": "GET"}},
"rawPath": "/orders/ana",
"headers": {"accept": "application/json"},
}
def handler(event, context):
method = event["requestContext"]["http"]["method"]
path = event["rawPath"]
data = {"you_asked": method + " " + path}
return {"statusCode": 200,
"headers": {"content-type": "application/json"},
"body": json.dumps(data)}
answer = handler(event, None)
print(answer["statusCode"])
print(answer["body"])
print(type(answer["body"]).__name__)
# Change "GET" to "POST" in the event and run again.
A status code says who got it wrong
A 2 at the start, such as 200 or 201, means success. A 4, such as 400 or 404, means the caller asked for something wrong. A 5 means the function itself failed.
A wrong request is not a crash. Return a 4 with a short error in the body. A function that raises an error gives the caller a vague 5, and tells them nothing they can fix.
Worked example: the codes this lesson uses
| Code | Meaning | Use it when |
|---|---|---|
| 200 | OK | You found and returned the data |
| 201 | Created | A new order was stored |
| 400 | Bad request | A field is missing or wrong, or the body is not valid JSON |
| 404 | Not found | No such path or no such order |
| 405 | Method not allowed | The path exists, the method does not |
| 415 | Unsupported media type | The content-type is not application/json |
The cells students fill in are left blank in this preview.
A common mistake
Warning
Many people let the function crash on bad input. The caller then gets a vague server error. Check the input first. When the caller is at fault, return a 4 and say what to fix. Keep a 5 for faults that are yours.
Run it: an error as a response
import json
def reply(status, data):
return {"statusCode": status,
"headers": {"content-type": "application/json"},
"body": json.dumps(data)}
def handler(event, context):
# A missing key is the caller's mistake, so answer 400, do not crash.
if "queryStringParameters" not in event:
return reply(400, {"error": "add ?customer=name"})
name = event["queryStringParameters"]["customer"]
return reply(200, {"hello": name})
print(handler({"queryStringParameters": {"customer": "ana"}}, None)["statusCode"])
print(handler({}, None)["statusCode"])
print(handler({}, None)["body"])
# Add a third line that calls handler with {"queryStringParameters": {}}. What happens?
Read the request carefully
Not every key is always there. With no query string, queryStringParameters is left out of the event. Check with in before you read it.
The body arrives as text. If isBase64Encoded is true, the text is encoded and you decode it first. Header names arrive in lower case, so look for content-type.
Run it: read a base64 body
import json
import base64
event = {
"body": base64.b64encode(b'{"item": "tea"}').decode("utf-8"),
"isBase64Encoded": True,
}
text = event["body"]
print(text)
if event["isBase64Encoded"] is True:
text = base64.b64decode(text).decode("utf-8")
print(text)
print(json.loads(text)["item"])
# Set isBase64Encoded to False and run again. What goes wrong?
What this module did not teach
Note
Everything here ran offline in Python, over answers shaped like the real ones. You did not deploy anything, sign in to a real account or see a bill. You did not meet the costs of keeping a service running. Those are the next steps, and they are best learnt with someone who can check your account settings.