Skip to content

wvalencs/devTo-electron

Repository files navigation

Dev.to Desktop Client

Node.js backend + Electron frontend desktop app for browsing Dev.to articles.

A cross-platform Electron desktop application that wraps the Dev.to platform into a native reading and interaction experience. The frontend is built with vanilla JS/HTML/CSS inside Electron, backed by an embedded Express server that proxies the Dev.to public API, handles authentication, and manages caching — all running inside a single portable .exe with no installation required.

  1. Embedding a backend server inside Electron Running an Express HTTP server inside the Electron main process instead of spawning a separate Node.js process. The server starts before the browser window loads, eliminating process management, IPC complexity, and port conflicts between app instances.

  2. Building a single portable .exe with no installer electron-builder with portable target bundles the entire app — Electron runtime, frontend assets, and backend with all its node_modules — into one self-contained executable. The extraResources config copies the backend folder into the packaged app's resource path at build time.

  3. Bypassing Windows symlink restriction during build electron-builder downloads winCodeSign (a macOS code-signing tool archive) which contains Unix symlinks. Extracting it on Windows without Developer Mode enabled fails with Cannot create symbolic link. Solved by patching binDownload.js in three separate app-builder-lib copies to return the pre-extracted cache path directly, and setting signAndEditExecutable: false to skip the rcedit step entirely.

  4. In-memory LRU cache with TTL and pattern invalidation Built a custom LRUCache class using a Map for O(1) get/set with LRU eviction when capacity is reached. Each entry carries an expiry timestamp. Supports glob-style pattern invalidation (e.g. clearing all articles:* keys when a reaction is posted) and automatic background purging of expired entries.

  5. Dev.to API proxy with exponential back-off retry All outbound Dev.to API calls go through a single Axios instance with interceptors. Failed requests (non-4xx) are retried up to N times with exponential delay (delay * 2^attempt). Per-request API key injection is handled via a custom _apiKey config field on the Axios request, stripped before the request is sent.

  6. JWT authentication with Supabase as the user store User registration and login use bcryptjs for password hashing and jsonwebtoken for stateless session tokens. Supabase acts as the database — the client is lazily initialised only when credentials are present in the environment, so the app degrades gracefully when the DB is not configured rather than crashing on startup.

  7. Response envelope compatibility between backend and frontend The frontend's api.js calls res.json() and uses the result directly as an array or object. Wrapping responses in { success, data } broke all article/tag rendering with "Unexpected response". Solved by keeping raw data as the response body and only wrapping error responses in { error: '...' } — the shape the frontend already checks.

  8. ReferenceError from temporal dead zone in module scope app.use(loggerHelper.logRequest) was placed at line 284 in server.js, but const app = express() was declared at line 991. JavaScript const is not hoisted — accessing it before its declaration throws ReferenceError: Cannot access 'app' before initialization. Fixed by moving all app.use() calls to after the app initialisation block.

  9. Layered architecture refactor from a single flat file The original server.js was a single 200-line file with all routes, auth, and caching mixed together. Refactored into config/, controllers/, services/, middleware/, routes/, and utils/ layers — each with a single responsibility — while keeping the embedded server entry point working identically for the Electron main process.

  10. pino transport misconfiguration causing silent log loss logger.helper.js configured a pino transport with colorize, translateTime, and other pretty-print options but omitted the required target: 'pino-pretty' field. Without target, pino ignores the entire transport block silently and writes nothing to stderr in development. The fix is either adding target: 'pino-pretty' or removing the transport block to fall back to plain JSON output.

Setup

Backend

cd backend
npm install
# Optional: add your Dev.to API key to .env
npm start

Frontend (dev)

cd frontend
npm install
npm start

Build .exe

cd frontend
npm run build
# Output: dist/DevToClient Setup *.exe

Features

  • Browse articles by tag
  • Search articles
  • Bookmark articles (stored locally)
  • Dark / Light mode toggle
  • Pagination

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors