Integrating Vue.js with Serverless Order Processing in GCP

Integrating Vue.js with Serverless Order Processing in GCP

Integrating Vue.js with Serverless Order Processing System

Introduction

In the previous tutorial, we developed a serverless order processing system on Google Cloud using API Gateway, Cloud Functions, Firestore, and Cloud Tasks. This backend system handles order placement, background processing, and logging.

In this tutorial, we’ll extend our system by integrating a client user interface (UI) using Vue.js. Vue.js is a progressive JavaScript framework for building user interfaces and single-page applications. We will focus on:

  • Setting up a Vue.js project.
  • Building components for order creation and displaying order status.
  • Integrating Firebase Cloud Messaging (FCM) for real-time push notifications.

Setting Up Firebase Cloud Messaging (FCM)

To begin with Firebase Cloud Messaging (FCM), we first need to create a Firebase project. This project will serve as the foundation for integrating FCM into our web application.

Go to the Firebase Console and log in with your Google account. If you haven’t created a Firebase project yet, you’ll need to start by setting up a new Firebase project:

  1. Click on the “Create a project” button or select an existing project from the Firebase Console dashboard.
  2. Follow the prompts to give your project a name. Optionally, you can enable Google Analytics for your project, which provides insights into user engagement and app usage.
  3. After creating the Firebase project, click on the project name to enter the project dashboard.
  4. Click on the “Add App” and then click on the “Web” icon () to add Firebase to your web app.
  5. Provide a nickname for your app (e.g., “MyOrderApp”) and click “Register app”.

Once registered, Firebase will provide you with a configuration object containing API keys and other identifiers essential for connecting your web app to Firebase services. This configuration looks something like this:

// Import the functions you need from the SDKs you need
import { initializeApp } from "firebase/app";
// TODO: Add SDKs for Firebase products that you want to use
// https://firebase.google.com/docs/web/setup#available-libraries

// Your web app's Firebase configuration
const firebaseConfig = {
  apiKey: "YOUR_API_KEY",
  authDomain: "orderprocessingsystem.firebaseapp.com",
  projectId: "orderprocessingsystem",
  storageBucket: "orderprocessingsystem.appspot.com",
  messagingSenderId: "244205369552",
  appId: "1:244205369552:web:73c966048159c84bdb06d5"
};

// Initialize Firebase
const app = initializeApp(firebaseConfig);

Copy this configuration as you’ll need it when integrating Firebase into your Vue.js application.

In the Firebase Console, click on “Settings” (the gear icon) next to “Project Overview”. Select “Project settings”, then navigate to the “Cloud Messaging” tab.

Screenshot-2024-06-19-at-7.57.58 PM-1024x633 Integrating Vue.js with Serverless Order Processing in GCP

Under “Web configuration”, if you haven’t already set up credentials, Firebase will guide you through generating a new key pair (VAPID key). This key pair is necessary for secure communication and sending push notifications to your web application.

Setting Up Vue.js Project

To begin, we need to set up a Vue.js project and integrate Firebase for handling notifications. Firstly, if Vue CLI is not already installed, we’ll need to do so using npm (Node Package Manager):

npm install -g @vue/cli

Next, create a new Vue project named order-processing-system:

vue create order-processing-system

Follow the prompts to select features and configure your Vue project according to your requirements. After setting up the Vue project, we need to add Firebase SDK to enable Firebase services:

cd order-processing-system
npm install firebase

To connect our Vue.js project to Firebase, we create a firebaseConfig object containing essential credentials and settings. This configuration initializes Firebase and configures Firebase Messaging (firebase.messaging()), allowing us to handle notifications.

import firebase from "firebase/compat/app";
import "firebase/compat/messaging";

const firebaseConfig = {
  apiKey: "AIzaSyB4AizQm5pmnP1GozUJqHlKE3Zp5H9MfpI",
  authDomain: "orderprocessingsystem.firebaseapp.com",
  projectId: "orderprocessingsystem",
  storageBucket: "orderprocessingsystem.appspot.com",
  messagingSenderId: "244205369552",
  appId: "1:244205369552:web:73c966048159c84bdb06d5"
};

