How to Identify and Troubleshoot Issues in Your Electron App

How to Identify and Troubleshoot Issues in Your Electron App

JavaScriptTechnology

Introduction

As developers, it’s easy to get fixated on the mobile sphere. We’re now spending 4-5 hours a day browsing apps on our phone (that’s over 1,800 hours a year), which means a huge volume of demand is channelling into Android and iOS projects.

But desktop apps are booming too. The growth in digital working, allied with the creative possibilities unlocked by apps like Figma, Discord and Loom, mean millions of people now depend on these programs to create, communicate and organize their lives.

So it’s essential that we, as a community, are as conversant with desktop as mobile technologies. And, in this article, we’ll unlock a framework that’ll give you the jump.

Electron is ideal for building those apps that drive the world of work. And today we’ll use this framework to build an app with a basic interface, styling, and API call to interact with the server.

Also, you’ll learn to integrate Bugfender with an Electron app.

What is Electron?

Electron is a brilliant open-source framework for building desktop applications using JavaScript, HTML, and CSS.

It embeds Node.js and Chromium to support web technology stacks, and crucially it enables us to build cross-platform (Windows, Linux, and macOS) applications that can run on these operating systems while adhering to the underlying architecture.

By empowering developers to build apps with JavaScript, HTML, and CSS, Electron opens up the surface area to a whole sweep of other web development frameworks and libraries. You can now use ReactJS, Angular, Next.js, TypeScript, TailwindCSS, and many other exciting web development technologies to build a desktop application.

Electron also enables you to customize the GUI of the app to match the look and feel of the native operating system. You can build the app installer and publish to an application store, for users to download and install.

Ok, we here you… enough of the promo. Now, let’s start building a desktop application using electron.

What are we building today?

We will build a desktop application to fetch the user details from GitHub by providing a user name. The app may sound simple, but you will cover the following areas:

  • Bootstrapping an electron app
  • Adding required libraries
  • Getting the app running with main.js
  • Understanding preloading
  • Building the user interface
  • Using API to fetch data
  • Basic Styling
  • Integrating Bugfender API with the app and starting to collect the logs

In the end, we will have an application that looks like this:

Ready? Let’s get into it.

Building the app using Electron

Let’s start by installing dependencies and setting up a project. If you want to get eyes on the code early, you can find it on this GitHub repository:

GitHub – atapas/learn-electron: An electron project to build a dashboard application. Using this app you can search any public GitHub user account using the user name.

Install and Set up

It’s important to note that you must have Node.js installed, so check whether you have a valid instance on board. To do so, simply open a terminal and type the command mentioned below:

node -v

The above command should print the version of Node.js.

Now, create a project directory and change it accordingly.

mkdir fetch-github && cd fetch-github

Now we will initiate a project. We will be using yarn instead of npm in this article; however, if you are only familiar with npm, no worries. You will find the alternate npm commands beside the yarn commands.

When you use this command to initiate a project, it will prompt a few questions. When you give the answers, make sure you have provided main.js as the entry point of your application.

yarn init # Or, npm init

In the end, you will find a file called package.json created inside the folder. The below content is from my personal package.json file.

{
  "name": "fetch-github",
  "version": "1.0.0",
  "description": "It is an electron app that showcase the GitHub fetch!",
  "main": "main.js",
  "keywords": [
    "electron",
    "github"
  ],
  "author": "Tapas Adhikary",
  "license": "MIT"
}

Let’s install the electron package now. We will install it as a dev dependency.

yarn add --dev electron # Or, npm install --save-dev electron

Now, we need to edit the package.json file to add a script entry to start the electron app using the yarn/node command.

{
  "scripts": {
    "start": "electron ."
  }
}

Finally, you can use the start command to start the application locally.

yarn start # Or, npm start

At this stage, you will encounter an error if you try to start the app locally. Don’t worry! We have not created an entry point for the app to run yet. So let’s do that.

The main process

We need to define the main script to create the entry point of the electron application. Looping back around, you’ll remember that we provided the value main.js for the entry point.

Now, let’s create a file called main.js at the root of the project folder. The main.js is a JavaScript file that will control the app’s lifecycle, manage renders, help with native display, and more.

Add the following code in the main.js file:

const { app, BrowserWindow } = require('electron')

// include the Node.js 'path' module at the top of your file
const path = require('path')

const createWindow = () => {
  const win = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js')
    }
  })

  win.loadFile('index.html')
  win.webContents.openDevTools()
}

app.whenReady().then(() => {
  
  createWindow()
  app.on('activate', () => {
    if (BrowserWindow.getAllWindows().length === 0) createWindow()
  })
})

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') app.quit()
})

Let’s review the code section by section to understand what’s happening here.

  • First, we have added two electron modules, the app and BrowserWindow. The app module takes care of the app’s lifecycle, and the BrowserWindow module helps deal with the application windows.
  • We also require the path module to locate a preload script (don’t worry, we will explore this in a while).
  • Then we added a createWindow function that will load the index.html file (we will create it shortly) on a defined window size of 800X600.
  • We have made sure that we open DevTools so that we can debug the code whenever it is needed.
  • Notice the webPreferences section inside the createWindow method. The preload option of the webPreferences object helps us to load a script that runs before any rendering takes place in the application’s interface. We will not use it much in our application, but it is good to know about it.
  • Finally, we invoked the createWindow function once the whenReady promise was resolved successfully. We have also taken care of quitting the app when the window is closed.

The Interface

Now create an index.html file at the root of the project folder, with the following content:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Fetch GitHub Users</title>
    <link rel="stylesheet" href="./style.css">
  </head>
  <body>
    <div class="container">
      <h1>Fetch GitHub User Details</h1>
      <div class="fields">
        <input type="text" placeholder="Enter user name" id="uname" />
        <button id="fetch-action">Fetch</button>
      </div>
      <div id="users"></div>
      <div id="error"></div>
    </div>
    <script src="renderer.js"></script>
  </body>
</html>

This is a simple HTML file linked to a CSS file called style.css. The body contains a heading and input text for users to provide a GitHub user name and a button to initiate the search by user name.

We have added a couple of empty div sections, showing the search result and the error messages. In the end, we see a script called render.js, where we will place all the rendering logic of the application. We will learn about it soon, but before we get there, let’s create the style.css file.

To create a file called style.css , you can use the following content:

.container {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  align-content: center;
}

.fields {
  margin-bottom: 5px;
}

.fields input[type="text"] {
  height: 2rem;
  width: 15rem;
  border-radius: 10px;
}

.fields button {
  height: 2rem;
  width: 4rem;
  border-radius: 5px;
  cursor: pointer;
}

.result {
  background-color: #ebebeb;
  padding: 1rem;
  border-radius: 3px;
  display: flex;
  flex-direction: column;
  align-content: center;
  justify-content: center;
  align-items: center;
  margin: 10px;
}

.result img {
  width: 200px;
  height: 200px;
  border-radius: 50%;
}

Now go to your terminal and run the yarn start command to start the app locally. This time, you should see the app running on a window.

As you’ll notice, the DevTools section is open, showing an error which indicates that the render.js file is not found. That’s correct, because we haven’t created it yet. So let’s do that now.

Logic and Rendering

So far, we have built an application that renders HTML, which is not very useful on its own. To make it more useful, we need to add a little pinch of logic.

Create a file called render.js at the root of the project folder, with the following content:


// 1. The logic to fetch user details from GitHub and show to the UI
const fetchUser = () => {
  const userName = document.getElementById('uname').value
  fetch(`https://api.github.com/users/${userName}`)
		.then(response => response.json()) //Converting the response to a JSON object
		.then( data => {
      if (data.name) {
        document.getElementById("error").innerText = ''
        document.getElementById('users').innerText = ''
        
        document.getElementById('users').classList.add('result')

        if(data['avatar_url']) {
          const img = document.createElement('img')
          img.src = data['avatar_url']
          img.alt = data.name
          document.getElementById("users").appendChild(img)
        }
        addData('p', data.name)

        if(data.bio) {
          addData('p', data.bio)
        } 
      } else {
        document.getElementById('users').classList.remove('result')
        document.getElementById("users").innerText = ''
        document.getElementById("error").innerText = `No information available for the user ${userName}`
      }
    })
		.catch( error => {
      console.error(error)
    })
}

/**
  a method to add an element to the DOM with a text value
*/
const addData = (elem, value) => {
  const element = document.createElement(elem)
  element.innerText = value
  document.getElementById("users").appendChild(element)
}

// 2. Add the Click Event Handler to the button
document.getElementById('fetch-action').addEventListener('click', fetchUser)

There’s a lot to unpack there, so let’s go back and review what we’ve done.

  • We have defined a method called fetchUser() to fetch the user details using the GitHub API. We have also made an async call using JavaScript’s fetch API.
  • After fetching the user details, we have extracted the avatar, name, and bio from the response.
  • We have used the method addData() to create DOM elements and added the details as text values.
  • Finally, we have attached the fetchUser() method to the click handler of the Fetch button defined in the index.html file.

Now close and reopen the app. You should find the app working on fetching the user details based on the user name. Here, you’ll see I can use the user name to fetch my details from GitHub (cool, right?)

Integrating Bugfender with Electron

Ok, now for the bit we’ve all been waiting for…

Bugfender is a remote logging service that allows users to collect logs for everything in the application. It is easy to integrate Bugfender with Electron and get started, and it’s also possible to integrate SDKs with other web and mobile applications (if we say so ourselves).

To kick off the integration, simply create an account with Bugfender. Once logged in, create a Bugfender application for web apps using the Web SDK option. You can follow this step-by-step guide to create a Bugfender application, and you will find an API key ready for you. Keep it safe.

Once you have created your app, the Bugfender dashboard will enable you to keep track of logs, issues, feedback and crashes. This is how my dashboard looks:

The BugFender dashboard with an app

Add Bugfender to the Electron app

Bugfender provides SDKs you can use as NPM or a browser script. We have built our application using Plain JavaScript and HTML, so we will import the SDK directly into the index.html file.

Please import the Bugfender SDK script inside the <head>...</head> tags.

<script src="<https://js.bugfender.com/bugfender-v2.js>"></script>

Next, we need to initialize Bugfender to start using its services. To do so, you need to use the API key you created in the previous step.

Please add this code snippet at the top of the render.js file.

Bugfender.init({
  appKey: '<YOUR_API_KEY>',
})

// the code below remain unchanged
const fetchUser = () => {
.....
....
...

Now let’s use the methods from the Bugfender SDK. The SDK provides the following logs to send to the server.

  • Bugfender.trace()
  • Bugfender.info()
  • Bugfender.log()
  • Bugfender.warn()
  • Bugfender.error()
  • Bugfender.fatal()

You can modify the fetchUser() method in the render.js file to include some of these methods. Please note the Bugfender method usage in the modified code below. We have used the log, warn, and error methods to log these scenarios:

  • A normal log message when grabbing the user name provided by the user.
  • A normal log message, when we find user data from the GitHub API response.
  • A warning log, when the avatar is missing in the user data.
  • A warning log, when the bio is missing in the user data.
  • An error log, when the user data itself is missing.
const fetchUser = () => {
  const userName = document.getElementById('uname').value

  Bugfender.log(`Grabbed the username as ${userName}`)
  
  fetch(`https://api.github.com/users/${userName}`)
		.then(response => response.json()) //Converting the response to a JSON object
		.then( data => {
      if (data.name) {
        Bugfender.log(`Found the user ${data.name} by the user name!`)
        
        document.getElementById("error").innerText = ''
        document.getElementById('users').innerText = ''
        
        document.getElementById('users').classList.add('result')

        if(data['avatar_url']) {
          const img = document.createElement('img')
          img.src = data['avatar_url']
          img.alt = data.name
          document.getElementById("users").appendChild(img)
        } else {
          Bugfender.warn(`No avatar presents for the user ${userName}`)
        }
        
        addData('p', data.name)

        if(data.bio) {
          addData('p', data.bio)
        } else {
          Bugfender.warn(`No bio presents for the user ${userName}`)
        }
      } else {
        Bugfender.error(`A problem occured!!! We couldn't fetch the data for the user name ${userName}`)
        
        document.getElementById('users').classList.remove('result')
        document.getElementById("users").innerText = ''
        document.getElementById("error").innerText = `No information available for the user ${userName}`
      }
    })
		.catch( error => {
      console.error(error)
      Bugfender.error(error)
    })
}

View the Logs in the Bugfender Console

Now you’ll need to reopen your application window and perform some key actions, including:

  • Search details of a user who returns all data, including avatar, name, and bio.
  • Search details of a user who is missing an avatar or bio
  • Search details of a user that doesn’t exist!

Head over to the Bugfender console and log in. Now you should start seeing the respective logs appearing for you to analyze.

  • Log by grabbing user name and a valid response from the GitHub API:
  • Observe the warning log (in the yellow background) for an event where the bio info is missing in the API response:
  • Here is an event where we couldn’t get the user information as the user name doesn’t exist with GitHub:

Apart from the user-defined logs, you can also observe the user interactions and activity logs in the Bugfender console. Knowing how your users would have passed through the application and used it is very helpful. Take a moment to go through the logs in the image below:

Now let’s read it in sequence.

The device browser is detected as Electron, and that’s right. Next, we’ll see that the user has focused on the user name input box, interacted with it and then focused out. Next, the user focused on the button and clicked. With this action, we can fetch the user data and log that as well.

But wait, that’s not all folks!

You can perform other advanced usages with the Bugfender SDK. For example, you can create issues to keep track of important errors. To create an issue, you can use the sendIssue() method like this:

Bugfender.sendIssue('Invalid User Name', 'Please check the user name. It is Invalid.');

You can also view the issue in the Bugfender console to send it to GitHub for tracking.

Finally, you can collect user feedback using Bugfender’s SDK and send it back to the server. You can send logs with tags, send device-associated data and many more options. Please read this guide to know the usage better.

Right, what’s Next?

I hope you’re energised after this article and that it helps you to get started with Electron and Bugfender SDK. You can explore other Electron features to enhance the app and plan to build-publish it on the app store.

Before we end, here are some resources to read and explore further.

Trusted By

Orange as_cantina as_domestika as_safedome Slack as_adt_logo as_sk_telecom

Already Trusted by Thousands

Bugfender is the best remote logger for mobile and web apps.

Get Started for Free, No Credit Card Required