# Project 18: Serverless Blog : Seamless Content Management with AWS Lambda (Python), API Gateway, S3, and DynamoDB (Step by Step Implementation)

### **Introduction:**

The ServerlessBlog project represents a paradigm shift in modern web development, leveraging AWS Lambda (Python), API Gateway, S3, and DynamoDB to create a seamlessly scalable and secure content management system. By adopting a serverless architecture, the project minimizes operational overhead, ensuring cost-efficiency and easy maintenance. This innovative approach enables automatic scaling based on demand, optimizing resource utilization. The use of DynamoDB as a NoSQL database ensures high-performance data storage and retrieval, while API Gateway safeguards communication between clients and serverless functions. Overall, ServerlessBlog addresses the challenges of traditional server-based systems, offering a flexible, low-maintenance, and cost-effective solution for dynamic content management.

### **Technologies Used in This Project:**

1. **AWS Lambda (Python):** Utilized for serverless computing, allowing automatic scaling of functions in response to demand. Python was chosen for its readability, ease of integration, and extensive libraries, enhancing development speed and flexibility.
    
2. **API Gateway:** Serves as a secure and scalable entry point for the backend, facilitating communication between clients and serverless functions. It ensures efficient API management, authentication, and authorization, enhancing the overall project's security and accessibility.
    
3. **Amazon S3:** Employed for scalable and durable object storage, providing a reliable solution for storing and retrieving images. S3's high durability, low latency, and easy integration with other AWS services make it an optimal choice for managing and serving media content.
    
4. **DynamoDB:** Chosen as a NoSQL database to store and retrieve blog data with low-latency and high-throughput. DynamoDB's seamless scalability, low operational overhead, and consistent performance make it suitable for handling dynamic content and metadata in a serverless architecture.
    

### **Prerequisites:**

Before diving into the ServerlessBlog project, ensure you have the following prerequisites in place:

1. **AWS Account:** A valid AWS account is necessary to leverage cloud services like Lambda, API Gateway, S3, and DynamoDB.
    
2. **Domain Name:** Obtain a domain name for your blog, as it will be essential for securing and accessing your ServerlessBlog through a custom web address.
    
3. **Basic HTML, Bootstrap, and jQuery Knowledge:** Familiarity with HTML for structuring web content, Bootstrap for responsive design, and jQuery for enhanced front-end interactivity is recommended for a smoother development experience.
    
4. **Python Skills:** Understanding of Python programming language is crucial as it powers the serverless functions implemented using AWS Lambda.
    

### **Project Overview:**

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1708045014182/201acf2b-8f0a-4179-a2ef-32ad1888c295.png align="center")

### **Project:**

We will be using this project repo: [https://github.com/patelajay745/Serverless-Blog](https://github.com/patelajay745/Serverless-Blog)

`Step-1 : Create Lambda function`

Navigate to the Lambda service on the AWS console and click on "Create function."

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707784140142/79a039fb-9525-4cd0-974e-47fcd13bc3a5.png align="center")

Provide the name "**ManageBlogPostFunction**" and choose "Python 3.8" as the runtime.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707784568162/472e1187-ccb4-41dc-a953-2d3c153b01f9.png align="center")

Click "Create Function" to proceed.

Paste the provided code into the Lambda function editor.

<mark>Ensure to customize the values for "allowed_origin," "s3_name" (which will be created later for hosting the website and images), and "Access-Control-Allow-Origin."</mark>

