One idea…

One idea we could try is to implement the “Create a donation event” feature in the same way as our donor sign-up feature.

Architecturally they would look the same:

If we transformed it into steps, it would also be:

  1. A user sends an HTTP POST request with a JSON payload
  2. API Gateway receives it and invokes the Lambda function
  3. Lambda function receives the JSON payload in the event argument

API design and Lambda function

I’ll go with /donation/create.

Our ~/serverless_workshop/savealife/app.py file can be extended to include the highlighted lines:

import logging

from os import getenv
from chalice import Chalice
from chalicelib.db import get_app_db

try:
    from dotenv import load_dotenv

    load_dotenv()
except ImportError:
    pass

first_name = getenv("WORKSHOP_NAME", "ivica")  # replace with your own name of course

app = Chalice(app_name=f"{first_name}-savealife")
app.log.setLevel(logging.DEBUG)


@app.route("/donor/signup", methods=["POST"])
def donor_signup():
    body = app.current_request.json_body

    app.log.debug(f"Received JSON payload: {body}")

    return get_app_db().donor_signup(body)


@app.route("/donation/create", methods=["POST"])
def donation_create():
    body = app.current_request.json_body

    app.log.debug(f"Received JSON payload: {body}")

    return get_app_db().donation_create(body)

When it comes to the JSON payload itself I would propose we keep things simple again. What potential donors should know about a blood donation are 4 pieces of information: in which city is it being held, on which date and at what time, which blood types are needed and what is the location. So, something like this:

Persisting to DynamoDB

Very similar to how we copy-pasted existing code for the Lambda function, our blood donation persistence layer can be copy-pasted from the donor_signup function. Add the highlighted lines to ~/serverless_workshop/savealife/chalicelib/db.py

class SavealifeDB:
    def __init__(self, table, logger):
        # ... SNIP ...


    def donation_create(self, donation_dict):
        try:
            self._table.put_item(
                Item={
                    "city": donation_dict.get("city"),
                    "datetime": donation_dict.get("datetime"),
                    "address": donation_dict.get("address")
                }
            )
            self._logger.debug(
                f"Inserted donation '{donation_dict.get('city')}, {donation_dict.get('address')}' "
                f"into DynamoDB table '{self._table}'"
            )

            return True

        except Exception as exc:
            self._logger.exception(exc)
Moment of truth

Run the local server chalice local --autoreload and hit the endpoint we just created.

http :8000/donation/create city=Haarlem blood_types="A-" address="Main Street"

Does it work? What happened?

Another error …

The endpoint, from the API perspective, works just fine but nothing will be saved to the DynamoDB table. That is because of how our DynamoDB table was created:

Having logging configured help us to clearly see the issue; first_name is our primary key and the expected attribute.

http -b POST :8000/donation/create city=Haarlem address="Main street"

will show something along the lines of:

Back to the drawing board…