Our application works great so far when adding new donors and donation events. To make it usable for an organization, for the purposes of building a user interface, we need to display those donors. Let’s build an API endpoint that will display all donors from the DynamoDB table.
Looking at it from the architecture standpoint, this is what we want to achieve:
If we transformed it into steps, they would look like so:
GET
requestGoing with the nouns to represent resources and verbs to represent actions mantra we can deduce that in this particular case we have a collection of resources, or “donors”.
I’ll go with /donors
.
Adding another API endpoint is very easy; just append these couple of lines to ~/serverless_workshop/savealife/app.py
:
@app.route("/donors", methods=["GET"])
def donors_get():
db_response = get_app_db().donors_all()
app.log.debug(f"DBResponse: {db_response}")
return Response(
body=asdict(db_response),
status_code=200 if db_response.success else 400
)
On the database front the things are almost as simple. The donors_all()
function from chalicelib/db_donor.py
may look
like this:
def donors_all(self):
db_response = DBResponse(resource_id="", success=False, error_message="", return_value={})
try:
response = self._table.scan(
FilterExpression=Key("PK").begins_with("DONOR"),
ReturnConsumedCapacity="TOTAL",
)
donors = response.get("Items")
self._logger.debug(f"Consumed {response.get('ConsumedCapacity').get('CapacityUnits')} capacity units")
self._logger.debug(f"Fetched {len(donors)} donor(s)")
if response.get("ResponseMetadata").get('HTTPStatusCode') == 200:
db_response.success = True
db_response.return_value = donors
return db_response
except Exception as exc:
db_response.success = False
db_response.error_message = str(exc)
self._logger.exception(exc)
finally:
return db_response
Don’t forget the import from boto3.dynamodb.conditions import Key
The highlighted part is the most interesting so let’s go over it with a bit more detail. While inserting data with previous
functions we relied on the put_item()
function to write data to DynamoDB. Reading data from DynamoDB can be achieved
by using:
Scan
operationsQuery
operationsScan
operations are less efficient because they always scan the entire table or index. Optionally filters can be applied
to the data set but they are only applied after reading (scanning) all data first. In SQL terms you can think of them as:
They can return up to 1MB of data and if more data is to be returned it is then paged.
Query
operations are much more precise than Scan
operations, and to be precise they require us to provide the
primary key value to query with. In SQL terms you can think of them as:
Looking back to our code, we can see that it performs a Scan
operation ($$$) with some server-side filtering; we
are asking the table to give us all the items whose primary key PK
begins with DONOR
. Let’s see it in action:
http -b GET :8000/donors
will output something similar to:
Deploy at will and verify that your function is returning data when running locally and in AWS.
chalice deploy
Performing that GET
request to list all donors is now working as it should, showing all donors
http -b GET $(chalice url)/donors
Great success!