I built an Electron app using React and bundled it with Webpack. The backend is built using Express.js, and during development (npm start), everything works perfectly—the backend starts and handles HTTP requests without any issues.
However, after building the app using npm run make, the backend doesn’t seem to work. Specifically, HTTP requests to the backend are stuck in a pending state, and no response is received.
Here’s the setup:
Frontend: Built with React and bundled using Webpack.
Backend: Built with Express.js, running on localhost (e.g., http://localhost:8000).
Development: Everything works fine, requests to the backend are handled correctly.
Production Build: After running npm run make, requests to the backend are stuck in a pending state.
Here is my index.ts file (main):
import { app, BrowserWindow, screen } from "electron";
import { startServer, checkServerHealth } from "./backend/server";
// This allows TypeScript to pick up the magic constants auto-generated by Forge's Webpack
// plugin that tells the Electron app where to look for the Webpack-bundled app code (depending on
// whether you're running in development or production).
declare const MAIN_WINDOW_WEBPACK_ENTRY: string;
declare const MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY: string;
// Handle creating/removing shortcuts on Windows when installing/uninstalling.
import("electron-squirrel-startup").then((module) => {
if (module.default) {
app.quit();
}
});
const createWindow = (): void => {
// Get the primary display's work area size (excluding the taskbar)
const { width, height } = screen.getPrimaryDisplay().workAreaSize;
// Create the browser window to fit the full screen dimensions with the taskbar visible.
const mainWindow = new BrowserWindow({
width, // Set width to the screen's width
height, // Set height to the screen's height
webPreferences: {
preload: MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY,
webSecurity: false, // This should only be used for development
},
});
// Load the index.html of the app.
mainWindow.loadURL(MAIN_WINDOW_WEBPACK_ENTRY);
// Set the CSP directly in the HTML file (recommended approach):
mainWindow.webContents.on("did-finish-load", () => {
mainWindow.webContents.executeJavaScript(`
const meta = document.createElement('meta');
meta.httpEquiv = 'Content-Security-Policy';
meta.content = "default-src 'self'; connect-src 'self' http://localhost:3000 http://localhost:8000; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline';";
document.getElementsByTagName('head')[0].appendChild(meta);
`);
});
};
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
app.on("ready", async () => {
try {
console.log("Starting backend...");
startServer();
console.log("Backend started. Checking health...");
await checkServerHealth();
console.log("Backend health check passed. Creating window...");
createWindow();
} catch (error) {
console.error("Failed to start backend or check health:", error.message);
}
});
// Quit when all windows are closed, except on macOS.
app.on("window-all-closed", () => {
if (process.platform !== "darwin") {
app.quit();
}
});
app.on("activate", () => {
// On macOS, re-create a window in the app when the dock icon is clicked
// and there are no other windows open.
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
When creating window object test following property:
allowRunningInsecureContent: true
mainWindow = new BrowserWindow({
icon: path.join(__dirname, '../icon/logo.ico'),
webPreferences: {
nodeIntegration: true,
webSecurity: false,
allowRunningInsecureContent: true,
}
});