47
loading...
This website collects cookies to deliver better user experience
API Gateway HTTP APIs are roughly a third of the cost of API Gateway REST APIs. They do not support all integrations and features of REST APIs, you can read more here: Choosing between HTTP APIs and REST APIs
The source code is available in full here: https://github.com/simonireilly/sst-python-api
npx create-serverless-stack@latest \
api-stack \
--language typescript \
--use-yarn
api-stack/lib/index.ts
, removing lines 6,7 and 8, so it looks like this:// api-stack/lib/index.ts
import MyStack from "./MyStack";
import * as sst from "@serverless-stack/resources";
export default function main(
app: sst.App
): void {
new MyStack(app, "my-stack");
}
python3 -m venv env
source $PWD/env/bin/activate
pip3 install mangum fastapi pydantic
src
folder there is currently a api-stack/src/lambda.ts
file. This file is not needed, and we can replace that file with a new one, that will be python based..
├── api-stack
│ ├── src
│ │ └── api.py
# api-stack/src/api.py
from mangum import Mangum
from fastapi import FastAPI
APP = FastAPI(
title="Example Test API",
description="Describe API documentation to be served; types come from "
"pydantic, routes from the decorators, and docs from the fastapi internal",
version="0.0.1",
)
@APP.get("/v1/items")
def list_items():
"""
Return a collection of items
"""
return {
'message': 'Hello, world!'
}
handler = Mangum(APP, lifespan="off")
src
directory. We have also define a proxy route, that will route all API requests to our API handler created above.// api-stack/lib/MyStack.ts
import * as sst from "@serverless-stack/resources";
export default class MyStack extends sst.Stack {
constructor(scope: sst.App, id: string, props?: sst.StackProps) {
super(scope, id, props);
// Create a HTTP API
const api = new sst.Api(this, "Api", {
defaultFunctionProps: {
srcPath: "src",
runtime: "python3.8"
},
routes: {
"ANY /{proxy+}": "api.handler",
},
});
// Show the endpoint in the output
this.addOutputs({
"ApiEndpoint": api.url,
});
}
}
src/api.py
. Later, we can transition to something that looks more like a Fat-Lambda.Really there are many schools of thought on this topic. If you want to delve deeper then this is quite a nice round up that includes the thoughts of some serverless Hero's. I personally favour operational agility and every pattern has a use case, but its best to know the pros and cons.
yarn start
curl https://qpk7n59tz3.execute-api.us-east-1.amazonaws.com/v1/items | jq .
{
"message": "Hello, world!"
}
Looking under the hood, we have just deployed two cloud formation stacks, and these stacks work together so we can develop locally; read more here to understand how serverless-stack creates its live development environment
@APP.get("/v1/items")
def list_items():
"""
Return a collection of items
"""
print("Hello, local world!")
return {
'message': 'Hello, world!'
}
d2755156-e548-4e91-a5f3-e6a1733ef248 REQUEST dev-api-stack-my-stack-ApiLambdaANYproxyB463B5F1-cBeRkQMraTip [src/api.handler] invoked by API GET /v1/items
Hello, local world!
INFO:mangum.http:HTTPCycleState.REQUEST: 'http.response.start' event received from application.
INFO:mangum.http:HTTPCycleState.RESPONSE: 'http.response.body' event received from application.
d2755156-e548-4e91-a5f3-e6a1733ef248 RESPONSE {"isBase64Encoded":false,"statusCode":200,"headers":{"content-length":"27","content-type":"application/json"},"body":"{\"message\":\"Hello, world!\"}"}
GET /docs
URL of the API:api.py
file:# api-stack/src/api.py
from typing import List, Optional
from mangum import Mangum
from fastapi import FastAPI
from pydantic import BaseModel, validator
from datetime import date, timedelta
class Item(BaseModel):
"""
This model will have validation of the below fields
"""
name: str
description: Optional[str] = None
price: float
date: date
@validator('date')
def check_date_is_valid(cls, v):
"""
We also have specific validation on the date
"""
if v > date.today():
raise ValueError('Date must be in the past.')
if v < date.today() - timedelta(days=120):
raise ValueError('Date must be within 120 days')
return v
APP = FastAPI(
title="Example Test API",
description="Describe API documentation to be served; types come from "
"pydantic, routes from the decorators, and docs from the fastapi internal",
version="0.0.2",
)
@APP.get("/v1/items", response_model=List[Item])
def list_items():
"""
Return a collection of items
"""
return [
Item(
name="Purple Jumper",
price=10.99,
date=date.today()
),
Item(
name="Red Jumper",
price=11.99,
date=date.today() - timedelta(days=10)
)
]
@APP.post("/v1/items", response_model=Item)
def create_item(item: Item):
"""
Adds a new Item
"""
return item
handler = Mangum(APP, lifespan="off")
GET /docs
publicly accessible to encourage collaborators. Every other route can be secured.Other options include using a Lambda Authorizer, to write fully custom authentication, it is worth reading the documentation for @aws-cdk/aws-apigatewayv2-authorizers if you have a deeper interest and then exploring the developer guide for AWS API Gateway HTTP API authorizers
// api-stack/lib/MyStack.ts
import * as sst from "@serverless-stack/resources";
export default class MyStack extends sst.Stack {
constructor(scope: sst.App, id: string, props?: sst.StackProps) {
super(scope, id, props);
// Create a HTTP API
const api = new sst.Api(this, "Api", {
defaultFunctionProps: {
srcPath: "src",
runtime: "python3.8"
},
routes: {
"ANY /{proxy+}": {
function: {
handler: "api.handler",
},
authorizationType: sst.ApiAuthorizationType.AWS_IAM,
},
"GET /docs": "api.handler",
"GET /openapi.json": "api.handler"
}
});
this.addOutputs({
"ApiEndpoint": api.url
});
}
}
This part is optional and has nothing specific to add to the code pattern, read on if you like, Postman, Non-Functional Testing, and graphs.
pip3 freeze > ./api-stack/src/requirements.txt
yarn deploy
Metric | Average | Max |
---|---|---|
Lambda Concurrent executions | N/A | 5 |
Lambda Duration | 3.9 ms | 45 ms |