DEV Community

Cover image for Building a TaskManager Serverless Python Api with AWS Lambda, API Gateway, CloudWatch events, DynamoDB and Cognito
Kenneth Kimani
Kenneth Kimani

Posted on

Building a TaskManager Serverless Python Api with AWS Lambda, API Gateway, CloudWatch events, DynamoDB and Cognito

Introduction

Welcome, tech enthusiasts and coding adventurers! 🚀

Have you ever dreamed of building a sleek, serverless Task Manager that effortlessly scales in the cloud? Whether you're setting out to create a handy to-do list or are simply fascinated by serverless technology, you’re in the right spot. In this tutorial, we’ll harness the power of AWS Lambda, API Gateway, DynamoDB, and Cognito to craft a robust Task Manager API.

We’ll explore how to manage tasks, implement authentication with Cognito, and set up automated reminders, all while keeping our infrastructure maintenance-free. Ready to dive into serverless computing and build something awesome? Let’s get started and transform your task management ideas into a fully functional reality!

Prerequisites

Before we dive into building our serverless Task Manager API, here are a few things you'll need:

  • AWS Account: Ensure you have an AWS account set up. If you don’t, you can create one here.
  • Basic Knowledge of AWS Services: Familiarity with AWS Lambda, API Gateway, DynamoDB, and Cognito will be helpful. If you're new to these services, check out the AWS documentation for a quick overview.
  • Programming Skills: Some experience with Python will be useful, as we'll be using it for our Lambda functions. If you're new to Python, you might want to brush up on the basics.
  • Postman: We’ll use Postman for testing our API endpoints. You can download it here.
  • Basic Knowledge of JSON: Since our API will interact using JSON, a basic understanding of JSON formatting will be beneficial.

Objectives

By the end of this tutorial, you'll have built a serverless Task Manager API with the following features:

  • Create Tasks: Users will be able to create tasks with titles, descriptions, due dates, and categories. Each task will be associated with a unique ID and stored in DynamoDB.
  • Retrieve Tasks: Users will be able to retrieve all their tasks or fetch a specific task by its ID.
  • Update Tasks: Users will have the ability to update the details of their tasks, including title, description, due date, and category.
  • Delete Tasks: Users will be able to delete tasks they no longer need.
  • Automated Reminders: Our system will automatically send SMS reminders for tasks that are due, thanks to AWS Lambda's scheduled events and integration with Twilio.
  • Secure Access: We'll use AWS Cognito to handle user authentication, ensuring that only authorized users can interact with their tasks.
  • Efficient Querying: We'll utilize a Global Secondary Index (GSI) in DynamoDB to efficiently query tasks based on the due_date. This allows us to easily retrieve tasks that are due on a specific date.

DynamoDB Setup

Creating the DynamoDB Table

To store and manage our tasks efficiently, we'll set up a DynamoDB table. Follow these steps:

  1. Create a DynamoDB Table:

    • Table Name: Tasks
    • Primary Key:
      • Partition Key: task_id (String)
  2. Attributes:

    • task_id: A unique identifier for each task.
    • user_id: The ID of the user who created the task.
    • title: The title of the task.
    • description: A brief description of the task.
    • due_date: The due date of the task.
    • status: The current status of the task (e.g., pending, completed).

Setting Up a Global Secondary Index (GSI)

To optimize our task retrieval based on due dates, we've set up ## Setting Up API Gateway for Task Manager

Overview

In this section, we'll walk through the setup of API Gateway for our Task Manager application. This API Gateway is designed to handle CRUD operations for tasks, including creating, retrieving, updating, and deleting tasks, and it integrates with AWS Lambda functions for processing these operations.

