Explanation - Yesh492/gcloud-vision-saas GitHub Wiki

This page describes the inner workings of the application, the role of each component, and how it was deployed using Google App Engine.

🧠 What Our App Does

This project is a Software-as-a-Service (SaaS) web application that uses the Google Cloud Vision API to analyze images uploaded by users and return relevant labels (tags) describing the image contents. These labels include objects, scenes, or characteristics identified by Google’s AI model.

The workflow is simple and user-friendly:

A user accesses the web app and uploads an image via a clean HTML form. The image is sent to a backend server running on Node.js with the Express framework. The server uses Google Cloud’s Vision API to analyze the uploaded image. The API returns label annotations based on what it detects. These labels are formatted and returned to the user in a styled HTML response.

🧰 Technologies Used

  • Node.js - JavaScript runtime to build backend logic
  • Express.js - Lightweight web server framework
  • Multer - Middleware for handling image uploads
  • Google Cloud Vision API - AI image analysis (label detection)
  • Google App Engine - Cloud platform for deployment
  • HTML/CSS - Frontend form and result page styling

Backend Code Overview (index.js)

Dependencies and Setup

  • const express = require('express');
    Express is used to set up server routes.
  • const multer = require('multer'); Multer handles multipart form data (image uploads).
  • const vision = require('@google-cloud/vision');
    Vision connects to Google Cloud’s image analysis API.

Initialize App and Middleware

  • const app = express();
    app: our main Express app instance.
  • const upload = multer({ storage: multer.memoryStorage() }); upload: multer configured to use in-memory storage, which is essential for environments like Google App Engine, which do not allow local disk writes.
  • const client = new vision.ImageAnnotatorClient(); client: creates a Vision API client instance that communicates with Google Cloud.

Serve Static Files

  • app.use(express.static('public')); This allows serving static files like index.html from the /public folder.

POST /upload Route for Image Submission

  • app.post('/upload', upload.single('image'), async (req, res) => { ... });

Handles image uploads sent via the form on the frontend. Uses multer middleware to parse multipart/form-data. The uploaded file is accessible via req.file.

Check for Uploaded File

  • if (!req.file || !req.file.buffer) { throw new Error('No file uploaded or file is empty'); }

Validates whether an image file was uploaded. If not, throws an error which will be caught and returned as a 500 response.

Use Vision API to Perform Label Detection

  • const [result] = await client.labelDetection({ image: { content: req.file.buffer } });

This sends the in-memory image buffer to the Vision API. The API responds with a result containing label annotations.

Parse Labels

  • const labels = result.labelAnnotations.map(label => label.description);

Extracts readable labels (like "Snow", "Mountain", etc.) from the API's response. Only the description field is returned for simplicity.

Return a Styled HTML Page with Results

  • res.send(` ... ${labels.map(label => `
  • ${label}
  • `).join('')} ...

`);

Dynamically injects labels into a styled HTML template. Results are displayed in a clean UI using custom CSS.

Error Handling

  • catch (err) { console.error("❌ ERROR:", err.message); res.status(500).send(<h2>Internal Server Error</h2><pre>${err.message}</pre>); }

Logs and displays detailed error messages when something goes wrong (e.g., missing image, API failure).

Start the Server

  • const PORT = process.env.PORT || 8080; app.listen(PORT, () => { console.log(✅ Server running on port ${PORT}); });

Starts the server on the specified port (default: 8080). process.env.PORT allows it to work properly on Google App Engine, which assigns a dynamic port.

Frontend UI Code Explanation (index.html)

Fonts & Styling

Loads the "Inter" font from Google Fonts for modern, readable UI.

CSS Styling

  • body { background: linear-gradient(to right, #e0eafc, #cfdef3); ... }

Uses a soft gradient background and card-style container.

  • container { background: #fff; padding: 2rem; ... }

Provides padding, drop shadows, and rounded corners.

  • button { background-color: #4285f4; color: white; ... }

The button has a material-blue color with hover-friendly styling.

Main Upload Form

  • form id="uploadForm" action="/upload" method="POST" enctype="multipart/form-data"

The action="/upload" sends the image to the Express backend route you defined in index.js.

  • input type="file" name="image" accept="image/*" required /

The enctype ="multipart/form-data" ensures the image is transmitted properly.

  • button type="submit">Analyze Image /button /form

Accepts any image file format (.png, .jpg, .jpeg, etc.)

Loading Indicator

p id="loading">⏳ Analyzing image...</p

Displays a text-based loading message when the form is submitted.

JavaScript for UX Enhancement

  • const form = document.getElementById('uploadForm');
  • const loading = document.getElementById('loading');
  • form.addEventListener('submit', () => { loading.style.display = 'block'; });

Adds interactivity: once the user clicks “Analyze Image”, the loading message appears immediately. Gives feedback that the image is being processed while waiting for a server response.


☁️ Deployment to Google App Engine

Google Cloud Project

  • Created GCP project: exercisesaas
  • Set up billing and enabled APIs

Enable Google Cloud Vision API

  • Navigated to APIs & Services → Library
  • Enabled Google Cloud Vision API

App Engine Setup

  • Chose Standard Environment
  • Created app.yaml to define runtime

Deploying the App

Authentication Notes

  • Locally, a GOOGLE_APPLICATION_CREDENTIALS environment variable was set using a downloaded .json file.
  • In production (App Engine), no manual credentials are needed — GCP handles auth behind the scenes.
⚠️ **GitHub.com Fallback** ⚠️