```plaintext
import json
import boto3
import uuid
import base64
from datetime import datetime
from boto3.dynamodb.conditions import Key

dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('BlogPosts')
s3 = boto3.client('s3')
s3_name = '<ur s3 bucker name>'

def lambda_handler(event, context):
    
    print(event)
    http_method = event['httpMethod']
    
    request_origin = event['headers'].get('origin', '')
    allowed_origin = '<ur domain name>'
    
    print(f"request_origin: {request_origin}")
    
    if request_origin == allowed_origin:

        if http_method == 'GET':
            # Retrieve all blog posts
            
            response = table.scan()
            items = response.get('Items', [])
            
            # Sort the items by createdAt in descending order
            sorted_items = sorted(items, key=lambda x: x['createdAt'], reverse=True)
    
            return {
                'statusCode': 200,
                'headers': generate_cors_headers(),
                'body': json.dumps(sorted_items)
            }
            
        elif http_method == 'POST':
            # Create a new blog post with date and time
            data = json.loads(event['body'])
            post_id = str(uuid.uuid4())
            current_datetime = datetime.now().isoformat()
            
            fileExtension = data['extension']
            
            timestamp = str(int(datetime.now().timestamp())) + fileExtension
            
            decode_content = base64.b64decode(data['image'])
            
            s3_upload = s3.put_object(Bucket=s3_name, Key=timestamp, Body=decode_content)
            
            image_url = f"https://{s3_name}.s3.amazonaws.com/{timestamp}"  # Update with your S3 bucket URL
            
            table.put_item(
                Item={
                    'postId': post_id,
                    'title': data['title'],
                    'content': data['content'],
                    'createdAt': current_datetime,
                    'updatedAt': current_datetime,
                    'imageUrl': image_url
                }
            )
            return {
                'statusCode': 201,
                'headers': generate_cors_headers(),
                'body': json.dumps(f'{post_id} post created successfully!')
            }
            
        elif http_method == 'PUT':
            # Update an existing blog post with date and time
            data = json.loads(event['body'])
            post_id = event['queryStringParameters']['postId']
            if not post_id:
                return {
                    'statusCode': 400,
                    'headers': generate_cors_headers(),
                    'body': json.dumps('Missing postId parameter')
                }
            
            current_datetime = datetime.now().isoformat()
            
            # Check if there's a new image in the request
            if 'newImage' in data:
                # Upload the new image to S3
                file_extension = data['extension']
                timestamp = str(int(datetime.now().timestamp())) + file_extension
                decode_content = base64.b64decode(data['newImage'])
                
                # Delete the associated image from S3
                post = table.get_item(Key={'postId': post_id}).get('Item', {})
                if 'imageUrl' in post:
                    key = post['imageUrl'].split('/')[-1]
                    print(key)
                    s3.delete_object(Bucket=s3_name, Key=key)
                
                s3_upload = s3.put_object(Bucket=s3_name, Key=timestamp, Body=decode_content)
                image_url = f"https://{s3_name}.s3.amazonaws.com/images/{timestamp}"
                
                # Update the post with the new image URL
                table.update_item(
                    Key={'postId': post_id},
                    UpdateExpression='SET title = :title, content = :content, imageUrl = :imageUrl, updatedAt = :updatedAt',
                    ExpressionAttributeValues={
                        ':title': data['title'],
                        ':content': data['content'],
                        ':imageUrl': image_url,
                        ':updatedAt': current_datetime
                    }
                )
            else:
                # Update the post without changing the image
                table.update_item(
                    Key={'postId': post_id},
                    UpdateExpression='SET title = :title, content = :content, updatedAt = :updatedAt',
                    ExpressionAttributeValues={
                        ':title': data['title'],
                        ':content': data['content'],
                        ':updatedAt': current_datetime
                    }
                )
            
            return {
                'statusCode': 200,
                'headers': generate_cors_headers(),
                'body': json.dumps(f'{post_id} post updated successfully!')
            }
        
        elif http_method == 'DELETE':
            post_id = event['queryStringParameters']['postId']
            if not post_id:
                return {
                    'statusCode': 400,
                    'body': json.dumps('Missing postId parameter in the URL path')
                }
            
            # Delete the associated image from S3
            post = table.get_item(Key={'postId': post_id}).get('Item', {})
            if 'imageUrl' in post:
                key = post['imageUrl'].split('/')[-1]
                print(key)
                s3.delete_object(Bucket=s3_name, Key=key)
            
            table.delete_item(
                Key={'postId': post_id}
            )
            return {
                'statusCode': 200,
                'headers': generate_cors_headers(),
                'body': json.dumps(f'{post_id} post deleted successfully!')
            }
        else:
            return {
                'statusCode': 400,
                'headers': generate_cors_headers(),
                'body': json.dumps('Invalid HTTP method')
            }
    else:
        return {
                'statusCode': 400,
                'headers': generate_cors_headers(),
                'body': json.dumps('you are not authorized')
            }

def generate_cors_headers():
    return {
        'Access-Control-Allow-Headers': 'Content-Type',
        'Access-Control-Allow-Origin': 'http://ur domain name',
        'Access-Control-Allow-Methods': 'OPTIONS,GET,POST,PUT,DELETE'
    }
```

Navigate to the Configuration tab, then Permissions.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707785603372/400d20d1-078c-444b-ae2b-6be68b1a539a.png align="center")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707787175381/4c39e8f8-27c9-4872-857d-a29dbe484840.png align="center")