Step-by-Step Setup

  1. Create API Gateway
  • Navigate to API Gateway: Open the AWS Management Console and navigate to API Gateway.
  • Create a New API: Choose "Create API" and select "REST API" for our use case.
  1. Define Resources and Methods
  • Create /tasks Resource:

    • GET Method: Connect this method to a Lambda function that retrieves all tasks from the DynamoDB table.
    • POST Method: Connect this method to a Lambda function that creates a new task in DynamoDB.
  • Create /tasks/{taskId} Resource:

    • GET Method: Connect this method to a Lambda function that retrieves a specific task by ID from DynamoDB.
    • PUT Method: Connect this method to a Lambda function that updates a specific task by ID in DynamoDB.
    • DELETE Method: Connect this method to a Lambda function that deletes a specific task by ID from DynamoDB.
  1. Set Up Authorization
  • Authorization Type: Use Cognito Authorizer to secure the API endpoints.
  • Configure Cognito Authorizer:
    • In the API Gateway console, go to "Authorizers" and create a new Cognito Authorizer.
    • Link this authorizer to your API methods to enforce authentication.
  1. API Key Settings
  • API Key Required: Set this to "No" as API keys are not required for this API.
  1. Request Validation
  • Request Validator: No request validator is applied, meaning no schema validation is enforced for incoming requests.
  1. SDK Operation Naming
  • SDK Operation Name: The SDK operation names are generated automatically based on the method and path. This naming helps in integrating the API with client SDKs.
  1. Deploy the API
  • Create a Deployment Stage: After configuring the methods and settings, create a deployment stage (e.g., prod) and deploy your API.
  1. Test the API
  • Test Endpoints: Use tools like Postman or cURL to test the endpoints and ensure they work as expected.

Summary

With these steps, you've successfully set up API Gateway to handle CRUD operations for your Task Manager application, connecting each API method to its corresponding Lambda function. The integration with AWS Cognito provides secure access, while the configuration ensures that the API is ready for deployment and testing.
a Global Secondary Index (GSI) on our DynamoDB table. Here are the details:

  • Index Name: UserDueDateIndex
  • Partition Key: user_id (String)
  • Sort Key: due_date (String)
  • Read Capacity: 5 (Auto scaling is off)
  • Write Capacity: 5 (Auto scaling is off)
  • Projected Attributes: All

This index helps us efficiently fetch tasks that are due on a specific date for a particular user, ensuring our reminders and queries are both fast and scalable.

Steps to Create the GSI

  1. Navigate to DynamoDB in the AWS Console:

    • Go to the DynamoDB service in your AWS Management Console.
  2. Select Your Table:

    • Choose the Tasks table you created earlier.
  3. Create Global Secondary Index:

    • Go to the "Indexes" tab and select "Create Index".
    • Set the Index Name to UserDueDateIndex.
    • Set the Partition Key to user_id (String).
    • Set the Sort Key to due_date (String).
    • Configure Read and Write Capacity as needed (for our example, we set both to 5 and turned off auto-scaling).
    • Select "All" for Projected Attributes to include all attributes in the index.

AWS Cognito Integration

Overview

AWS Cognito provides user authentication and authorization services, allowing you to easily add user sign-up, sign-in, and access control features to your application. For our serverless Task Manager API, we've integrated AWS Cognito to handle user authentication, ensuring that only authorized users can interact with their tasks.

Setting Up AWS Cognito

  1. Create a User Pool: First, you'll need to create a User Pool in AWS Cognito. This pool will manage your users, handle sign-ups, and provide user authentication.

  2. Configure App Client: Within your User Pool, create an App Client. This client will be used by your application to interact with Cognito. Make sure to configure the callback and sign-out URLs as needed for your application.

Callback URLs: These are the URLs where users will be redirected after they successfully sign in or sign out. Make sure to include the URL of your front-end application here. For example, if your front-end is hosted on S3, the callback URL might be https://your-frontend-bucket.s3.amazonaws.com/auth/callback.

Sign-Out URLs: These are the URLs where users will be redirected after they sign out. Typically, this would be your application's home page.

  1. Set Up Cognito Authorizer in API Gateway:
    • Create a Cognito Authorizer: In API Gateway, create a new Cognito Authorizer and associate it with the User Pool you created. This authorizer will be used to validate the JWT tokens sent by clients.
    • Attach the Authorizer to Your API: Apply the Cognito Authorizer to the routes in your API Gateway. This ensures that requests to your Lambda functions are authenticated.

