Take IT Shipping with Serverless Technology
Tags: aws, serverless, dynamodb, lambda
Published: 2024-03-21 12:35:59

In this article, I'll take you through the journey of creating a mobile application utilizing serverless architecture. 

The app idea is a platform that facilitates and supports peer-to-peer shipping services, connecting requesters and travelers.

You can [download](https://play.google.com/store/apps/details?id=com.gotakeit.take_it) the app on Android. 

![Architecture](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iby0sg0k53m1ah4mw6md.jpeg)

The App Architecture:

**Frontend Framework (Flutter)**:

- Flutter serves as the frontend framework for developing the mobile application.

- Flutter allows for cross-platform development, enabling the app to run on both iOS and Android devices from a single codebase.

**Backend Services (Lambda)**:

The backend service is built using Lambda, a serverless computing platform.
Lambda functions are deployed in response to events triggered by user actions, ensuring scalability and cost-efficiency.
"The Take IT" app utilizes Python Flask within the Lambda functions


**Authentication (Cognito):**

Cognito, a serverless authentication service, manages the sign-up/sign-in process in the Take IT app. Additionally, it seamlessly integrates with identity providers such as Google or Facebook. Authentication is typically managed using JWT (JSON Web Tokens) for secure and efficient user authentication.


**API Gateway:**

Integrating AWS API Gateway with Cognito offers enhanced security and scalability for the Take IT app. 
API Gateway ensures only authenticated users can access backend services. 

**Database (DynamoDB)**:

DynamoDB serves as the serverless database utilized to store all data in the Take IT app. It is leveraged to manage user preferences, trip details, contacts, as well as the status of sent and received requests (including accepted, pending, and declined requests).


**Adding a Trip**

![Adding a Trip](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v4275tv58eujw2jk12ul.png)


The function looks like this:
```
def add_trip(item):
    return query_table.put_item(Item=item)
```
Below is an example of how a trip is saved into DynamoDB in JSON format:

```
{
 "username": "4fb1dce7-6a50-41a8-8d7d",
 "created": "2024-10-04-19-34-18-736061",
 "acceptfrom": "2024-10-05",
 "acceptto": "2024-10-30",
 "allowed": {
  "Clothes": {
   "cost": "3.0",
   "kg": "10.0"
  },
  "Electronics": {
   "cost": "20.0",
   "kg": "5.0"
  }
 },
 "currency": "EUR",
 "fromcity": "Berlin-Germany",
 "fromto": "Berlin-Germany_Cairo-Egypt",
 "tocity": "Cairo-Egypt",
 "trdate": "2024-10-31",
 "tstamp": 1732961762
}

```

By utilizing the TTL (Time to Live) feature in DynamoDB, you can automatically delete records after a specified time period. For instance, in the trip record, there is an attribute called "tstamp" that determines the deletion time of the record.

**Find available trips**

The attributes in the trip record, such as "fromCity," "toCity," or "fromTo," are utilized for search functionality when users seek trips. I employ a global secondary index in DynamoDB to retrieve trips based on the originating city, destination city, or the combination of both.


![Find Trips](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kyrbttp7jpsmgpgpwcub.png)



The function looks like this:
- Index: This refers to the name of the global secondary index.
- Key: This represents the DynamoDB attribute used for indexing, typically referring to "fromCity" or "toCity."
- City: Denotes the name of the city being referenced.
- Limit: Specifies the maximum number of records to retrieve.
- Today_date: This indicates the current date, used to filter and display only trips available from today onwards. 
- LastKey: Utilized for pagination purposes, facilitating the retrieval of subsequent sets of records beyond the initial limit.

```
def get_global_index(index,key,city,limit,today_date,lastkey=None):

    if lastkey:
        return query_table.query(
        IndexName=index,KeyConditionExpression=Key(
            key).eq(city),Limit=int(limit),FilterExpression=Attr('acceptto').gte(today_date),ExclusiveStartKey=json.loads(lastkey))

    return query_table.query(
        IndexName=index,KeyConditionExpression=Key(
            key).eq(city),Limit=int(limit),FilterExpression=Attr('acceptto').gte(today_date))

```


**Users Sent/Received Requests**
DynamoDB supports querying data with a key that begins with a specific value. When users send or receive requests in the Take IT app, these requests are stored in DynamoDB with statuses such as "pending," "accepted," "request," or "declined." This allows for efficient querying and retrieval of requests based on their status, enabling seamless management and tracking of request statuses within the application.


The function looks like this:
- item: This refers to "pending," "accepted," "request," or "declined." 
```
query_table.query(KeyConditionExpression=Key("username").eq(username) & Key("created").begins_with(item))
```

Here's an example of how a user request is saved into DynamoDB in JSON format:
```
{
 "username": "user2-466b-a4b9-94f90",
 "created":  "request_2022-10-15-22-35-18-213147_user1-4fb1dce7-6a50",
 "dtime": "2022-10-15-22-42-41-599518",
 "tripid": "2022-10-15-22-35-18-213147",
 "tstamp": 1669766400
},
{
 "username": "user1-4fb1dce7-6a50",
 "created": "pending_2022-10-15-22-35-18-213147_user2-466b-a4b9-94f90",
 "dtime": "2022-10-15-22-42-41-599518",
 "tripid": "2022-10-15-22-35-18-213147",
 "tstamp": 1669766400
}

```


**Remove Account**
When a user decides to delete their account in the Take IT app, several steps are initiated:

Querying User Data: All data associated with the user is queried from DynamoDB, including trip history, pending requests, and any other relevant information.

Deleting User Data: Each record associated with the user's account is deleted from DynamoDB.
This includes trip records, request records (both sent and received), and any other user-specific data stored in the database.

Removing User from Cognito: The user is removed from the Cognito user pool, deleting their account and associated authentication tokens.

The Cognito delete function looks like this:
```
cognito = boto3.client('cognito-idp',region_name = region_name, verify=True)
cognito.admin_delete_user(UserPoolId= userpoolid, Username= username)

```
The delete function looks like this:
```
def delete_records(username):
   items= query_table.query(
       KeyConditionExpression=Key("username").eq(username)
   )
   for i in range(len(items['Items'])):
       query_table.delete_item(
       Key={
           'username': username,
           'created' : items['Items'][i]['created']
           }
           )
   return True
```