RestApi Tutorial
This tutorial will guide you through creating a REST API using the mango_pycore library.
This tutorial is the second part of the Hello World tutorial.
Prerequisites
Before starting, make sure you have the following tools installed:
Python 3.8 or later
AWS SAM CLI
Docker (for local testing)
mango_pycore library
Step 2: Modifying the URL GET Parameters
Inside the app.py file we previously created, we will add the following function:
from mango_pycore.api.rest_api import RestApi
from mango_pycore.api.request import RestApiRequest
from mango_pycore.api.response import Response
app = RestApi()
app.debug = True
CIUDADES = {
'Bogota': 'Cundinamarca',
'Medellin': 'Antioquia',
}
@app.route(path="/", methods=["GET"])
def hello_world(request: RestApiRequest):
return Response(
code=200,
body={"message": "Hello World"}
)
@app.route(path='/ciudades/{ciudad}', methods=['GET'])
def depto_ciudad(request: RestApiRequest):
ciudad = request.path_parameters.get('ciudad')
departamento = CIUDADES.get(ciudad)
return Response(
code=200,
body={'departamento': departamento}
)
NOTICE: We added the CIUDADES dict and the depto_ciudad function to the app.py file. In the depto_ciudad function, we are using the path_parameters attribute of the request object to get the value of the ciudad parameter in the URL.
Step 3: Modifying the SAM Template
Inside the file named template.yaml we previously created, we will add the following content:
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
MANGO_Boilerplate_BackEnd Stack
Globals:
Function:
Timeout: 30
MemorySize: 512
Resources:
RestApi:
Type: AWS::Serverless::Api
Properties:
StageName: v1
HelloWorldFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: .
Handler: app.app
Runtime: python3.8
Architectures:
- x86_64
Events:
optionsCollection:
Type: Api
Properties:
RestApiId: !Ref RestApi
Method: options
Path: /{proxy+}
helloWorld:
Type: Api
Properties:
RestApiId: !Ref RestApi
Method: get
Path: /
depto_ciudad:
Type: Api
Properties:
RestApiId: !Ref RestApi
Method: get
Path: /ciudades/{ciudad}
Outputs:
UrlApi:
Description: URL of your API
Value:
Fn::Sub: 'http://127.0.0.1:3000/v1'
HelloWorldFunction:
Description: "Hello World Lambda Function ARN"
Value: !GetAtt HelloWorldFunction.Arn
HelloWorldFunctionIamRole:
Description: "Implicit IAM Role created for Hello World function"
Value: !GetAtt HelloWorldFunction.Arn
NOTICE: We added the depto_ciudad event to the HelloWorldFunction resource. This event will trigger the depto_ciudad function when a GET request is made to the /ciudades/{ciudad} URL.
Remember to rebuild the application using the sam build command and start the API using the sam local start-api command.
This template defines:
RestApi: An AWS API Gateway that exposes your Lambda function.
HelloWorldFunction: The AWS Lambda function that will handle API requests.
Outputs: Provides the URL of your API and the ARN of your Lambda function.
Step 4: Testing the new URL Parameters
Build your application using the following command:
$ sam build
$ sam local start-api
Your API will be available at http://127.0.0.1:3000.
Send a GET request with the depto_ciudad parameter to the URL:
$ http GET localhost:3000/ciudades/Medellin
HTTP/1.1 200 OK
"departamento": "Antioquia"
$ http GET localhost:3000/ciudades/Bogota
HTTP/1.1 200 OK
"departamento": "Cundinamarca"
PUT request
Our examples have only allowed GET requests. It’s possible to support additional HTTP methods with mango-pycore. Here’s an example of a view function that supports PUT:
@app.route(path='/ciudades/{ciudad}', methods=['PUT'])
def update_ciudad(request: RestApiRequest):
ciudad = request.path_parameters.get('ciudad')
# Parse the request body as JSON
data = json.loads(request.body)
departamento = data.get('departamento')
# Update the CIUDADES dictionary
CIUDADES[ciudad] = departamento
return Response(
code=200,
body={"ciudades": CIUDADES}
)
Remember to add the event to the template.yaml file, to rebuild the application using the sam build command and start the API using the sam local start-api command.
To Test it RUN the following command in your terminal:
http PUT localhost:3000/ciudades/Bogota departamento="Santander"
You should get the response:
HTTP/1.1 200 OK
"ciudades": {
"Bogota": "Santander",
"Medellin": "Antioquia"
}
NOTICE that we are using the PUT method to update the value of the departamento key in the CIUDADES dictionary. We are using attributes of the request object to get the path_parameters and the body of the request. This is done through the RestApiRequest object from mango-pycore.
POST request
Here’s an example of a view function that supports POST:
@app.route(path='/ciudades', methods=['POST'])
def create_ciudad(request: RestApiRequest):
# Parse the request body as JSON
data = json.loads(request.body)
ciudad = data.get('ciudad')
departamento = data.get('departamento')
# Add the new ciudad to the CIUDADES dictionary
CIUDADES[ciudad] = departamento
return Response(
code=201, # 201 Created
body={"ciudades": CIUDADES}
)
Remember to add the event to the template.yaml file, to rebuild the application using the sam build command and start the API using the sam local start-api command.
To Test it RUN the following command in your terminal:
http POST localhost:3000/ciudades ciudad="Cartagena" departamento="Bolivar"
You should get the response:
HTTP/1.1 201 Created
"ciudades": {
"Bogota": "Cundinamarca",
"Medellin": "Antioquia",
"Cartagena": "Bolivar"
}
NOTICE that we are using the POST method to add a new key-value pair to the CIUDADES dictionary.
DELETE request
Here’s an example of a view function that supports DELETE:
@app.route(path='/ciudades/{ciudad}', methods=['DELETE'])
def delete_ciudad(request: RestApiRequest):
ciudad = request.path_parameters.get('ciudad')
# Delete the ciudad from the CIUDADES dictionary
del CIUDADES[ciudad]
return Response(
code=200,
body={"ciudades": CIUDADES}
)
Remember to add the event to the template.yaml file, to rebuild the application using the sam build command and start the API using the sam local start-api command.
To Test it RUN the following command in your terminal:
http DELETE localhost:3000/ciudades/Bogota
You should get the response:
HTTP/1.1 200 OK
"ciudades": {
"Medellin": "Antioquia"
}
REQUEST
The request.path_parameters and the request.body are part of the RestApiRequest object from mango-pycore which also has the following properties:
self.headers = event['headers']
self.method = event['requestContext']['httpMethod']
self.path = event['path']
self.timestamp = event['requestContext']['requestTimeEpoch']
self.date = datetime.datetime.utcfromtimestamp(self.timestamp/1000)
self.route_key = f"{self.method} {event['requestContext']['resourcePath']}"
self.stage = event['requestContext']['stage']
# Conditional Data
self.api_id = event['requestContext'].get('apiId')
self.request_id = event['requestContext'].get('requestId')
self.cookies = event.get('cookies')
self.body = event.get('body', {}) if event.get('body', {}) else '{}'
self.is_base_64_encoded = event.get('isBase64Encoded')
self.path_parameters = event.get('pathParameters', {})
self.query_string = event.get('queryStringParameters', {}) if event.get('queryStringParameters') else {}
self.protocol = event['requestContext'].get('protocol')
self.source_ip = event['requestContext']['identity'].get('sourceIp')
self.host = event['requestContext'].get('domainName')
This is further explained in the mango_pycore documentation.
Conclusion
Congratulations! You’ve successfully created and deployed a Rest API using mango_pycore and AWS SAM. You can now expand this basic example to build more complex serverless applications.