P.S.: Remember to save your client_id, user_pool_id, and domain. These values are crucial for configuring your application to interact with Cognito. You can find them in the AWS Cognito console:

  • Client ID: Found in the "App Clients" section of your User Pool.
  • User Pool ID: Found in the "General settings" section of your User Pool.
  • Domain: Found in the "Domain name" section under "App integration".

How Cognito Works in Our Task Manager API

  1. User Authentication: When a user signs up or signs in, they receive a JWT token from Cognito. This token is used to authenticate their requests.

  2. Authorization Header: Users must include the JWT token in the Authorization header of their requests. Our Lambda functions will decode and verify this token to ensure the request is made by an authorized user.

  3. Token Validation: Each Lambda function extracts the token from the Authorization header, decodes it using the Cognito public keys, and validates it. If the token is valid, the request is processed; otherwise, an error is returned.

Implementing Cognito in Lambda Functions

Here's how the integration is implemented in the Lambda functions:

  • Extract and Decode Token: Each Lambda function extracts the JWT token from the Authorization header and decodes it to extract the user ID.

  • Verify Token: The token is verified to ensure it was issued by AWS Cognito and is still valid. We use the jwt library to handle decoding and verification.

  • Authorize Requests: Based on the decoded user ID, the Lambda function processes the request if the token is valid. If the token is missing or invalid, an appropriate error message is returned.

Example Code

Here’s a snippet of how the JWT token is handled in one of our Lambda functions:

import jwt

def lambda_handler(event, context):
    try:
        # Extract JWT token from the Authorization header
        auth_header = event['headers']['Authorization']
        token = auth_header.split()[1]

        # Decode and verify the token
        decoded_token = jwt.decode(token, options={"verify_signature": False})
        user_id = decoded_token['sub']

        # Process the request if token is valid

    except Exception as e:
        # Handle errors
        return {
            'statusCode': 401,
            'body': json.dumps({'error': str(e)})
        }
Enter fullscreen mode Exit fullscreen mode

AWS Lambda Functions

In our serverless Task Manager application, we use several AWS Lambda functions to handle the core functionality. Here’s a breakdown of each function and its role in the application:

1. Create Task Function

This function handles creating a new task in the DynamoDB table.

Key Parts:

  • Input Parsing and Validation: Parses the request body and ensures all required fields are present.
  • Task Creation: Generates a unique task ID and prepares the task item for storage.
  • DynamoDB Post Operation: Inserts the new task into the DynamoDB table.
# Parse the request body
body = json.loads(event.get('body', '{}'))
task_title = body.get('title')
if not task_title:
    raise ValueError("Task title is required")

task_description = body.get('description', '')
due_date = body.get('due_date')

# Generate a unique task ID
task_id = str(uuid.uuid4())
item = {
    'user_id': user_id,
    'task_id': task_id,
    'title': task_title,
    'description': task_description,
    'created_at': datetime.now().isoformat(),
    'status': 'pending'
}

if due_date:
    item['due_date'] = due_date

category = body.get('category')
if category:
    item['category'] = category

# Store the item in DynamoDB
table.put_item(Item=item)

logger.info(f"Task created successfully: {task_id}")
return {
    'statusCode': 201,
    'body': json.dumps({'message': 'Task created successfully', 'task_id': task_id})
}
Enter fullscreen mode Exit fullscreen mode

2. Get Tasks Function

This function retrieves all tasks associated with the currently authenticated user from the DynamoDB table.

Key Parts:

  • Query DynamoDB: Fetches tasks from DynamoDB based on the user ID.
# Fetch tasks from DynamoDB
response = table.query(
    KeyConditionExpression=Key('user_id').eq(user_id)
)

tasks = response.get('Items', [])

logger.info(f"Retrieved {len(tasks)} tasks for user: {user_id}")
return {
    'statusCode': 200,
    'body': json.dumps(tasks)
}
Enter fullscreen mode Exit fullscreen mode

3. Get Task by ID Function

This function retrieves a specific task by its ID from the DynamoDB table.

Key Parts:

  • Retrieve Task from DynamoDB: Queries DynamoDB to fetch a specific task based on the user ID and task ID.
