Logo

dev-resources.site

for different kinds of informations.

Full Stack Application Hosting in AWS

Published at
12/30/2024
Categories
aws
node
s3
cloudfron
Author
randiakm
Categories
4 categories in total
aws
open
node
open
s3
open
cloudfron
open
Author
8 person written this
randiakm
open
Full Stack Application Hosting in AWS

Todo Application Documentation

Table of Contents

  1. Introduction
  2. Architecture Overview
  3. Backend Setup
  4. Frontend Setup
  5. AWS Configuration
  6. Deployment
  7. Troubleshooting
  8. Maintenance and Updates

1. Introduction

This document provides a comprehensive guide for setting up, deploying, and maintaining a full-stack Todo application using Node.js, React, Express, and MongoDB. The application is deployed on AWS, utilizing services such as Lambda, API Gateway, S3, and CloudFront.

2. Architecture Overview

  • Backend: Node.js with Express, deployed as an AWS Lambda function
  • Frontend: React, built with Vite, hosted on S3 and served via CloudFront
  • Database: MongoDB Atlas
  • API: AWS API Gateway
  • Authentication: (To be implemented)

3. Backend Setup

3.1 Lambda Function

Create a new file named index.js:

const mongoose = require('mongoose');

let cachedDb = null;

async function connectToDatabase() {
  if (cachedDb) {
    return cachedDb;
  }

  const connection = await mongoose.connect(process.env.MONGODB_URI, {
    useNewUrlParser: true,
    useUnifiedTopology: true,
  });

  cachedDb = connection;
  return connection;
}

const todoSchema = new mongoose.Schema({
  title: { type: String, required: true },
  completed: { type: Boolean, default: false },
  createdAt: { type: Date, default: Date.now },
  updatedAt: { type: Date, default: Date.now },
});

const Todo = mongoose.model('Todo', todoSchema);

exports.handler = async (event) => {
  const corsHeaders = {
    'Access-Control-Allow-Origin': 'https://your-cloudfront-domain.cloudfront.net',
    'Access-Control-Allow-Headers': 'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token',
    'Access-Control-Allow-Methods': 'GET,POST,PUT,DELETE,OPTIONS',
    'Access-Control-Allow-Credentials': 'true'
  };

  if (event.httpMethod === 'OPTIONS') {
    return {
      statusCode: 200,
      headers: corsHeaders,
      body: JSON.stringify({ message: 'CORS preflight request successful' })
    };
  }

  try {
    await connectToDatabase();

    const { httpMethod, resource, pathParameters, body } = event;

    switch (`\${httpMethod} \${resource}`) {
      case 'GET /todos':
        const todos = await Todo.find().sort({ createdAt: -1 });
        return { 
          statusCode: 200, 
          headers: { ...corsHeaders, 'Content-Type': 'application/json' },
          body: JSON.stringify(todos) 
        };

      case 'POST /todos':
        const newTodo = new Todo(JSON.parse(body));
        const savedTodo = await newTodo.save();
        return { 
          statusCode: 201, 
          headers: { ...corsHeaders, 'Content-Type': 'application/json' },
          body: JSON.stringify(savedTodo) 
        };

      case 'PUT /todos/{id}':
        const updatedTodo = await Todo.findByIdAndUpdate(
          pathParameters.id,
          { ...JSON.parse(body), updatedAt: Date.now() },
          { new: true }
        );
        if (!updatedTodo) {
          return { 
            statusCode: 404, 
            headers: { ...corsHeaders, 'Content-Type': 'application/json' },
            body: JSON.stringify({ message: 'Todo not found' }) 
          };
        }
        return { 
          statusCode: 200, 
          headers: { ...corsHeaders, 'Content-Type': 'application/json' },
          body: JSON.stringify(updatedTodo) 
        };

      case 'DELETE /todos/{id}':
        const deletedTodo = await Todo.findByIdAndDelete(pathParameters.id);
        if (!deletedTodo) {
          return { 
            statusCode: 404, 
            headers: { ...corsHeaders, 'Content-Type': 'application/json' },
            body: JSON.stringify({ message: 'Todo not found' }) 
          };
        }
        return { 
          statusCode: 200, 
          headers: { ...corsHeaders, 'Content-Type': 'application/json' },
          body: JSON.stringify({ message: 'Todo deleted successfully' }) 
        };

      default:
        return { 
          statusCode: 400, 
          headers: { ...corsHeaders, 'Content-Type': 'application/json' },
          body: JSON.stringify({ message: 'Invalid request' }) 
        };
    }
  } catch (error) {
    console.error('Error:', error);
    return { 
      statusCode: 500, 
      headers: { ...corsHeaders, 'Content-Type': 'application/json' },
      body: JSON.stringify({ message: 'Internal server error' }) 
    };
  }
};
Enter fullscreen mode Exit fullscreen mode