// Initialize Firebase
firebase.initializeApp(firebaseConfig);
const messaging = firebase.messaging();

export { messaging };

By exporting messaging, we make it accessible throughout our Vue.js application, enabling us to send notifications using Firebase Cloud Messaging (FCM) when specific events, such as order processing, occur.

Building Vue.js Components

Next, we will create two Vue components: OrderForm.vue for order creation and OrderStatus.vue for displaying the order status.

Creating the OrderForm Component

This component provides a form for users to place orders. When the form is submitted, it sends the order data to a specified API endpoint.

<!-- src/components/OrderFORM.vue -->
<template>
  <div>
    <form @submit.prevent="placeOrder">
      <label for="order_id">Order ID:</label>
      <input type="text" id="order_id" v-model="order.order_id" required><br><br>

      <label for="product">Product:</label>
      <input type="text" id="product" v-model="order.product" required><br><br>

      <label for="quantity">Quantity:</label>
      <input type="number" id="quantity" v-model.number="order.quantity" required><br><br>

      <label for="customer_name">Customer Name:</label>
      <input type="text" id="customer_name" v-model="order.customer_name" required><br><br>

      <label for="customer_email">Customer Email:</label>
      <input type="email" id="customer_email" v-model="order.customer_email" required><br><br>

      <label for="status">Status:</label>
      <select id="status" v-model="order.status" required>
        <option value="Pending">Pending</option>
        <option value="Processing">Processing</option>
        <option value="Completed">Completed</option>
      </select><br><br>

      <button type="submit">Place Order</button>
    </form>
  </div>
</template>

<script>
export default {
  data() {
    return {
      order: {
        order_id: '',
        product: '',
        quantity: null,
        customer_name: '',
        customer_email: '',
        status: 'Pending'
      }
    };
  },
  methods: {
    async placeOrder() {
      try {
        fetch('https://asia-southeast1-orderprocessingsystem.cloudfunctions.net/createOrder', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json'
          },
          body: JSON.stringify(this.order)
        })
        .then(response => {
          return response.json();
        })
        .then(data => {
          console.log("Order placed:", data);
        })
        .catch(error => {
          console.error('Error placing order:', error);
        });
      } catch (error) {
        console.error('Error placing order:', error);
        alert('Failed to place order. Please try again.');
      }
    }
  }
};
</script>

Creating the OrderStatus Component

This component listens for FCM notifications and updates the order status accordingly.

<!-- src/components/OrderStatus.vue -->
<template>
  <div>
    <h2>Order Status</h2>
    <p v-if="loading">Loading order status...</p>
    <p v-else>{{ status }}</p>
  </div>
</template>

<script>
import { messaging } from '../firebase'; 

export default {
  data() {
    return {
      status: '',
      loading: true
    };
  },
  created() {
    // Example: Subscribe to Firebase Cloud Messaging (FCM) notifications
    messaging.onMessage(payload => {
      console.log('FCM Message received:', payload);
      this.loading = false;
      this.status = `Order ${payload.data.orderId} processed`;
    });
  }
};
</script>

Using Components in the Main App

We will now integrate the OrderForm and OrderStatus components into our main application component, App.vue.

<!-- src/App.vue -->
<template>
  <div id="app">
    <h1>Order Processing System</h1>
    <OrderForm />
    <OrderStatus />
  </div>
</template>

<script>
import OrderForm from './components/OrderForm.vue';
import OrderStatus from './components/OrderStatus.vue';

export default {
  components: {
    OrderForm,
    OrderStatus
  }
};
</script>

Firebase Cloud Messaging (FCM) Integration

To complete the FCM integration, we need to handle incoming messages using a service worker. We’ll create a service worker file and register it in our Vue.js project.

Create the Service Worker File