This action will redirect you to IAM. Click on "Add Permission" and proceed to attach policies.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707786001824/c41e6cd7-85cb-4db2-8c6d-a277066510b6.png align="center")

Choose "S3FullAccess" and "DynamoDB" and then click on "Add Permission." Close this window.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707786200681/3f5e2671-837b-41df-b7bb-8d482d14764c.png align="center")

Now, click on "Deploy" as one function is ready for use. We will proceed to create two more functions for usage.

Create Second Lambda function.

Navigate to the Lambda service on the AWS console and click on "Create function."

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707786755506/fd8fc42b-d5e5-450b-9bd2-05905c520916.png align="center")

Provide the name "**RetrieveBlogPostsFunction**" and choose "Python 3.8" as the runtime.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707786870260/7fe6f7d3-a30f-4a66-ad08-81e1f183e433.png align="center")

Click "Create Function" to proceed.

Paste the provided code into the Lambda function editor.

<mark>Ensure to customize the values for "Access-Control-Allow-Origin."</mark>

```plaintext
import json
import boto3

dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('BlogPosts')

def lambda_handler(event, context):
    http_method = event['httpMethod']

    if http_method == 'GET':
        # Retrieve a specific blog post based on postId
        post_id = event['queryStringParameters']['postId']
        if not post_id:
            return {
                'statusCode': 400,
                'headers': generate_cors_headers(),
                'body': json.dumps('Missing postId parameter in the URL path')
            }

        response = table.get_item(Key={'postId': post_id})
        item = response.get('Item', {})
        return {
            'statusCode': 200,
            'headers': generate_cors_headers(),
            'body': json.dumps(item)
        }
    else:
        return {
            'statusCode': 400,
            'headers': generate_cors_headers(),
            'body': json.dumps('Invalid HTTP method')
        }

def generate_cors_headers():
    return {
        'Access-Control-Allow-Headers': 'Content-Type',
        'Access-Control-Allow-Origin': 'http://ur domain name',
        'Access-Control-Allow-Methods': 'OPTIONS,GET'
    }
```

Navigate to the Configuration tab, then Permissions.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707785603372/400d20d1-078c-444b-ae2b-6be68b1a539a.png align="center")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707787228691/c464b16e-d6f8-410a-a773-25c59f5cbf40.png align="center")

This action will redirect you to IAM. Click on "Add Permission" and proceed to attach policies.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707786001824/c41e6cd7-85cb-4db2-8c6d-a277066510b6.png align="center")

Choose "DynamoDB" and then click on "Add Permission." Close this window.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707787826013/f24f4ad8-e0a3-4597-a154-2714f05b64de.png align="center")

Now, click on "Deploy" as this function is ready for use. We will proceed to create last functions for login.

Create Last Lambda function.

Navigate to the Lambda service on the AWS console and click on "Create function."

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707787999234/c6fbf869-51ca-4902-a3bb-24085dc428c1.png align="center")

Provide the name "**Login**" and choose "Python 3.8" as the runtime.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707788252560/afc4e290-d2f7-4c25-a86d-c92a3de53f67.png align="center")

Click "Create Function" to proceed.

Paste the provided code into the Lambda function editor.

<mark>Ensure to customize the values for "Access-Control-Allow-Origin."</mark>

```plaintext
import json
import boto3

def lambda_handler(event, context):
    # Extract username and password from the query parameters
    
    print(event)
    username = event['queryStringParameters']['username']
    password = event['queryStringParameters']['password']

    # Check credentials against DynamoDB table
    is_authenticated = check_credentials(username, password)

    # Return a response indicating authentication status
    response = {
        'isAuthenticated': is_authenticated
    }

    return {
        'statusCode': 200,
        'headers': generate_cors_headers(),
        'body': json.dumps(response)
    }

def check_credentials(username, password):
    # Connect to DynamoDB
    dynamodb = boto3.resource('dynamodb')
    table = dynamodb.Table('login')  # Replace 'login' with your actual table name

    # Scan DynamoDB table for the provided username and password
    response = table.scan(
        FilterExpression='username = :u and password = :p',
        ExpressionAttributeValues={
            ':u': username,
            ':p': password
        }
    )

    # Check if a matching record was found
    return len(response['Items']) > 0
    
    
def generate_cors_headers():
    return {
        'Access-Control-Allow-Headers': 'Content-Type',
        'Access-Control-Allow-Origin': 'http://ur domain name',
        'Access-Control-Allow-Methods': 'OPTIONS,POST'
    }
```

`Step 2 : Create API Gateway.`

Navigate to API Gateway and click on "REST API." Click on "Build."

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707789325164/0acaddeb-50c6-4b7d-8473-3e4e59a82829.png align="center")

Provide a name for the API, such as "ServerlessBlogAPIGateway," and click on "Create API."

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707789525598/425990b8-a57e-4b89-8399-730f3fe85fa5.png align="center")

Then, click on "Create Resource," name it "Login," select "CORS," and click on "Create Resource."

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707789985180/ecbab8c0-3f33-44b5-bf40-602739a838ef.png align="center")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707790174195/72a17959-fd25-48c5-8494-282294272627.png align="center")

Click on "Create method"

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707875742524/50f8940b-1476-4805-911b-96946f606362.png align="center")

Choose "POST" as the Method type and "Lambda Function" as the Integration type. Select "Lambda Proxy Integration" and then choose the Lambda function "Login" from the list. Finally, click on "Create Method."

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707876035023/073337db-8242-466a-a47d-ea33da22c76e.png align="center")

Click on "Create resource"

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707876458243/a6cc97a8-5df7-4b02-a625-0e094a29e019.png align="center")

Provide name to resource "RetrieveBlogDetailsById". Enable "CORS" and click on "Create Resource"

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707876612640/837d8132-4fc7-4084-8671-e858cd129eab.png align="center")

Click on "Create Method" but make sure you create under "RetrieveBlogDetailsById" rersource

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707876755770/7dacea7f-0a4a-4566-8772-3a7120551339.png align="center")

Choose "GET" as the Method type and "Lambda Function" as the Integration type. Select "Lambda Proxy Integration" and then choose the Lambda function "**RetrieveBlogPostsFunction**" from the list. Finally, click on "Create Method."

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707876912709/28e05b70-6062-431d-86e9-cd1c9bf5726b.png align="center")

Click on "Create resource"

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707876458243/a6cc97a8-5df7-4b02-a625-0e094a29e019.png align="center")

Provide name to resource "ManageBlogPost". Enable "CORS" and click on "Create Resource"

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707877575430/3128f75c-75d6-44d7-a617-0d4093c908a4.png align="center")

Click on "Create Method" but make sure you create under "ManageBlogPost" resource

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707877661687/c544877e-5dea-4eed-b0fb-01bfbf830d8c.png align="center")

Choose "GET" as the Method type and "Lambda Function" as the Integration type. Select "Lambda Proxy Integration" and then choose the Lambda function "**ManageBlogPostFunction**" from the list. Finally, click on "Create Method."

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707877781005/052daf11-0575-472e-b0ef-e778c1b4db0b.png align="center")

Let's Create one more method to create blog post . Click on "Create Method" but make sure you create under "ManageBlogPost" resource

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707877661687/c544877e-5dea-4eed-b0fb-01bfbf830d8c.png align="center")

Choose "POST" as the Method type and "Lambda Function" as the Integration type. Select "Lambda Proxy Integration" and then choose the Lambda function "**ManageBlogPostFunction**" from the list. Finally, click on "Create Method."

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707878010662/6a1eb0af-2b92-4e55-b4d1-d774c1b85f77.png align="center")

Let's Create one more method to update blog post . Click on "Create Method" but make sure you create under "ManageBlogPost" resource

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707877661687/c544877e-5dea-4eed-b0fb-01bfbf830d8c.png align="center")

Choose "PUT" as the Method type and "Lambda Function" as the Integration type. Select "Lambda Proxy Integration" and then choose the Lambda function "**ManageBlogPostFunction**" from the list. Finally, click on "Create Method."

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707878155772/059b2341-3581-42a6-94e8-accce875a325.png align="center")

Let's Create one more method to delete blog post . Click on "Create Method" but make sure you create under "ManageBlogPost" resource

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707877661687/c544877e-5dea-4eed-b0fb-01bfbf830d8c.png align="center")

Choose "Delete" as the Method type and "Lambda Function" as the Integration type. Select "Lambda Proxy Integration" and then choose the Lambda function "**ManageBlogPostFunction**" from the list. Finally, click on "Create Method."

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707878300219/c6c5ebe2-b0d6-497a-aed3-3e663181b54b.png align="center")

Finally , your Resource and all method should look like this .

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707878348105/21f04f38-8e19-4862-a95c-199a197c68d1.png align="center")