# Get task_id from path parameters
task_id = event['pathParameters']['taskId']

# Query the item from DynamoDB
response = table.get_item(
    Key={
        'user_id': user_id,
        'task_id': task_id
    }
)

task = response.get('Item')

if not task:
    logger.warning(f"Task not found: {task_id}")
    return {
        'statusCode': 404,
        'body': json.dumps({'message': 'Task not found'})
    }

logger.info(f"Task retrieved successfully: {task_id}")
return {
    'statusCode': 200,
    'body': json.dumps(task)
}
Enter fullscreen mode Exit fullscreen mode

4. Update Task Function

This function updates an existing task in the DynamoDB table.

Key Parts:

  • Prepare Update Expression: Constructs the update expression dynamically based on the provided fields.
# Parse the request body
if 'body' not in event:
    raise ValueError("Request body is missing")

body = json.loads(event['body'])
logger.info(f"Request body: {body}")

# Get task_id from path parameters
if 'pathParameters' not in event or 'taskId' not in event['pathParameters']:
    raise ValueError("taskId is missing in path parameters")
task_id = event['pathParameters']['taskId']

# Treat the entire body as updates
updates = body

# Prepare the update expression
update_expression = "SET "
expression_attribute_values = {}
expression_attribute_names = {}

for key, value in updates.items():
    update_expression += f"#{key} = :{key}, "
    expression_attribute_values[f":{key}"] = value
    expression_attribute_names[f"#{key}"] = key

update_expression = update_expression.rstrip(", ")

logger.info(f"Update expression: {update_expression}")
logger.info(f"Expression attribute values: {expression_attribute_values}")
logger.info(f"Expression attribute names: {expression_attribute_names}")

# Update the item in DynamoDB
response = table.update_item(
    Key={
        'user_id': user_id,
        'task_id': task_id
    },
    UpdateExpression=update_expression,
    ExpressionAttributeValues=expression_attribute_values,
    ExpressionAttributeNames=expression_attribute_names,
    ReturnValues="ALL_NEW"
)

updated_task = response['Attributes']

logger.info(f"Task updated successfully: {task_id}")
logger.info(f"Updated task: {updated_task}")

return {
    'statusCode': 200,
    'body': json.dumps(updated_task)
}
Enter fullscreen mode Exit fullscreen mode

5. Delete Task Function

This function deletes a specific task from the DynamoDB table.

Key Parts:

  • Delete Item from DynamoDB: Uses the delete_item method to remove the task based on user_id and task_id.
# Get task_id from path parameters
if 'pathParameters' not in event or 'taskId' not in event['pathParameters']:
    raise ValueError("task_id is missing in path parameters")
task_id = event['pathParameters']['taskId']

# Delete the item from DynamoDB
table.delete_item(
    Key={
        'user_id': user_id,
        'task_id': task_id
    }
)

logger.info(f"Task deleted successfully: {task_id}")
return {
    'statusCode': 200,
    'body': json.dumps({'message': 'Task deleted successfully'})
}
Enter fullscreen mode Exit fullscreen mode

6. Sending Reminders Function

This function sends SMS reminders for tasks due on the current date.

Key Parts:

  • Query DynamoDB for Due Tasks: Queries the UserDueDateIndex for tasks due today.
  • Send SMS Reminders: Uses Twilio's API to send SMS reminders to users.
def send_reminder_sms(phone_number, task_title, due_date):
    ACCOUNT_SID = os.environ['TWILIO_ACCOUNT_SID']
    AUTH_TOKEN = os.environ['TWILIO_AUTH_TOKEN']
    TWILIO_PHONE_NUMBER = os.environ['TWILIO_PHONE_NUMBER']

    client = Client(ACCOUNT_SID, AUTH_TOKEN)

    MESSAGE = f"Reminder: Your task '{task_title}' is due on {due_date}."

    try:
        message = client.messages.create(
            body=MESSAGE,
            from_=TWILIO_PHONE_NUMBER,
            to=phone_number
        )
        logger.info(f"SMS sent! Message SID: {message.sid}")
    except Exception as e:
        logger.error(f"Error sending SMS: {e}")