3.2 Package Configuration

Create a package.json file:

{
  "name": "todo-api-lambda",
  "version": "1.0.0",
  "description": "Todo API Lambda Function",
  "main": "index.js",
  "dependencies": {
    "mongoose": "^6.0.0"
  }
}
Enter fullscreen mode Exit fullscreen mode

3.3 Deployment Package

  1. Install dependencies: npm install
  2. Create a ZIP file: zip -r function.zip index.js node_modules

4. Frontend Setup

4.1 Create React App

  1. Create a new Vite project: npm create vite@latest client -- --template react
  2. Navigate to the project directory: cd client
  3. Install dependencies: npm install

4.2 API Service

Create a file named src/services/todoService.js:

import axios from 'axios';

const API_URL = 'https://your-api-gateway-url.execute-api.us-west-2.amazonaws.com/prod/todos';

const api = axios.create({
  baseURL: API_URL,
  withCredentials: true
});

export const getTodos = async () => {
  const response = await api.get('');
  return response.data;
};

export const createTodo = async (title) => {
  const response = await api.post('', { title });
  return response.data;
};

export const updateTodo = async (id, updates) => {
  const response = await api.put(`/\${id}`, updates);
  return response.data;
};

export const deleteTodo = async (id) => {
  await api.delete(`/\${id}`);
};
Enter fullscreen mode Exit fullscreen mode

4.3 Main App Component

Update src/App.jsx:

import React, { useState, useEffect } from 'react';
import { getTodos, createTodo, updateTodo, deleteTodo } from './services/todoService';
import './App.css';

