--- title: "Configure the Backend of your Application" url: https://docs.catalyst.zoho.com/en/tutorials/photo-store-app/nodejs/config-backend/ description: "Build a React gallery app to upload and share images in a Stratus bucket. Host the app on AppSail and deploy it from GitHub using Pipelines." last_updated: 2026-11-04T10:10:44Z service: All Services type: tutorial related_links: - name: "Catalyst Stratus" url: /en/cloud-scale/help/stratus/introduction/ - name: "Catalyst AppSail" url: /en/serverless/help/appsail/introduction/ - name: "Stratus Node.js SDK" url: /en/sdk/nodejs/v2/cloud-scale/stratus/overview/ - name: "Stratus REST API" url: /en/api/code-reference/cloud-scale/stratus/get-all-buckets/#GetAllBuckets --- # Configure the Backend of your Application In this step, you are going to configure the backend logic required for the application. Before you start adding code, you need to install the following packages to ensure all required dependencies are satisfied. Navigate to the server folder present in the `PhotoStoreApp/server` directory and install the following packages using the command shown below: - **Express**: The [`express`](https://www.npmjs.com/package/express) package is required to handle HTTP requests. - **Sharp**: The [`sharp`](https://www.npmjs.com/package/sharp) package is required to display the uploaded images as in varying sizes including thumbnails in the application. - **Multer**: The [`multer`](https://www.npmjs.com/package/multer) package is required to handle file uploads in Express applications. - [**Catalyst SDK**](https://docs.catalyst.zoho.com/en/sdk/nodejs/v2/overview/): To use the required SDK methods in the AppSail service. ```sh npm install express path zcatalyst-sdk-node@3.0.0-beta.3 multer sharp ``` ![catalyst_tutorials_photostore_stratus_install_backend_pack](https://catalyst-docs-development.zohostratus.in/images/tutorials/photo-store/catalyst_tutorials_photostore_stratus_install_backend_pack.webp) You will now add code to the following files, in order to implement the required backend logic. We will be adding to code to the following files present in the server folder in the `PhotoStoreApp/server/` directory: - `index.js`: This file will contain the required APIs to implement various application functionalities, such as, converting the images to thumbnails, getting all the images, and getting all the shared images. - `helperfunction.js`: This files contains the required helper functions to complete the processes detailed in the `index.js` file. The following table illustrates the backend logic of this application.
Application Functionality APIs Present in the `index.js` Functions present in the `helperfunction.js`
To resize and upload images as thumbnails `/convertToThumbnailAndUpload` `uploadToStratus()`
Get required images from the bucket `/fetchAllImages` `listMyObjects()`
Get shared images alone `/getSharedImages` `listSharedObjects()` `listMyObjects()`
Copy the code below and paste it into the respective files of your project using an IDE and save the files. > **Note:** Go through the code in this section to make sure you fully understand it. **index.js** ```js 'use strict'; const express = require('express'); const path = require('path'); let catalyst = require('zcatalyst-sdk-node'); const multer = require("multer"); const helperFunctions = require('./helper-functions'); const app = express(); const appDir = path.join(__dirname, '../photo-store-app'); const port = process.env.X_ZOHO_CATALYST_LISTEN_PORT || 9000; app.use(express.json()); app.use(express.static(appDir)); const upload = multer({ dest: "uploads/" }); app.get('/', function (req, res) { res.sendFile(path.join(appDir, 'index.html')); }); app.post("/convertToThumbnailAndUpload", upload.single("image"), async (req, res) => { try { const obj = catalyst.initialize(req, { scope: 'admin' }); const stratus = obj.stratus(); const bucket = stratus.bucket("YOUR_BUCKET_NAME"); const thumbnailName = req.file.originalname.substring(0, req.file.originalname.lastIndexOf(".")); const inputPath = req.file.path; const zuid = req.body.id; console.log("ID: " + zuid); const thumbnailPath = `photos/thumbnails/${zuid}/`; let result; await helperFunctions.uploadToStratus(bucket, inputPath, thumbnailPath, thumbnailName) .then(resp => { console.log("Success"); result = resp; res.json({ message: "Thumbnail created and uploaded successfully" }); }) .catch(error => { console.error("Error: " + JSON.stringify(error.message)); res.status(500).json({ message: "Error Occurred" }); return; }); } catch (error) { console.log("Error in convertToThumbnailAndUpload API: " + error.message); } }); app.get("/fetchAllImages", async (req, res) => { try { const obj = catalyst.initialize(req); const zuid = req.query.id; console.log("ID: " + zuid); const objPath = "photos/" + zuid; const stratus = obj.stratus(); const bucket = stratus.bucket("YOUR_BUCKET_NAME"); let resp = await helperFunctions.listMyObjects(bucket, objPath); res.json(resp); } catch (error) { console.error("Error at fetchAllImages API... ", error.message); res.status(500).send({ error: "An error occurred while fetching images." }); } }); app.get('/getAllUsers', async (req, res) => { try { const app = catalyst.initialize(req, { scope: "user" }); const appAdmin = catalyst.initialize(req, { scope: "admin" }); const userManagementAdmin = appAdmin.userManagement(); const userManagements = app.userManagement(); let allUserPromise = userManagementAdmin.getAllUsers(); let currentUserPromise = userManagements.getCurrentUser(); let details; let currentUser; await allUserPromise.then(allUserDetails => { details = allUserDetails; }).catch(err => { console.log("Error: " + err.message); }); await currentUserPromise.then(details => { currentUser = details.email_id; }).catch(err => { console.log("Error: " + err.message); }); const userDetails = details.map(id => ({ zuid: id.zuid, mailId: id.email_id, name: id.first_name })); const otherUsers = userDetails.filter(user => user.mailId !== currentUser); res.send(otherUsers); } catch (error) { console.error("Error in getAllUsers API: " + JSON.stringify(error.message)); res.status(500).send({ error: "An error occurred while fetching details." }); } }); app.post('/shareDetails', async (req, res) => { try { const app = catalyst.initialize(req); let zcql = app.zcql(); let query = `SELECT COUNT(ImageShareDetails.BucketPath) FROM ImageShareDetails WHERE BucketPath = '${req.body.imagePath}' AND UserZuid = '${req.body.zuid}'`; let result = await zcql.executeZCQLQuery(query); let isPresent = result[0].ImageShareDetails["COUNT(BucketPath)"]; if (isPresent == 0) { let rowData = { UserName: req.body.userName, BucketPath: req.body.imagePath, UserZuid: req.body.zuid, IsUpdate: req.body.isUpdate, SharedBy: req.body.sharedBy }; let datastore = app.datastore(); let table = datastore.table('ImageShareDetails'); let insertPromise = table.insertRow(rowData); insertPromise.then((row) => { console.log("Inserted Row: " + row); }).catch((err) => { console.error("Error: " + err.message); }); res.json({ message: "Access Provided" }); } else { res.json({ message: "Image Already Shared" }); } } catch (error) { console.error("Error in shareDetails API: " + error.message); res.status(500).send({ message: "Error Occurred" }); } }); app.get('/getSharedImages', async (req, res) => { try { const obj = catalyst.initialize(req); const zuid = req.query.id; const objPath = "photos/" + zuid; const stratus = obj.stratus(); const bucket = stratus.bucket("YOUR_BUCKET_NAME"); const zcql = obj.zcql(); let resp = await helperFunctions.listSharedObjects(bucket, objPath, zcql, zuid); res.json(resp); } catch (error) { console.error("Error in getSharedImages API: " + error.message); res.status(500).json({ message: "Error Occurred" }); } }); app.get('/getSharedDetails', async (req, res) => { try { const obj = catalyst.initialize(req); const zuid = req.query.id; let zcql = obj.zcql(); let query = `SELECT * FROM ImageShareDetails WHERE SharedBy = '${zuid}'`; let data = await zcql.executeZCQLQuery(query); const result = data.map(item => ({ UserName: item.ImageShareDetails.UserName, IsUpdate: item.ImageShareDetails.IsUpdate, BucketPath: item.ImageShareDetails.BucketPath, UserId: item.ImageShareDetails.UserZuid })); res.send(result); } catch (error) { console.error("Error in getSharedDetails API: " + error.message); res.status(500).json({ message: "Error Occurred" }); } }); app.patch('/updateSharedDetails', async (req, res) => { try { const obj = catalyst.initialize(req); const isRevoke = req.body.RevokeAccess; const zuid = req.body.UserId; const isUpdate = req.body.IsUpdate; const bucketPath = req.body.BucketPath; let zcql = obj.zcql(); let query; if (isRevoke == "yes") { query = `DELETE FROM ImageShareDetails WHERE UserZuid = '${zuid}' AND BucketPath = '${bucketPath}'`; } else { query = `UPDATE ImageShareDetails SET IsUpdate = ${isUpdate} WHERE UserZuid = '${zuid}' AND BucketPath = '${bucketPath}'`; } let data = await zcql.executeZCQLQuery(query); res.json({ message: "Updated Successfully" }); } catch (error) { console.error("Error in updateSharedDetails API: " + error); res.status(500).json({ message: "Error Occurred" }); } }); app.listen(port, () => { console.log(`Example app listening on port ${port}`); }); ``` > **Note:** Ensure you provide your bucket name in the lines **20**, **49**, and **127**. **helper-function.js** ```js const sharp = require("sharp"); async function listMyObjects(bucket, prefix, isEdit = true) { try { const objects = await bucket.listPagedObjects({ prefix: prefix }); let result = []; for (let i = 0; i < objects.contents.length; i++) { const objDetails = JSON.parse(objects.contents[i]); const imgInfo = { key: objDetails.key, object_url: objDetails.object_url, isEditAccess: isEdit }; result.push(imgInfo); } return result; } catch (error) { console.error("Error at listMyObjects function: " + error); } } async function uploadToStratus(bucket, inputPath, thumbnailPath, thumbnailName) { try { const streamData = await sharp(inputPath) .resize({ width: 150, height: 150 }) .toFormat("jpeg", { quality: 70 }) .toBuffer(); const result = await bucket.putObject( thumbnailPath + thumbnailName + ".jpeg", streamData ); console.log("uploadToStratus method Completed"); return result; } catch (error) { console.error("Error Occurred..." + JSON.stringify(error)); throw { message: "Error in uploading", code: 500 }; } } async function listSharedObjects(bucket, prefix, zcql, zuid) { try { let query = `SELECT * FROM ImageShareDetails WHERE UserZuid = ${zuid}`; let result = await zcql.executeZCQLQuery(query); const queryData = result.map(item => ({ path: item.ImageShareDetails.BucketPath, isEdit: item.ImageShareDetails.IsUpdate })); let allSharedImages = []; for (const item of queryData) { const result = await listMyObjects(bucket, item.path, item.isEdit); allSharedImages.push(result); } return allSharedImages; } catch (error) { console.error("Error Occurred..." + error.message); } } module.exports = { listMyObjects, uploadToStratus, listSharedObjects }; ```