Part3: Deploying a Secure Full-Stack Application on AWS EC2 with Docker and Nginx

Part3: Deploying a Secure Full-Stack Application on AWS EC2 with Docker and Nginx

ยท

6 min read

Introduction

In the previous sections, we set up the environment for deployment on AWS EC2 and configured Nginx as a reverse proxy, securing our Application with SSL/TLS. Now, we will integrate our frontend Application with the backend, where these two layers will seamlessly link. This integration is paramount as it makes your Application fully functional, forcing the frontend to communicate with the backend and giving it a cohesive user experience. Your role in this balanced integration directly influences the Application's performance, safety, and usability.

Our Full-Stack Application's frontend always depends on the backend for data entry and reliability. The backend, which you have expertly deployed in AWS EC2 and configured with Nginx for reverse proxy, serves as the backbone of our Application. It allows us to deploy the frontend Application on any server we choose. Your expertise in configuring CORS (Cross-Origin Resource Sharing) in the backend is instrumental in ensuring smooth communication between the frontend and backend, a critical aspect of our Application's functionality.

In this final part of our guides, we will configure our frontend to communicate with the backend, set up the necessary API endpoints, ensure secure communication by enabling Cros in the backend Application, use the new API endpoint in the frontend, and finally deploy our frontend to Vercel by adding the required environment variables before deployment.

Step 1: Configuring the Frontend Application

To configure our frontend Application, we must first understand how it communicates with the backend by viewing the default configuration settings.

Viewing the Frontend Code

When fetching data in a frontend Application, we typically use the fetch function to make a request. However, tools like Axios simplify the workflows of fetching data. Our frontend Application uses apiClient, a standalone function we can call from any part of our Application whenever we want to connect to the backend. Here is a sample code:

import axios from 'axios';


const apiClient = axios.create({
  baseURL: 'http://localhost:8000/api',
});

export default apiClient;

Explanation

  • axios: We import the axios library, meaning you must install it.

  • baseURL: The URL for our API client to communicate with the backend (http://localhost:8000/api).

Updating to Use the Actual URL Endpoint

To ensure our frontend Application communicates effectively with the backend, we need to update the sample code to use our actual URL endpoint and include additional configuration for authenticated requests:

import axios from 'axios';

const apiClient = axios.create({
    baseURL: 'https://app.lokytech.co/api',
});

// Authenticated client
const authApiClient = axios.create({
    baseURL: 'https://app.lokytech.co/api',
});

authApiClient.interceptors.request.use((config) => {
    const token = localStorage.getItem('token');
    if (token) {
        config.headers.Authorization = `Bearer ${token}`;
    }
    return config;
}, (error) => {
    return Promise.reject(error);
});

export { apiClient, authApiClient };

Explanation:

  • We set the base URL for the general and authenticated clients to https://app.lokytech.co/api.

  • We create two Axios instances: apiClient for general requests and authApiClient for authenticated requests.

  • The authApiClient includes an interceptor to add them automatically Authorization header with the token from localStorage before each request.

This setup efficiently handles authenticated and non-authenticated requests, providing a robust solution for interacting with the backend.

Deploying the Frontend Application

Deploying the Application to a hosting provider like Vercel ensures your Application is accessible to users. Follow these detailed steps to deploy:

  1. Using Environment Variables:

    When deploying the Application, it uses environment variables to hide sensitive information, such as the base URL and authorization tokens. Create a .env file at the root of the project directory and add this code. NEXT_PUBLIC_API_URL= "https://app.lokytech.co/api". Here's an example of how you can update the configuration to use environment variables:

     import axios from 'axios';
    
     const API_URL = process.env.NEXT_PUBLIC_API_URL;
    
     const apiClient = axios.create({
         baseURL: `${API_URL}/api`,
     });
    
     // Authenticated client
     const authApiClient = axios.create({
         baseURL: `${API_URL}/api`,
     });
    
     authApiClient.interceptors.request.use((config) => {
         const token = localStorage.getItem('token');
         if (token) {
             config.headers.Authorization = `Bearer ${token}`;
         }
         return config;
     }, (error) => {
         return Promise.reject(error);
     });
    
     export { apiClient, authApiClient };
    
  2. Build the Application: Go to your terminal and run the build command to create a production-ready version of your Application.

     npm run build
    

  3. Deploy to Vercel: To deploy to Vercel, you can use Vercel CLI or the web interface to deploy the built Application. We will use the web interface since we have environment variables that we will use during deployment.

    Gif description

These steps will make your frontend Application live and ready to interact with the backend.

Step 2: Update the backend Application to use CORS

Updating the backend Application to use CORS (Cross-Origin Resource Sharing) ensures proper communication between the frontend and backend. It involves modifying the app.js or index.js files to secure appropriate communication using the correct endpoint.

Updating index configuration files:

To update the index file or app file which holds the configuration settings, install the CORS library in the Application if not already installed using this command:

npm install cors

Then, add the code sample below to ensure proper configuration:

const cors = require('cors');
const express = require('express');
const app = express();

const url = "https://techdoc-iota.vercel.app";

app.use(cors({
    origin: url,
    methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
    allowedHeaders: ['Content-Type', 'Authorization']
}));

Explanation:

  • URL: The deployed frontend URL link.

  • app.use(cors({...})): CORS configuration setup, specifying allowed origins, methods, and headers.

Push Code to Git and Pull in EC2 Server

To ensure proper code updates, both locally and on your EC2 server, follow these steps:

  1. Commit and Push Your Changes to Git:

    Use the following command to add, commit, and push your changes in one step:

     git add . && git commit -m "Update backend to use CORS and configure frontend URL" && git push origin main
    
  2. Pull Code on Your EC2 Server:

    Log in to your EC2 instance using the AWS Console, navigate to your project directory, and pull the latest changes from the repository using the following commands:

     git pull origin main
    
  3. Restart Application in EC2:

    It is appropriate to restart the Application again since we just pulled it from our code repository. Use this command to restart the Application.

     docker-compose up -d --build
    

GIf

Step 3: Testing the Application

After configuring and deploying the frontend and updating the backend for CORS, it's essential to test the integration thoroughly to ensure everything works as expected.

Verifying Backend API

Before testing the integration, verify that the backend API is accessible at the configured domain. Use Postman to test the backend API.

  1. Using Postman: Open Postman and create a new request. Set the request method to GET and enter the API endpoint URL (https://app.lokytech.co/api). Click Send.

    • Explanation: This request should return a response from the server. If everything is correct, you should receive a successful response.

Testing Frontend Integration

After confirming the backend API's accessibility, test the frontend application to ensure it can interact successfully with the backend.

  1. Open the Frontend Application: Go to the URL where your frontend is deployed.

  2. Perform API Calls: Use the frontend Application to perform various actions that require API calls, such as logging in or fetching data.

  3. Check for Errors: Ensure that all API calls succeed and there are no errors in the browser console.

This testing ensures that the frontend and backend are correct and data flows seamlessly.

Conclusion

In this part of the guide, we successfully integrated the frontend Application with the backend, ensuring seamless communication between both layers. This integration is critical for a robust, secure, and scalable Application. Following these steps ensures your Application is well-prepared to handle real-world usage while maintaining high security and performance standards. Thanks for sticking with me on this journey; happy coding

ย