def lambda_handler(event, context):
    today_date = datetime.utcnow().strftime('%Y-%m-%d')

    try:
        response = table.query(
            IndexName='UserDueDateIndex',
            KeyConditionExpression='due_date = :due_date',
            ExpressionAttributeValues={':due_date': today_date}
        )

        tasks = response.get('Items', [])
        for task in tasks:
            phone_number = task.get('phone_number')
            task_title = task.get('title', "No Title")
            due_date = task.get('due_date', "No Due Date")
            send_reminder_sms(phone_number, task_title, due_date)

        return {
            'statusCode': 200,
            'body': 'SMS reminders sent successfully'
        }
    except Exception as e:
        logger.error(f"Error processing tasks: {e}")
        return {
            'statusCode': 500,
            'body': f'Error processing tasks: {e}'
        }
Enter fullscreen mode Exit fullscreen mode

Here is one of the lambda functions on the console(note: we will integrate the gateway in the coming steps):

Image description

Packaging and Deploying Lambda Functions

After implementing the Lambda functions for your serverless Task Manager API, you need to package them with their dependencies and deploy them to AWS Lambda. Follow these steps:

1. Packaging Lambda Functions

1.1 Prepare Your Environment

Ensure you have Python and pip installed on your local machine.

1.2 Create a Project Directory

Create a directory for each Lambda function. For example:

mkdir lambda_get_tasks
mkdir lambda_get_task_by_id
mkdir lambda_update_task
mkdir lambda_delete_task
mkdir lambda_send_reminders
Enter fullscreen mode Exit fullscreen mode

1.3 Install Dependencies

Navigate to each directory and install the required Python packages.

  • For GetTasks, GetTaskById, UpdateTask, and DeleteTask functions:
  cd lambda_get_tasks
  pip install pyjwt boto3 -t .
Enter fullscreen mode Exit fullscreen mode

For SendReminders Function (which includes twilio):

cd ../lambda_send_reminders
pip install pyjwt boto3 twilio -t .
Enter fullscreen mode Exit fullscreen mode

1.4 Add Your Lambda Function Code

Place your Lambda function code in a file named lambda_function.py within the respective directory.

1.5 Zip Your Lambda Function

Navigate to the directory containing your Lambda function code and dependencies. Create a ZIP file of the contents:

cd lambda_get_tasks
zip -r lambda_function.zip .
Enter fullscreen mode Exit fullscreen mode

Repeat the zipping process for each function directory.

1.6 Upload to AWS Lambda Console

  1. Log in to the AWS Lambda Console.
  2. Create a new Lambda function or select an existing one.
  3. In the function configuration, choose Upload from .zip file and upload the corresponding lambda_function.zip file.

2. Configuring Environment Variables

For the Lambda functions that require environment variables, follow these steps:

2.1 Navigate to Your Lambda Function

  1. In the AWS Lambda Console, select your Lambda function.

2.2 Configure Environment Variables

  1. Go to the Configuration tab.
  2. Select Environment variables.
  3. Click on Edit and add the required environment variables:
  • For the SendReminders function, add:

    • TWILIO_ACCOUNT_SID
    • TWILIO_AUTH_TOKEN
    • TWILIO_PHONE_NUMBER
  • Add any additional environment variables required for your functions, such as:

    • DYNAMODB_TABLE_NAME (for functions that interact with DynamoDB in our case its the Tasks table)

2.3 Save Changes

Setting Up API Gateway for Task Manager

Overview

In this section, we'll walk through the setup of API Gateway for our Task Manager application. This API Gateway is designed to handle CRUD operations for tasks, including creating, retrieving, updating, and deleting tasks, and it integrates with AWS Lambda functions for processing these operations.