function App() {
  const [todos, setTodos] = useState([]);
  const [newTodo, setNewTodo] = useState('');

  useEffect(() => {
    fetchTodos();
  }, []);

  const fetchTodos = async () => {
    const fetchedTodos = await getTodos();
    setTodos(fetchedTodos);
  };

  const handleCreateTodo = async (e) => {
    e.preventDefault();
    if (newTodo.trim()) {
      const createdTodo = await createTodo(newTodo);
      setTodos([createdTodo, ...todos]);
      setNewTodo('');
    }
  };

  const handleUpdateTodo = async (id, updates) => {
    const updatedTodo = await updateTodo(id, updates);
    setTodos(todos.map(todo => todo._id === id ? updatedTodo : todo));
  };

  const handleDeleteTodo = async (id) => {
    await deleteTodo(id);
    setTodos(todos.filter(todo => todo._id !== id));
  };

  return (
    <div className="App">
      <h1>Todo App</h1>
      <form onSubmit={handleCreateTodo}>
        <input
          type="text"
          value={newTodo}
          onChange={(e) => setNewTodo(e.target.value)}
          placeholder="Add a new todo"
        />
        <button type="submit">Add Todo</button>
      </form>
      <ul>
        {todos.map(todo => (
          <li key={todo._id}>
            <input
              type="checkbox"
              checked={todo.completed}
              onChange={() => handleUpdateTodo(todo._id, { completed: !todo.completed })}
            />
            <span style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}>
              {todo.title}
            </span>
            <button onClick={() => handleDeleteTodo(todo._id)}>Delete</button>
          </li>
        ))}
      </ul>
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

5. AWS Configuration

5.1 Lambda Function

  1. Create a new Lambda function in the AWS Console.
  2. Upload the ZIP file created in step 3.3.
  3. Set the handler to index.handler.
  4. Add environment variable: MONGODB_URI with your MongoDB connection string.

5.2 API Gateway

  1. Create a new API in API Gateway.
  2. Create resources and methods for /todos and /todos/{id}.
  3. Integrate each method with your Lambda function.
  4. Enable CORS for each method.
  5. Deploy the API to a new stage (e.g., "prod").

5.3 S3 and CloudFront

  1. Create an S3 bucket for hosting the React app.
  2. Create a CloudFront distribution with the S3 bucket as the origin.
  3. Set up Origin Access Control (OAC) for secure access to the S3 bucket.

6. Deployment

6.1 Backend Deployment

Update the Lambda function code:

zip -r function.zip index.js node_modules
aws lambda update-function-code --function-name YourFunctionName --zip-file fileb://function.zip
Enter fullscreen mode Exit fullscreen mode

6.2 Frontend Deployment

  1. Build the React app: npm run build
  2. Upload to S3:
   aws s3 sync build/ s3://your-bucket-name --delete
Enter fullscreen mode Exit fullscreen mode
  1. Invalidate CloudFront cache:
   aws cloudfront create-invalidation --distribution-id YourDistributionID --paths "/*"
Enter fullscreen mode Exit fullscreen mode

7. Troubleshooting

7.1 CORS Issues

If encountering CORS errors:

  1. Ensure CORS is enabled in API Gateway for all methods.
  2. Verify CORS headers in the Lambda function response.
  3. Check that the Access-Control-Allow-Origin header matches your CloudFront domain.

7.2 API Gateway 5XX Errors

  1. Check Lambda function logs in CloudWatch.
  2. Verify that the Lambda function has the correct permissions to access other AWS services.

7.3 MongoDB Connection Issues

  1. Ensure the MONGODB_URI environment variable is set correctly in Lambda.
  2. Verify that the Lambda function has network access to MongoDB Atlas (may require VPC configuration).

8. Maintenance and Updates

8.1 Updating the Backend

  1. Make changes to the Lambda function code.
  2. Redeploy using the steps in section 6.1.

8.2 Updating the Frontend

  1. Make changes to the React application.
  2. Rebuild and redeploy using the steps in section 6.2.

8.3 Monitoring

  1. Use CloudWatch to monitor Lambda function performance and errors.
  2. Set up CloudWatch Alarms for critical metrics.

8.4 Scaling

  1. Adjust Lambda function memory and timeout settings as needed.
  2. Consider implementing caching at the API Gateway level for frequently accessed data.

This documentation provides a comprehensive guide for setting up, deploying, and maintaining your Todo application. Remember to keep your dependencies updated and regularly review AWS best practices for potential improvements to your architecture.

s3 Article's
30 articles in total
Favicon
Building a Weather Data Collection System with AWS S3 and OpenWeather API
Favicon
Comprehensive Guide to Installing AWS CLI, Configuring It, and Downloading S3 Buckets Locally
Favicon
Stream de Arquivo PDF ou Imagem S3 - AWS
Favicon
Efficiently Deleting Millions of Objects in Amazon S3 Using Lifecycle Policy
Favicon
Uploading Files to Amazon S3 in ASP.NET Core with Razor Pages
Favicon
AWS S3 Presigned URLs: Secure and Temporary File Access Made Simple
Favicon
How to implement File uploads in Nodejs: A step by step guide
Favicon
Lee esto antes de implementar S3 y CloudFront usando Terraform.
Favicon
Lee esto antes de implementar S3 y CloudFront usando Terraform.
Favicon
🚀 1. Efficient Video Uploads to AWS S3 with React
Favicon
Full Stack Application Hosting in AWS
Favicon
Building an S3 Static Website with CloudFront Using Terraform
Favicon
Configure IRSA using EKS to access S3 from a POD in terraform
Favicon
Setting up IAM Anywhere using terraform
Favicon
AWS S3 System Design Concepts
Favicon
Creating an S3 Bucket in AWS and generate a pre - signed URL
Favicon
Switching to the Terraform S3 Backend with Native State File Locks
Favicon
Around the World in 15 Buckets
Favicon
My (non-AI) AWS re:Invent 24 picks
Favicon
How to Simulate AWS S3 on Your Local Machine with LocalStack
Favicon
Building Websites with Cursor and AWS.
Favicon
Configuring AWS WAF, CloudFront, and S3 Bucket for Secure Access
Favicon
Buckets? No, S3 buckets
Favicon
Download Video from s3 with Cloudfront, nodejs and react
Favicon
AWS Quick Guide - Amazon S3
Favicon
Fastest and Cheapest Ways to Delete Millions of Files from Amazon S3
Favicon
Using MinIO Server for Local Development: A Smarter Alternative to S3
Favicon
AWS CloudFront vs S3 Cross-Region Replication
Favicon
Comparison of S3 upload feature between Documenso and aws-s3-image-upload example
Favicon
Securing Your AWS EC2 and S3 Communication: Best Practices for Enhanced Security

Featured ones: