
Cos’è Electron e a cosa serve
Electron è un framework open-source che consente lo sviluppo di applicazioni desktop native utilizzando tecnologie web standard: HTML5, CSS3 e JavaScript. Originariamente creato da GitHub per l’editor Atom (e oggi alla base di software come Visual Studio Code, Slack e Spotify), Electron combina due pilastri tecnologici fondamentali:
- Chromium: Il motore di rendering open-source di Google Chrome, incaricato di visualizzare l’interfaccia utente (UI).
- Node.js: L’ambiente di runtime JavaScript lato server, che garantisce l’accesso diretto al sistema operativo, al file system e alle risorse hardware.
L’obiettivo primario di Electron è abbattere le barriere tra il web e il desktop. Consente agli sviluppatori web di riutilizzare interamente le proprie competenze e stack tecnologici (React, Vue, Astro, Tailwind) per distribuire applicazioni cross-platform stabili su Windows, macOS e Linux da un unico codebase.
L’Architettura a Processi Separati
Per comprendere a fondo Electron, è essenziale capirne l’architettura. Electron eredita da Chromium una struttura multi-processo per garantire stabilità e sicurezza:
- Main Process (Processo Principale): Gestito da Node.js, è il punto di ingresso dell’applicazione. Ha il controllo completo sul ciclo di vita dell’app, crea le finestre native del sistema operativo (
BrowserWindow), gestisce i menu nativi e possiede i privilegi completi di accesso al sistema operativo. - Renderer Process (Processo di Rendering): Ogni finestra aperta dall’applicazione corrisponde a un processo di rendering separato. Questo processo si comporta esattamente come una scheda del browser: renderizza la pagina web (HTML/CSS/JS). Per motivi di sicurezza (soprattutto se carica contenuti remoti), l’accesso diretto a Node.js all’interno del Renderer è disabilitato di default (
nodeIntegration: false,contextIsolation: true). - Preload Script: Un ponte sicuro tra il Main e il Renderer. Esegue in un contesto speciale che ha accesso sia alle API di Node.js sia al DOM del Renderer, esponendo selettivamente funzionalità sicure tramite l’oggetto
contextBridge.
Confronto con altre soluzioni Desktop GUI
Nel panorama attuale, Electron non è l’unica opzione. Ecco una matrice comparativa per valutare la scelta tecnologica in base alle necessità strutturali del progetto:
| Criterio | Electron | Tauri | Flutter (Desktop) | Native (C#/.NET / Swift) |
|---|---|---|---|---|
| Tecnologia UI | HTML / CSS / JS | HTML / CSS / JS | Dart (Skia/Impeller) | XAML / Storyboards |
| Backend Core | Node.js | Rust | Dart | C# / Swift |
| Peso dell’App | Elevato (~100MB+) | Ridotto (~10-15MB) | Medio (~30-50MB) | Minimo (Nativo) |
| Consumo RAM | Elevato (Chromium inst.) | Basso/Medio (Webview OS) | Moderato | Ottimizzato |
| Accesso OS | Eccellente e nativo | Eccellente (via Rust) | Buono (tramite plugin) | Totale e nativo |
| Curva di apprendimento | Molto Bassa (Web devs) | Media (Richiede Rust) | Media (Nuovo ecosistema) | Alta (Specificità OS) |
Perché scegliere Electron?
Nonostante il peso dei pacchetti e l’impronta di memoria causati dall’inclusione di Chromium, Electron rimane lo standard industriale per la sua maturità, per la sterminata disponibilità di librerie dell’ecosistema npm e per la fedeltà di rendering cross-platform garantita dall’avere un motore di visualizzazione integrato identico su ogni OS.
Architettura di Dialogo: Electron + Backend Remoto (Laravel)
Quando si progetta un’interfaccia desktop con Electron che deve dialogare con un backend strutturato come Laravel, si aprono tre pattern architetturali principali:
- Embedded WebView (Monolitico Remoto): Electron punta direttamente l’URL dell’applicazione Laravel remota (
win.loadURL('https://mio-app-laravel.com')). L’app desktop agisce da guscio (Kiosk). - API Driven (Decoppiato): Il frontend (es. una Single Page Application o un sito statico in Astro) viene impacchettato localmente dentro Electron (
win.loadFile(...)) ed esegue chiamate asincrone (fetchoaxios) verso le API REST o GraphQL esposte dal server Laravel. - Hybrid Local/Remote con IPC Bridge: Il frontend locale gestisce l’interfaccia e sfrutta il Processo di Preload e i canali IPC (Inter-Process Communication) per delegare al processo Main le operazioni di rete complesse, la persistenza locale o la crittografia dei token di autenticazione.
Il terzo approccio è il più sicuro e robusto per applicazioni enterprise, in quanto separa nettamente la logica di presentazione del browser dalle chiamate di sistema e di rete.
Guida Pratica: Costruzione dell’Interfaccia ed Esempi di Codice
Vediamo come strutturare un’applicazione Electron minimale che comunica in modo sicuro con un backend Laravel per recuperare e visualizzare l’elenco dei prodotti di un magazzino logistico.
1. Inizializzazione del Progetto Electron
Crea una nuova cartella per la GUI desktop e inizializza il progetto:
mkdir mensiva-desktop-gui
cd mensiva-desktop-gui
npm init -y
npm install electron --save-dev
Modifica il file package.json impostando il file di ingresso e lo script di avvio:
{
"name": "mensiva-desktop-gui",
"version": "1.0.0",
"main": "main.js",
"scripts": {
"start": "electron ."
},
"dependencies": {},
"devDependencies": {
"electron": "^31.0.0"
}
}
2. Il Processo Main (main.js)
Questo file configura la finestra principale, blocca l’accesso diretto a Node per motivi di sicurezza e si pone in ascolto sui canali IPC per inoltrare le richieste a Laravel.
const { app, BrowserWindow, ipcMain } = require('electron');
const path = require('path');
let mainWindow;
function createWindow() {
mainWindow = new BrowserWindow({
width: 1000,
height: 700,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: true, // Isola il contesto del preload dal renderer
nodeIntegration: false // Disabilita Node.js direttamente nel renderer
}
});
mainWindow.loadFile('index.html');
}
app.whenReady().then(() => {
createWindow();
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) createWindow();
});
});
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit();
});
// Canale IPC per intercettare la richiesta dati del Renderer e interpellare Laravel
ipcMain.handle('fetch-products', async (event, token) => {
// In produzione, l'URL verrebbe da una configurazione di ambiente
const LARAVEL_API_URL = '[https://api.mensiva.it/v1/products](https://api.mensiva.it/v1/products)';
try {
const response = await fetch(LARAVEL_API_URL, {
method: 'GET',
headers: {
'Authorization': `Bearer ${token}`,
'Accept': 'application/json',
'Content-Type': 'application/json'
}
});
if (!response.ok) {
throw new Error(`Errore Server Laravel: ${response.status}`);
}
return await response.json();
} catch (error) {
return { error: true, message: error.message };
}
});
3. Il File di Preload (preload.js)
Il file di preload espone in modo sicuro l’API al Renderer senza esporre l’intero modulo ipcRenderer.
const { contextBridge, ipcRenderer } = require('electron');
// Esponiamo un oggetto sicuro sul window globale del Renderer
contextBridge.exposeInMainWorld('mensivaAPI', {
getProducts: (token) => ipcRenderer.invoke('fetch-products', token)
});
4. Il Renderer Process (index.html e renderer.js)
Ecco la nostra interfaccia utente minimale.
index.html
<!DOCTYPE html>
<html lang="it">
<head>
<meta charset="UTF-8">
<title>Mensiva Logistica - Desktop Dashboard</title>
<style>
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #f4f6f9; color: #333; margin: 20px; }
.card { background: white; padding: 20px; border-radius: 6px; box-shadow: 0 2px 4px rgba(0,0,0,0.05); }
button { background: #007bff; color: white; border: none; padding: 10px 15px; border-radius: 4px; cursor: pointer; }
button:hover { background: #0056b3; }
ul { list-style-type: none; padding: 0; }
li { padding: 10px; border-bottom: 1px solid #eee; display: flex; justify-content: space-between; }
.error { color: #dc3545; font-weight: bold; }
</style>
</head>
<body>
<div class="card">
<h1>Dashboard Magazzino</h1>
<button id="load-btn">Sincronizza Dati Laravel</button>
<p id="status-msg"></p>
<ul id="products-list"></ul>
</div>
<script src="renderer.js"></script>
</body>
</html>
renderer.js
document.getElementById('load-btn').addEventListener('click', async () => {
const listElement = document.getElementById('products-list');
const statusMsg = document.getElementById('status-msg');
listElement.innerHTML = '';
statusMsg.innerText = 'Chiamata IPC in corso verso il Main process...';
// Token fittizio recuperato in una fase precedente di login
const authToken = "1|eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...";
// Utilizzo sicuro dell'API esposta dal contextBridge
const result = await window.mensivaAPI.getProducts(authToken);
if (result.error) {
statusMsg.innerHTML = `<span class="error">Errore di sincronizzazione: ${result.message}</span>`;
return;
}
statusMsg.innerText = 'Dati ricevuti con successo dal backend Laravel!';
// Supponendo che Laravel restituisca un array sotto la chiave 'data'
const products = result.data || result;
products.forEach(product => {
const li = document.createElement('li');
li.innerHTML = `<span><strong>${product.name}</strong> (SKU: ${product.sku})</span> <span>Q.tà: ${product.stock}</span>`;
listElement.appendChild(li);
});
});
Tips & Tricks per uno sviluppo professionale
Gestione dei Token di Autenticazione (Sanctum/Passport): Evita assolutamente di salvare i JWT o i token Bearer di Laravel nel localStorage del Renderer. Utilizza invece librerie native del Main Process come keytar per salvare i segreti nel portachiavi di sistema (Keychain su macOS, Credential Manager su Windows).
Abilitare la Content Security Policy (CSP): All’interno dell’head del file HTML, dichiara una CSP rigida per prevenire attacchi di tipo Cross-Site Scripting (XSS), specialmente se l’app esegue il rendering di dati inseriti dagli utenti sul backend remoto.
Gestione degli Stati di Connessione Offline: Una vera app desktop deve saper gestire la mancanza di rete. Sfrutta l’evento del browser navigator.onLine nel Renderer per bloccare le richieste IPC e mostrare una notifica nativa (new Notification(…)) se il server Laravel non è raggiungibile.
Ottimizzazione del peso finale (Bundle Size): Rimuovi i moduli npm non utilizzati prima dell’impacchettamento finalizzato con electron-builder. Ogni dipendenza pesante inserita nel Main process incrementa il tempo di bootstrap dell’applicazione.
Bibliografia essenziale e Risorse Online
Documentazione Ufficiale di Electron: electronjs.org/docs https://www.electronjs.org/docs/latest/ - Il punto di partenza imprescindibile per comprendere le API di sicurezza (contextBridge, ipcMain, ipcRenderer).
Electron Security Checklist: electronjs.org/docs/latest/tutorial/security https://www.electronjs.org/docs/latest/tutorial/security - Linee guida fondamentali per la messa in sicurezza dei processi e la mitigazione del codice arbitrario.
Documentazione Laravel API: laravel.com/docs/master/api https://laravel.com/docs/ - Riferimento ufficiale per la creazione di endpoint API stateless protetti da Laravel Sanctum.
Repository di Esempio di Sicurezza (Avanzato): github.com/reZach/secure-electron-template https://github.com/reZach/secure-electron-template - Un template solido incentrato su un’architettura IPC blindata e configurazioni CSP ottimali.