Step-by-Step Setup

  • Create API Gateway

    • Navigate to API Gateway: Open the AWS Management Console and navigate to API Gateway.
    • Create a New API: Choose "Create API" and select "REST API" for our use case.
  • Define Resources and Methods

    • Create /tasks Resource
    • GET Method: Connect this method to a Lambda function that retrieves all tasks from the DynamoDB table.
    • POST Method: Connect this method to a Lambda function that creates a new task in DynamoDB.
    • Create /tasks/{taskId} Resource
    • GET Method: Connect this method to a Lambda function that retrieves a specific task by ID from DynamoDB.
    • PUT Method: Connect this method to a Lambda function that updates a specific task by ID in DynamoDB.
    • DELETE Method: Connect this method to a Lambda function that deletes a specific task by ID from DynamoDB.
  • Set Up Authorization

    • Authorization Type: Use Cognito Authorizer to secure the API endpoints.
    • Configure Cognito Authorizer
    • In the API Gateway console, go to "Authorizers" and create a new Cognito Authorizer.
    • Link this authorizer to your API methods to enforce authentication.
  • API Key Settings

    • API Key Required: Set this to "No" as API keys are not required for this API.
  • Request Validation

    • Request Validator: No request validator is applied, meaning no schema validation is enforced for incoming requests.
  • SDK Operation Naming

    • SDK Operation Name: The SDK operation names are generated automatically based on the method and path. This naming helps in integrating the API with client SDKs.

Here is how the API looks like and a sample of a function endpoint:

Image description

Image description

  • Deploy the API

    • Create a Deployment Stage: After configuring the methods and settings, create a deployment stage (e.g., prod) and deploy your API.

Summary

With these steps, you've successfully set up API Gateway to handle CRUD operations for your Task Manager application, connecting each API method to its corresponding Lambda function. The integration with AWS Cognito provides secure access, while the configuration ensures that the API is ready for deployment and testing.

CloudWatch Events for Scheduling Reminders

To ensure that the reminder functionality operates daily and sends SMS reminders for tasks due on the current date, we use AWS CloudWatch Events. This service allows us to create scheduled events that trigger Lambda functions at specified intervals.

  1. Create a CloudWatch Rule
  • Navigate to CloudWatch: Open the AWS Management Console and go to CloudWatch.
  • Create a Rule: Select "Rules" under "Events" and click "Create rule."
  • Define the Schedule: Choose "Event Source" and select "Schedule." Use the cron expression cron(0 6 * * ? *) to run the Lambda function daily at 9 AM in the Africa/Nairobi time zone.

    Cron Expression Breakdown:

    • 0 6 * * ? *: This expression schedules the event to run at 6:00 AM UTC daily. Adjust for your desired time zone if needed.
  • Target: Set the target as the Lambda function responsible for sending reminders. Choose the Lambda function you configured for this purpose.

  1. Configure Permissions
  • IAM Role: Ensure that the Lambda function's execution role has the necessary permissions to interact with DynamoDB, send SMS via Twilio, and log events to CloudWatch Logs.
  • Event Permissions: Confirm that CloudWatch Events can invoke the Lambda function. This is typically set up automatically when configuring the rule.
  1. Save and Test
  • Save the Rule: After configuring the schedule and targets, save the rule.
  • Test the Setup: You can manually trigger the Lambda function to test its functionality or wait for the scheduled time to confirm it operates as expected.

Testing the API with Postman

Overview

In this section, we'll test the Task Manager API using Postman. Before testing, you'll need to obtain an authorization code and exchange it for an access token.

Step 1: Obtain Authorization Code

  1. Get Authorization Code:

    • Visit the following URL to get the authorization code:
     https://<YOUR_COGNITO_DOMAIN>/oauth2/authorize?response_type=code&client_id=<YOUR_CLIENT_ID>&redirect_uri=https://callback&scope=openid+email+phone
    
  • After sending the request, you'll be redirected to a callback URL. Pick the authorization code from the URL. This URL may be a non-existent page, so just get the code from the query parameters.

Image description

Step 2: Get Access Token

  1. Request Access Token:

    • Use the following curl command to exchange the authorization code for an access token:
     curl -X POST 'https://<YOUR_COGNITO_DOMAIN>/oauth2/token' \
     -H 'Content-Type: application/x-www-form-urlencoded' \
     -d 'grant_type=authorization_code&client_id=<YOUR_CLIENT_ID>&code=YOUR_AUTH_CODE&redirect_uri=https://callback'
    
  • Replace YOUR_AUTH_CODE with the code you obtained in the previous step.

