68

I've been scouring the Electron documentation to try and figure out how to persist data in an Electron app. For example, in iOS or OS X, you could use NSUserDefaults to store user settings and preferences. I would like to do something similar. How can I persist data in an Electron app?

Andrew
  • 227,796
  • 193
  • 515
  • 708
  • 1
    This would be a good module for your requirement. https://github.com/sindresorhus/electron-store – Tharanga Jul 28 '17 at 15:14
  • After building a custom solution that communicates over IPC to node to save a JSON file, I have realized that it may be much easier then the answers below give credit for. Note that the `app.getPath('userData')` path for each OS has a Local Storage, Session Storage and IndexedDB path (at least on Mac, not sure why my Windows does not have IndexedDB directory out of the gate). That said, I'm 95+% certain that local storage persists between app upgrades! And it is an incredibly simple option to implement. IndexedDB may help for blobs/etc. Worth considering! – ryanm Sep 10 '20 at 19:41

8 Answers8

33

NeDB is the only suggested or featured tool as an embedded persistent database for Electron by Electron, currently. - http://electron.atom.io/community/

It's also could be useful to store user settings if settings are complex.

Why NeDB could be a better solution on this case?

Embedded persistent or in memory database for Node.js, nw.js, Electron and browsers, 100% JavaScript, no binary dependency. API is a subset of MongoDB's and it's plenty fast. - NeDB

Creating or loading a database:

var Datastore = require('nedb')
  , db = new Datastore({ filename: 'path/to/datafile', autoload: true });
// You can issue commands right away

Inserting a document:

var doc = { hello: 'world'
               , n: 5
               , today: new Date()
               , nedbIsAwesome: true
               , notthere: null
               , notToBeSaved: undefined  // Will not be saved
               , fruits: [ 'apple', 'orange', 'pear' ]
               , infos: { name: 'nedb' }
               };

db.insert(doc, function (err, newDoc) {   // Callback is optional
  // newDoc is the newly inserted document, including its _id
  // newDoc has no key called notToBeSaved since its value was undefined
});

Finding documents:

// Finding all inhabited planets in the solar system
db.find({ system: 'solar', inhabited: true }, function (err, docs) {
  // docs is an array containing document Earth only
});

The list goes on...

Update - September 2019

As of 2019, this is no longer the valid answer. See the answers of @jviotti and @Tharanga below.

mertyildiran
  • 6,477
  • 5
  • 32
  • 55
  • 9
    Myself and others moved away from NeDB to [TeDB](https://github.com/tedb-org/teDB). The developer of NeDB abandoned the project. Others projects also were build on the side by other developers who did not want to join in the development of TeDB. – mjwrazor Sep 20 '17 at 15:37
  • @mjwrazor sure, by the time states of the libraries can change. Feel free to send **edits** to this answer when you think the alternative libraries become mature and better than NeDB. – mertyildiran Sep 20 '17 at 18:17
  • @mjwrazor As an update, the owner of Nedb considers nedb to be feature complete, so it's not abandoned. He will accept bug reports though. – Sayak Mukhopadhyay Sep 14 '18 at 13:32
  • @SayakMukhopadhyay he says that. But just accepts them. No fixes are being made. There is another developer who made a fork and is making the fixes though. – mjwrazor Sep 18 '18 at 14:18
  • 2
    This is no longer valid in 2019. Please update this answer. – RA. Aug 29 '19 at 03:49
  • 1
    @RA. I have updated my answer to tell people, this is no longer the valid answer. – mertyildiran Sep 01 '19 at 23:00
  • 1
    _Why_ is this no longer the valid answer? – David Poxon Sep 25 '20 at 08:23
  • I'd say this is still a viable option and very much a "valid answer", especially if you want Mongo-style queries on your persisted data. For simple storage of configuration or similar stuff, electron-store is probably easier to set up. – DasAntonym Feb 17 '21 at 17:33
  • NeDB on github from here https://github.com/louischatriot/nedb – Supun Sandaruwan May 07 '21 at 14:15
  • @DavidPoxon The NeDB Readme starts with a big warning "WARNING: this library is no longer maintained, and may have bugs and security issues. Feel free to fork but no pull request or security alert will be answered." – Stefnotch Jul 25 '22 at 15:39
26

There is an NPM module I wrote called electron-json-storage that is meant to abstract this out and provide a nice and easy interface to the developer.

The module internally reads/writes JSON to/from app.getPath('userData'):

const storage = require('electron-json-storage');

// Write
storage.set('foobar', { foo: 'bar' }).then(function() {

    // Read
    storage.get('foobar').then(function(object) {
        console.log(object.foo);
        // will print "bar"
    });

});
jviotti
  • 17,881
  • 26
  • 89
  • 148
  • 1
    jviotti, your example is showing Promises and on library callbacks are used. Thanks for sharing. – Vjeko Dec 01 '16 at 07:36
  • Hi @Vjeko, the library initially used Promise, however we dropped support for them because of some technical issues (see https://github.com/jviotti/electron-json-storage/pull/9). You can promisify the module with something like Bluebird's promisifyAll() to get the same effect. – jviotti Dec 04 '16 at 03:25
23

There is a nice module for storing user data in elecron. It's called electron-store.

Installation

$ npm install electron-store

Sample usage (copied from github page)

const Store = require('electron-store');
const store = new Store();

store.set('unicorn', '');
console.log(store.get('unicorn'));
//=> ''

// Use dot-notation to access nested properties
store.set('foo.bar', true);
console.log(store.get('foo'));
//=> {bar: true}

store.delete('unicorn');
console.log(store.get('unicorn'));
//=> undefined

This module has many features and there are many advantages over window.localStorage

Tharanga
  • 2,689
  • 1
  • 21
  • 18
12

Electron views are built with Webkit which gives you access to the web based localstorage api. Good for simple and easy settings storage.

If you need something more powerful or need storage access from the main script, you can use one of the numerous node based storage modules. Personally I like lowdb.

With most node storage modules, you will need to provide a file location. Try:

var app = require('app');
app.getPath('userData');
Max
  • 4,529
  • 1
  • 25
  • 29
  • Thanks, that's helpful. I'm also trying to wrap my head around where to put my model/controller logic. Do I write it in the same context as the browser window? Otherwise, how do I pass data from the electron js to the window js? – Andrew Feb 29 '16 at 04:04
  • 1
    Yes the browser window has full access to all that Electron offers. The main script is only really useful for starting up Electron, or if you're writing an app that primarily lives in the background. – Max Feb 29 '16 at 05:10
  • 3
    Electron feels a bit strange at first, because of the way it merges Node and the DOM. It just feels strange having full access to all of node from inside a browser window. – Max Feb 29 '16 at 05:12
11

You have multiple opions other than what mentioned in other answers.

Option 1 - SQL data

If you want to store data in an SQL databse then you can https://github.com/mapbox/node-sqlite3

Option 2 - Configuration data

Or if you are storing configurations, You can store directly in OS's userData storage.

 const electron = require('electron');
 const fs = require('fs');
 const path = require('path');

 const dataPath = electron.app.getPath('userData');
 const filePath = path.join(dataPath, 'config.json');

 function writeData(key, value){
   let contents = parseData()
   contents[key] = value;
   fs.writeFileSync(filePath, JSON.stringify(contents));
 }

 function readData(key, value) {
  let contents = parseData()
  return contents[key]
 }

 function parseData(){
   const defaultData = {}
   try {
    return JSON.parse(fs.readFileSync(filePath));
  } catch(error) {
    return defaultData;
  }
 }
Haseeb A
  • 5,356
  • 1
  • 30
  • 34
7

There is a module that gives simple methods to get and set json files to this directory, creates subdirectories if needed and supports callbacks and promises:

https://github.com/ran-y/electron-storage

Readme:

Installation

$ npm install --save electron-storage

usage

const storage = require('electron-storage');

API

storage.get(filePath, cb)

storage.get(filePath, (err, data) => {
  if (err) {
    console.error(err)
  } else {
    console.log(data);
  }
});

storage.get(filePath)

storage.get(filePath)
.then(data => {
  console.log(data);
})
.catch(err => {
  console.error(err);
});

storage.set(filePath, data, cb)

storage.set(filePath, data, (err) => {
  if (err) {
    console.error(err)
  }
});

storage.set(filePath, data)

storage.set(filePath, data)
.then(data => {
  console.log(data);
})
.catch(err => {
  console.error(err);
});

storage.isPathExists(path, cb)

storage.isPathExists(path, (itDoes) => {
  if (itDoes) {
    console.log('pathDoesExists !')
  }
});

storage.isPathExists(path)

storage.isPathExists(path)
.then(itDoes => {
  if (itDoes) {
    console.log('pathDoesExists !')
  }
});
JoniS
  • 89
  • 9
  • 11
  • 1
    This works great, especially for Electron 1.x applications that cannot use `electron-store`. The API is a little wonky with things like `isPathExists` but it does the job. – Joshua Pinter Jun 29 '18 at 05:07
2

You can go for Indexeddb, which is most likely suitable for client-side app needs due to:

  • Its built-in versioning mechanism. Client-side applications often face version fragmentation as users don't usually update to new version at the same time. So checking version of existing database and update accordingly is a good idea.
  • It's schemaless, which allows flexibility of adding more data to client's storage (which happen pretty often in my experience) without having to update database to a new version, unless you create new indices.
  • It support wide ranges of data: basic types as well as blob data (file, images)

All in all it's a good choice. The only caveat is that chromium cores may automatically wipe out indexeddb to reclaim disk space when storage is under strain if navigator.storage.persist is not set, or when the host machine is crashed leaving indexeddb in corrupted state.

vuamitom
  • 173
  • 2
  • 8
  • Care to add some references to `the navigator.storage.persist api does not work on Electron` ? – Ambroise Rabier Dec 18 '18 at 14:02
  • 1
    I don't have a reference. It is from my experience that sometimes the whole storage was wiped out on a client's machine had the host machine been crashed for some reason. Having said that, our use of indexeddb is pretty heavy compared to normal use. And I am updating my answer to clarify what I meant. Thank for pointing out. – vuamitom Dec 18 '18 at 15:10
  • 1
    Another problem I have faced with IndexedDB, in electron specifically is that it doesn't work in node.js, so you need to actually maintain it from your renderer process. – RA. Jul 23 '19 at 13:32
  • 1
    Exactly. For our application, data resides in the renderer process for main UI window. Any read and updates happen on that process. That way the app is usable both on the browser and on Electron runtime. – vuamitom Jul 24 '19 at 02:28
  • all the browser storage can be wiped out at any time, due to storage limits and LRU purging of stored data as @vuamitom implied above. This includes local storage (and session storage of course since it is purged right away) but also IndexDB. I find it's suitable for temporary offline storage, until you can get the local data synced with something else. – Appurist - Paul W Aug 31 '19 at 06:36
  • If this is for an Electron app, for reliability and consistency, I think you're better off letting the main process just use Node's filesystem api, or a library that uses that, to store your data locally, at least temporarily. And consider sending it to cloud storage somewhere once online again. – Appurist - Paul W Aug 31 '19 at 06:41
  • Just wanna mention the fact that PouchDB - https://pouchdb.com works seamlessly within browsers as well as Node so it could be used in preload scripts in Electron. Had quite some fun using it for some microservices. – Shape Apr 03 '22 at 16:34
0

There are a plethora of ways for data persistence that can be used in Electron and choosing the right approach depends essentially on your use case(s). If it's only about saving application settings then you can use simple mechanisms such as flat files or HTML5 Storage APIs, for advanced data requirements you should opt for large scale database solutions such as MySQL or MongoDB (with or without ORMs).

You can check this list of methods/tools to persist data in Electron apps

Ahmed
  • 1,008
  • 12
  • 15