First, create a service worker file named firebase-messaging-sw.js in the public directory of your Vue project. This file is responsible for handling incoming FCM messages, especially when the app is not in the foreground. This ensures that users receive notifications even when they are not actively using the app.

// public/firebase-messaging-sw.js
importScripts('https://www.gstatic.com/firebasejs/10.1.0/firebase-app-compat.js')
importScripts('https://www.gstatic.com/firebasejs/10.1.0/firebase-messaging-compat.js')

const firebaseConfig = {
  apiKey: "AIzaSyB4AizQm5pmnP1GozUJqHlKE3Zp5H9MfpI",
  authDomain: "orderprocessingsystem.firebaseapp.com",
  projectId: "orderprocessingsystem",
  messagingSenderId: "244205369552",
  appId: "1:244205369552:web:73c966048159c84bdb06d5"
};

firebase.initializeApp(firebaseConfig);
const messaging = firebase.messaging();

messaging.onBackgroundMessage((payload) => {
  console.log('FCM Background Message received:', payload);
  const notificationTitle = payload.notification.title;
  const notificationOptions = {
    body: payload.notification.body,
    icon: payload.notification.icon
  };

  return self.registration.showNotification(notificationTitle, notificationOptions);
});

We handle background messages with messaging.onBackgroundMessage, displaying a notification when an FCM message is received while the app is in the background.

Register the Service Worker

Next, we need to register this service worker in our main application file, typically main.js or index.js. This service worker listens for background messages and displays notifications using the self.registration.showNotification method. This improves user engagement by ensuring they are notified of important updates.

// src/main.js
import { createApp } from 'vue';
import App from './App.vue';
import { messaging } from './firebase';

navigator.serviceWorker.register('/firebase-messaging-sw.js')
  .then((registration) => {
    console.log('Service Worker registered:', registration);
    console.log(messaging)
    messaging.getToken({ vapidKey: 'YOUR_PUBLIC_VAPID_KEY' })
      .then((currentToken) => {
        if (currentToken) {
          console.log('Firebase Token:', currentToken);
          // Send the token to your server for storing and sending notifications
          fetch('https://asia-southeast1-orderprocessingsystem.cloudfunctions.net/storeToken', {
            method: 'POST',
            headers: {
              'Content-Type': 'application/json'
            },
            body: JSON.stringify({
              token: currentToken,
              user_id: 'USER_ID' // Replace with actual user ID or identifier
            })
          })
          .then(response => {
            console.log(response);
            if (!response.ok) {
              throw new Error('Failed to store token on server');
            }
            return response.json();
          })
          .then(data => {
            console.log('Token stored successfully:', data);
          })
          .catch(error => {
            console.error('Error storing token:', error);
          });

        } else {
          console.log('No registration token available. Request permission to generate one.');
        }
      }).catch((err) => {
        console.log('An error occurred while retrieving token. ', err);
      });
  }).catch((err) => {
    console.error('Service Worker registration failed:', err);
  });

createApp(App).mount('#app');

The VAPID key ('YOUR_PUBLIC_VAPID_KEY') is used to authorize requests to FCM.

When the service worker is registered, we obtain the FCM token using messaging.getToken. We send this token to our backend endpoint (https://YOUR_API_GATEWAY_URL/storeToken) to be stored along with user information. With the token, we can send Firebase Cloud Messaging (FCM) notifications to specific users.

With these components in place, our Vue.js application can now handle order creation and display order status updates in real time:

  • OrderForm Component: This component allows users to place orders by submitting item details and quantity. It handles form submission and communicates with the backend API.
  • OrderStatus Component: This component listens for FCM notifications and updates the user interface with the latest order status. It provides real-time feedback to users as their orders are processed.
  • Main App Integration: By integrating these components into App.vue, we create a cohesive user interface for managing and tracking orders within our application.

Create a Function to store Token

Let’s create a Cloud Function to store the FCM tokens when users register for notifications. This Cloud Function will accept the token and user ID from the client side and store them in Firestore.

import firebase_admin
from firebase_admin import credentials, firestore
from flask import jsonify, request

# Initialize Firebase Admin SDK
cred = credentials.ApplicationDefault()
firebase_admin.initialize_app(cred, {
    'projectId': 'orderprocessingsystem',
})
db = firestore.client()

def store_token(request):
    try:
        request_json = request.get_json()
        token = request_json.get('token')
        user_id = request_json.get('user_id')

        if not token or not user_id:
            return jsonify({'error': 'Invalid request'}), 400

        docs = db.collection('fcmTokens').where('user_id', '==', user_id).limit(1).get()
        
        if docs:
            doc_id = docs[0].id
            db.collection('fcmTokens').document(doc_id).update({
                'token': token
            })
            return jsonify({'message': 'Token stored successfully'}), 200
        else:
            db.collection('fcmTokens').add({
                'token': token,
                'user_id': user_id
            })
            return jsonify({'message': 'Token stored successfully'}), 200

        return jsonify({'message': 'Token stored successfully'}), 200
    except Exception as e:
        print(f"Error storing token: {e}")
        return jsonify({'error': 'Internal Server Error'}), 500

Update processOrder Function to Include FCM Notification

We’ll need to modify our existing code to include the logic for sending FCM notifications when an order is processed. Here’s how we can achieve this:

def process_order(request):
    try:
        ...

        # Simulate processing delay
        time.sleep(5)  # Simulate processing taking 5 seconds

        # Send FCM notification
        send_fcm_notification(order_id, "USER_ID") // for demostrating purpose.

        ...

def send_fcm_notification(order_id, user_id):
    # Retrieve the FCM token from Firestore
    docs = db.collection('fcmTokens').where('user_id', '==', user_id).limit(1).get()
    if not docs:
        print('No FCM token found for user_id:', user_id)
        return

    fcm_token = docs[0].to_dict().get('token')
    if not fcm_token:
        print('FCM token is missing for user_id:', user_id)
        return

    # Get a reference to the Firebase Cloud Messaging service
    message = messaging.Message(
        data={
            'orderId': order_id,
            'status': 'processed'
        },
        notification=messaging.Notification(
            title='Order Processed',
            body=f'Your order {order_id} has been processed successfully.'
        ),
        token=fcm_token
    )

    # Send the notification
    response = messaging.send(message)
    print('Successfully sent FCM notification:', response)

Test and Verify

Let’s test and verify our application to ensure we can receive the notification instantly. Start the development server by running:

npm run serve

The application should now be running at http://localhost:8080.

Fill out the order form with the necessary details. For example, we might need to enter the order ID, customer details, and other relevant information.

Screenshot-2024-06-20-at-11.09.58 AM-960x1024 Integrating Vue.js with Serverless Order Processing in GCP

Click the “Submit Order” button to submit the order. This action will trigger a request to the backend to create the order and process it.

Screenshot-2024-06-20-at-11.07.23 AM-1024x297 Integrating Vue.js with Serverless Order Processing in GCP

After submitting the order, observe the OrderStatus component is displayed. Initially, it will show “Loading order status…”. Wait for the backend to process the order. This may take a few seconds due to the simulated delay in the process_order function.

Screenshot-2024-06-20-at-11.07.37 AM-1024x394 Integrating Vue.js with Serverless Order Processing in GCP

Once the order is processed, the OrderStatus component should update and display the message indicating that the order has been processed, such as “Order [orderId] processed”.

Screenshot-2024-06-20-at-11.18.00 AM-944x1024 Integrating Vue.js with Serverless Order Processing in GCP

Conclusion

In this tutorial, we explored how to integrate Vue.js with our existing serverless order processing system on Google Cloud. We covered setting up a Vue project, building components for order creation and status display, and integrating Firebase Cloud Messaging (FCM) for real-time notifications. Full source code is available on GitHub.

Share this content:

Leave a Comment

Discover more from nnyw@tech

Subscribe now to keep reading and get access to the full archive.

Continue reading