Mock Screenshot feature of SaaS applications

Post by
Mock Screenshot feature of SaaS applications

Mock Screenshot feature of SaaS applications

While working on a SaaS application for a client recently, I came across a feature where the application allows you to take the screenshot of the entire page and email it either in the Image or PDF format. I got intrigued by this feature and thought of sharing it here to show you how to automatically get and email screenshots.

Step 1. The Setup

We are going to need a backend which I have designed using Node.js and Express.js to take the screenshot and mail it along with the frontend.

The Backend and Frontend can be found in the repos attached.

Step 2. The UI

Honestly, the UI of a SasS application can be quite boring as it just displays a lot of boring data, consisting of metric cards, goal scorecards, charts etc. so to make this blog a little interesting, our UI will display a collection of Pokemon cards, because who doesn't like Pokemons, eh?

UI

Step 3. The Backend

Since the UI is there, let's build the backend. It is gonna be quite simple. It'll have a route which would be called on when you click the "Export Screenshot" button from the front end.

const express = require("express")
let cors = require("cors")

const { sendEmail } = require("./helpers/helper")

const app = express()
app.use(cors())
app.use(express.json())

app.get("/", (req, res) => {
 res.send("backend home route")
})

app.post("/send-screenshot", async (req, res) => {
 const { receiversEmail } = req.body

 try {
   await sendEmail(receiversEmail)
   res.status(200).send("Email sent successfully")
 } catch (err) {
   res.status(400).send("Error in sending the email with screenshot")
 }
})

app.listen(4000, () => {
 console.info("Backend is running on port 4000")
})

Step 4. Taking the Screenshot

With the frontend and backend in place, let's use the Screenshot API's query builder to design a query for the screenshot.

Here, I have designed a query in order to get a high resolution, full page screenshot of the current page.

I am using the following options:

  • Full page screenshot - It means capturng the entire page, including the scrollable area.
  • Retina - Capture high-resolution screenshots.
  • Lazy loading - This will make sure that all the content is loaded before the screenshot is taken.
  • Fresh screenshot - The ScreenshotAPI caches all the screenshots for you on its server so I'm using this option to ensure I get a new screenshot every time. Alternatively, you can use "Destroy screenshot" feature which makes sure that your screenshot is not cached on their server in case the data displayed is sensitive.
  • Load event - A lot of nuances lie with this feature and using it correctly would save a lot of time. By default it is set to "load" but imagine a scenario where the page has a lot of images and obviously those images would take some time to load up. Since you want to make sure that all the images on the page show up in the screenshot, we need to use the option "networkidle" here which essentially means that the API is going to wait until all the network calls are completed and only then is it going to take the screenshot.

Moreover, if you want to take screenshot of any public website, you'll want to use the following two features: "Block ads" and "no Cookie banners."

Finally, the query would look something like this

https://shot.screenshotapi.net/screenshot?token=<YOUR_API_TOKEN>&url=<FORNTEND_URL>&full_page=true&fresh=true&output=image&
file_type=jpeg&lazy_load=true&retina=true&wait_for_event=networkidle

PS. For the frontend URL ngrok can be used.

Step 5. Emailing the Automated Screenshots

We are gonna use nodemailer for sending the screenshot. The screenshot.api will send back the JSON response which would contain the screenshot key with the screenshot URL.

Now, for emailing the image we need to first fetch the image, write it to the disk using the fs module, and then send it using nodemailer.

Attaching the code to the below:

const nodemailer = require("nodemailer")
const axios = require("axios")
const fs = require("fs")

const { SCREENSHOT_API_TOKEN } = require("./credentials")
const path = require("path")

const takeScreenshot = async () => {
 try {
   var query = "https://shot.screenshotapi.net/screenshot"
   let url = "<FRONTEND_URL>"
   query += `?token=${SCREENSHOT_API_TOKEN}&url=${url}&full_page=true&fresh=true&output=
image&file_type=jpeg&lazy_load=true&retina=true&
wait_for_event=networkidle`
   const response = await axios.get(query)

   console.info(JSON.stringify(response.data))

   const imageStream = await axios.get(screenshotURL, {
     responseType: "stream",
   })
   return imageStream
 } catch (err) {
   console.error("\nError while taking the screenshot", err)
   throw err
 }
}

const sendEmail = async (receiversEmail) => {
 try {
   let mailerConfig = {
     host: "smtp.gmail.com",
     port: 587,
     secure: false, // true for 465, false for other ports
     auth: {
       user: "<GMAIL_ID>", // user
       pass: "<APP_PASSWORD>", // password
     },
   }

   let transporter = nodemailer.createTransport(mailerConfig)

   const imageStream = await takeScreenshot()

   const imagePath = path.join(__dirname, "..", "output", "screenshot.png")
   imageStream.data
     .pipe(fs.createWriteStream(imagePath))
     .on("finish", () => {
       // send mail with defined transport object
       let info = await transporter.sendMail({
         from: "<SENDER'S EMAIL ADDRESS>", // sender address
         to: `${receiversEmail}`, // list of receivers
         subject: "Screenshot requested", // Subject line,
         attachment: [
           {
             filename: imagePath,
             content: imageBuffer,
             encoding: "base64",
           },
         ],
         text: "Hello! find the screenshot that you requested attached", // plain text body
         html: "<b>Hello! find the screenshot that you requested attached</b>", // html body
       })
     })
     .on("error", (err) => {
       console.error("Stream closed with following error: ", err)
     })
   return true
 } catch (err) {
   console.error("\nError in sending the email", err)
   throw err
 }
}

module.exports = {
 sendEmail,
}

PS. If you want to use your Gmail account then you'll need to generate an app password. More details can be found here!


Screenshots on Auto-Pilot

And there you have it! If you want to start taking screenshots automatically, use the code I mentioned above, and sign up for ScreenshotAPI. It'll make taking screenshots regularly a piece of cake!