Image description

Step 3: Test API Endpoints

  1. Set Up Postman:

    • Open Postman and create a new request.
  2. Add Authorization Header:

    • Use the access token obtained in Step 2 to set up the authorization header for each request:
      • Go to the "Authorization" tab in Postman.
      • Select "Bearer Token" as the type.
      • Paste the access token into the "Token" field.

Image description

Go to your lambda console and get your api enpoints i.e in the configurations on the triggers tab

  1. Test Endpoints:
  • GET /tasks:
    • Send a GET request to https://yy045z23h0.execute-api.us-east-1.amazonaws.com/prod/tasks to get all tasks by the user.
    • Image description

Image description

  • POST /tasks:
    • Send a POST request to https://yy045z23h0.execute-api.us-east-1.amazonaws.com/prod/tasks with the task data in the body.

Image description

  • GET /tasks/{taskId}:

    • Send a GET request to https://yy045z23h0.execute-api.us-east-1.amazonaws.com/prod/tasks/{taskId} to retrieve a specific task.
    • Image description
  • PUT /tasks/{taskId}:

    • Send a PUT request to https://yy045z23h0.execute-api.us-east-1.amazonaws.com/prod/tasks/{taskId} with the updated task data in the body.
    • Image description
  • DELETE /tasks/{taskId}:

    • Send a DELETE request to https://yy045z23h0.execute-api.us-east-1.amazonaws.com/prod/tasks/{taskId} to delete a specific task. - Image description
  • Test Sending Reminders:

    • Since the /send-reminders function is triggered by a scheduled event (cron job), you cannot test it with Postman. Instead, you can test it by invoking the Lambda function manually from the AWS Lambda console or by checking CloudWatch logs.
    • To test, go to the AWS Lambda console, select the Lambda function, and use the "Test" feature to simulate an invocation.
    • Image description

Image description

  • Image description

Scope and Grants

  • Scopes: We are using openid, email, and phone. You may include any other scopes as needed for your application.
  • Grants: We are using both authorization_code and implicit grant types in our AWS Cognito settings.

With these steps, you can effectively test the API endpoints of your Task Manager application using Postman. Ensure that you replace placeholders with your actual values and screenshots.

Conclusion

In this tutorial, we've built and tested a serverless Task Manager application using AWS services and various technologies. Here's a summary of what we've accomplished:

  1. Serverless Architecture Setup:

    • We created a serverless backend using AWS Lambda, API Gateway, and DynamoDB to manage CRUD operations for tasks.
    • We utilized AWS Cognito for authentication, configuring both authorization_code and implicit grant types to handle user sign-ins securely.
  2. API Gateway Configuration:

    • We set up API Gateway to handle various HTTP methods for the /tasks resource, including GET, POST, PUT, and DELETE operations.
    • We connected each API method to its corresponding Lambda function and secured the endpoints using AWS Cognito Authorizer.
    • The API was deployed to a production stage to make it accessible for testing.
  3. Testing the API:

    • We demonstrated how to obtain an authorization code and exchange it for an access token using AWS Cognito.
    • We tested the API endpoints with Postman, including operations to create, retrieve, update, and delete tasks. Each request was configured with the appropriate authorization token.
    • For the /send-reminders functionality, which is triggered by a scheduled event, we tested the Lambda function manually using the AWS Lambda console and reviewed the Lambda logs to ensure proper execution.
  4. Reminder Functionality:

    • We configured a Lambda function to send SMS reminders for tasks due on the current date using Twilio. This function was scheduled to run daily via CloudWatch Events.
    • The testing for the reminder function was handled within the AWS Lambda console, given that it does not expose a direct API endpoint.

The code for all the functions used in this tutorial is accessible on GitHub. Feel free to explore the repository for detailed implementation and configuration.

Stay tuned for more tutorials covering various aspects of backend development and cloud technologies. I'll be sharing insights and practical guides to help you build robust and scalable solutions using modern technologies. Happy coding!

Top comments (0)