Placing your DB code in your main process with nodeIntegration: false
and contextIsolation: true
is definitely the way to go when locking-down your application. Doing so will also prevent your render processes from freezing during heavy / lengthy DB calls.
There is no "typical pattern" for the structure of your main.js
(main process) file.
That said, placing all your main process code in your main.js
file will quickly lead to an unmaintainable file.
The file structure of your Electron Application is totally up to you but usually it is best to structure it in a logical hierarchical order. For example:
├─ dist
├─ node_modules
├─ src
| ├─ main-process
| | ├─ db
| | | ├─ file-1.js
| | | ├─ file-2.js
| | | └─ file-3.js
| | ├─ windows
| | | └─ main-window.js
| | ├─ main.js <-- Entry point
| | └─ preload.js
| └─ render-process
| ├─ main.html
| ├─ style.css
| └─ render.js
├─ package.json
└─ package-lock.json
Then, within your main.js
file, just require the files necessary to get your application up and running.
main.js
(main process)
// Import the necessary electron modules.
const electronApp = require('electron').app;
const electronBrowserWindow = require('electron').BrowserWindow;
// Import the necessary Node modules.
const nodePath = require('path');
// Import the necessary Application modules.
const appDb = require(nodePath.join(__dirname, './db/file-1'));
const appMainWindow = require(nodePath.join(__dirname, './windows/main-window'));
// Prevent garbage collection.
let mainWindow = null;
electronApp.on('ready', () => {
mainWindow = appMainWindow.create();
// Do DB connection here...
});
electronApp.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
electronApp.quit();
}
});
electronApp.on('activate', () => {
if (electronBrowserWindow.getAllWindows().length === 0) {
appMainWindow.createWindow();
}
});
main-window.js
(main process)
// Import the necessary Electron modules.
const electronBrowserWindow = require('electron').BrowserWindow;
// Import the necessary Node modules
const nodePath = require('path');
// Define the main window.
let mainWindow;
// Create the main window.
function create() {
mainWindow = new electronBrowserWindow({
x: 0,
y: 0,
width: 800,
height: 600,
show: false,
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
preload: nodePath.join(__dirname, '../preload.js')
}
});
mainWindow.loadFile(nodePath.join(__dirname, '../../render-process/main.html')
.then(() => { window.show(); });
return mainWindow;
}
// Get the main window instance.
function get() {
return mainWindow;
}
module.exports = {create, get}
Communicating between processes will be via IPC through the use of your preload.js
script.
Examples of various forms of preload.js
scripts can be found below .
For main process modules that need to receive events and data from the render process (EG: Your DB scripts), just include and use ipcMain
within your file.
const electronIpcMain = require('electron').ipcMain;
electronIpcMain.on('channelName', (event, message) => {
console.log(message);
})
For main process modules that need to transmit events and data to the render process(es), they will require reference to the window. If your module does not have a reference to the window, use your windows module get()
method. For example:
// Import the necessary Application modules.
const appMainWindow = require(nodePath.join(__dirname, './windows/main-window'));
function name() {
let mainWindow = appMainWindow.get();
let data = {'message': 'hello'}
mainWindow.webContents.send('channelName', data);
}
If you need to communicate between modules in your main process, instead of tightly coupling your module methods together you could use Node's event system. This nicely separates methods, files and domains for ease of maintainability and loose coupling.
To knit this all together, require
and use the modules that are necessary to get your application up and running within your main.js
file.
Within these imported modules, you can require
other modules that are needed for their functionality.
The use of module.exports
allows for the exporting of publicly available methods. Structure and separation is needed within your files when doing this else circular reference may occur.
Without seeing exactly how your DB files are separated or structured, it is hard to give further detail.
Lastly, debugging in the main process can be just as easy as debugging in the render process when setup correctly. See the below points for more information.