Click on "Deploy API"

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707878417907/6f77cef6-1775-4ebd-baf2-18b529345a39.png align="center")

Select "New stage"

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707878482390/c6f5f745-8442-48bb-874b-7d6bf0b99384.png align="center")

provide name "dev" and click on "Deploy"

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707878520365/a5dbc12b-3b15-4ad2-978c-6edcc24960bf.png align="center")

Our APIGateway is ready to use.Now it is time to setup DynamoDB.

`Step 3 : Create DynamoDB table.`

Navigate to the "Tables" section in the AWS Management Console under DynamoDB. Click on "Create table"

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707962707032/d7a243b0-65bc-47da-b18b-5d04d216f0ec.png align="center")

Provide name "login and partition key "username" and Click on "Create table"

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707962829203/a31e511f-77e0-4d24-a4a6-57765cecd43d.png align="center")

Let's Create second table. Provide name "BlogPosts" and partition key "postId" and Click on "Create table"

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707963585972/003e0293-76af-465e-8735-84dcd6de6532.png align="center")

We need to add a username and password entry to our login table. Click on the table name, then select "Action," and finally, click on "Create Item."

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707963743429/4848a8af-a68f-4a04-9004-a1e63af2829b.png align="center")

Enter the value of username and create on more attribute with name of "password". provide the value for password. Click on "Create item"

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707963889974/365a646e-8bce-4c14-b020-84b09a3741ed.png align="center")

`Step 3 : Create S3 bucket to host our website.`

Navigate to the AWS S3 console and click on "Create bucket."

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707964546543/e3dfc32b-c526-4db8-ba58-11a032389f1d.png align="center")

Enter the exact domain name as the Bucket name.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707964623168/d707efb9-59eb-45e8-bdb1-83de197ca424.png align="center")

Accessible to the public by deselecting "Block all public access."

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707964808063/25edbf1c-e480-44d5-b004-f9a2e003911e.png align="center")

Click on "Create bucket". Click on the newly created bucket's name. Navigate to "Permissions."

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707965434933/106ac29d-acef-4a55-b2a5-2f9c2437da98.png align="center")

Click on Edit button for Bucket policy.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707965607977/0bb93368-07ac-42bd-978c-79ac6bc63580.png align="center")

write this bucket policy and don't forgot to update resource ARN

```plaintext
{
    "Version": "2012-10-17",
    "Id": "Policy1707666026999",
    "Statement": [
        {
            "Sid": "Stmt1707666025128",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "<Bucket arn>/*"
        }
    ]
}
```

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707965740998/71de96a1-3f2b-4d02-b281-de12052866d5.png align="center")

Click on "save changes". Now you can see your bucket is public.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707965846290/150b44bb-1348-409a-b1a1-013ecd7a113e.png align="center")

Click on "Properties"

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707969025507/425940d8-f928-498f-a905-27f1e6afcfba.png align="center")

Click on "Edit" under Static website hosting"

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707969067022/463613e3-bb00-45a4-9df2-0548649521ce.png align="center")

"Enable" static website hosting. and write "index.html" and click "Save Changes"

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707969099008/7d2ff1a8-7967-407f-9ce1-e5bb508379f6.png align="center")

`Step 4 : Upload website to bucket.`

Before uploading website to s3 , we need to update all the url of api gateway in code.

First get your API gateway URL.

Navigate to API gateway in console. Click on API name. Click on "Stages"

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707966412819/b2e98ed5-29a9-4ba4-a240-31efd22ef0a0.png align="center")

Copy somewhere your Invoke URL, we will use this later.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707966462809/7f475cf0-bb47-4cf2-b3b5-6bfcf75ff70a.png align="center")

Clone this repository. [https://github.com/patelajay745/Serverless-Blog.git](https://github.com/patelajay745/Serverless-Blog.git)

In website folder, open following files,

add-post.html , edit-post.html , index.html . Search for "fetch(" and paste your api gateway Invoke url.

<mark>fetch('&lt;ur invoke url&gt;/ManageBlogPost', {</mark>

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707966634450/c2d6e393-b815-49c2-94c4-22f5ebbb41a0.png align="center")

For edit-post.html

<mark>fetch('&lt;ur invoke url&gt;/ManageBlogPost?postId=${postId}', {</mark>

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707966777956/0772f295-e09a-4e5b-bb48-8173b3e193f6.png align="center")

for index.html

<mark>fetch('&lt;ur invoke url&gt;/ManageBlogPost', {</mark>

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707966875706/9f007bac-c76b-4744-9c1c-42da8e5ef6e9.png align="center")

Now change to show-post.html

<mark>fetch('&lt;ur invoke url&gt;/RetrieveBlogDetailsById?postId=${postId}', {</mark>

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707967100150/5b32ffdb-988f-4352-be2c-23be4fc7218c.png align="center")

Now at last, make 3 changes in admin.html

search for "amazonaws"

<mark>fetch('&lt;ur invoke url&gt;/ManageBlogPost', {</mark>

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707967326184/e55b2320-92e1-4e34-907e-0c1ff8b2f82b.png align="center")

<mark>fetch('&lt;ur invoke url&gt;/ManageBlogPost?postId=${postId}', {</mark>

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707967400926/e4defbf6-f54e-4c4c-9c15-2e2ecf82be6f.png align="center")

### **<mark>&lt;ur invoke url&gt;login?username=</mark>**

Here make sure you enter login instead of <mark>ManageBlogPost</mark>

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707967552763/bd70a67d-5263-468b-8b03-67b01029f8d2.png align="center")

Now Go to S3 bucket . Click on "Upload"

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707967759484/56c108a8-dce7-4a54-9dae-0f0afb6974c1.png align="center")

To upload all files you need to click on "Add files" and for upload "Asset" folder you need to click "Add folder"

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707967905960/7e3cf29f-fb06-4a7f-80fb-b2eb2aca7f48.png align="center")

make sure you upload assets/ folder otherwise our website will look messy without css.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707968141562/66102816-ac93-4401-b6e2-5e4c2a3d1008.png align="center")

Feel free to test your index.html; however, please note that it won't display any posts due to our CORS restrictions, limited to our domain URL. Nonetheless, you can preview the appearance of our website.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707968305105/efb0946c-010f-4567-b3fc-fd6a56efd710.png align="center")

`Step 5 : Setup DNS for domain in Route53`

Navigate to Route 53 and Click on your domain name.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707968676258/9bf33848-b5f9-43ce-a120-cf3567d6ccc9.png align="center")

Click on "Create record"

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707968753478/f7615158-b4ba-42fa-bd11-e24af0a135d7.png align="center")

Write "www" if you are not using sub domain. I am using subdomain so I am writing subdomain name.

Select "A Record" as Record type.

Select "Alias".

Choose Endpoint to "Alias to 53 website endpoint"

Choose region to "Region of S3 bucket"

Your s3 Endpoint will pop up.

Click on "Create Records"

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707969351096/455b95a2-f55e-4e8f-bfe4-b58885913a4f.png align="center")

Make sure you entered right allowed origin name to your domain name in All Lambda function.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707970207144/d594dbeb-ce9b-4b15-8c00-bd54c30b74ea.png align="center")

Also Access-Control-Allow-Origin to your domain name. It will block your API access to your domain name only.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707970217883/3d8d5f57-35a3-4bce-a95b-eab79d2f2b0b.png align="center")

Now access your Serverless blog by visiting your domain name.

Now you can create new blog by clicking Adminpanel .

Default username is admin and password is admin.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707971047645/e2cf451c-a3e0-4427-a234-dda127ffb0fc.png align="center")

Great! Your Serverless blog is now set up!

### <mark>For added security, we have configured the Lambda function to match the origin, ensuring that only requests from our specified domain can access the API Gateway.</mark>

### **Conclusion 🚀:**

In this ServerlessBlog project, we've harnessed the power of AWS Lambda, API Gateway, S3, and DynamoDB to create a dynamic and scalable content management system. 💻 Leveraging Python in Lambda functions ensures readability and flexibility, while DynamoDB and S3 provide efficient data storage and retrieval. 🗄️ API Gateway acts as the gateway, ensuring secure communication between clients and serverless functions. 🌐

Following the step-by-step guide, we've set up the entire infrastructure, including DynamoDB tables, S3 buckets, and API Gateway, ensuring seamless deployment and management. 🛠️ **By incorporating CORS and tailored permissions, we've enhanced security.** 🛡️ The provided website, hosted on S3, demonstrates the real-time capabilities of our ServerlessBlog.

**Stay tuned for more as we explore further advancements, including SAM integration and CI/CD implementation!** 🌈✨ The journey continues towards a fully optimized and robust serverless architecture. 🚀🌐🔒
