Persistent user settings for Electron apps with async file reading, built-in JSON schema validation and RAM cached settings
npm install electron-json-settings-store
npm install --save electron-json-settings-store
`
or
`
yarn add electron-json-settings-store
`
$3
Electron 7.x or later (Node.js v.12.x)
Quick Usage
You only need to follow 4 simple steps to start using this module:
1. Import the library in the main process
`
const { ElectronJSONSettingsStoreMain } = require('electron-json-settings-store');
// or
import {ElectronJSONSettingsStoreMain, ElectronJSONSettingsStoreMainOptions} from 'electron-json-settings-store';
`
2. Declare the JSON schema for the settings (check the fastest-validator documentation) with the default values (the default key-values is always required for proper use of the module feature)
`
const schema = {
size: { default: 25, type: 'number', positive: true, integer: true, min: 10, max: 40 },
darkMode: { default: false, type: 'boolean' },
name: { default: 'World', type: 'string' }
};
`
3. Declare the class with your preferred settings and initialize the library to read the values from the JSON file
`
const config = new ElectronJSONSettingsStoreMain(schema, { watchFile: false, writeBeforeQuit: true });
// sync mode is easy to use, but experience programmers can use async mode
config.initSync();
console.log(config.getAll);
`
4. (Optional: If you need to access the settings on the renderer process) Declare the class with your preferred settings and initialize the library on the renderer preload script
`
const { ElectronJSONSettingsStoreRenderer } = require('electron-json-settings-store');
const config = new ElectronJSONSettingsStoreRenderer();
window.addEventListener('DOMContentLoaded', () => {
config.init().then(() =>
console.log(config.getAll)
);
});
`
Test the sample app
!Sample app screenshot
1. Download or clone this repository
2. Run npm install to install the dependencies
3. Run npm run test-electron and browse the sample-app folder
Main process
- The use of this module on the main process is mandatory. The main process acts like a centralized hub and communicates to the renderer processes
$3
$3
Property | Type | Default | Description
---------------- | -------- | ---------- | ----------------------
fileExtension | string | json | Extension of the config file.
fileName | string | config | Filename without extension.
filePath | string | app.getPath('userData')| Settings complete filepath. Storage file location. Don't specify this unless absolutely necessary! By default, it will pick the optimal location by adhering to system conventions.
prettyPrint | boolean | true | Save formatted (pretty print) JSON file. Disable only to save a few bytes/add some performance improvement.
validateFile | boolean | true | Settings will be validated after file reading. Note: the file is read on startup and on changed content (if watchFile option is true). Prevents the injection of invalid or harmfull config.
validate | boolean | true | Setting will be validated before is set. Prevents the injection of invalid or harmfull config.
defaultOnFailValidation | boolean | true | Return default value defined on schema if check validation failed. Recommended to prevent store invalid config.
watchFile | boolean | false | Watch File for changes. WARNING: Not recommended (feature in test).
writeBeforeQuit | boolean | false | Save settings before app quits. NOTE: uses sync writing process
* init()
* initSync()
* get(key)
* getAll
* getDefaults
* getDefault(key)
* getCompleteFilePath
* validate(key, value)
* set(key, value)
* setAll(data)
* setAndWriteSync(key, value)
setAndWrite(key, value) async*
* writeSync()
write() async*
* unset(key)
* has(key)
* reset()
* resetAndWriteSync()
resetAndWrite() async*
* disableFileWatcher()
$3
> Startup routine (asynchronous file operation).
`
config.init().then(()=> {
console.log(config.getAll);
} )
// or
await config.init();
`
$3
> Startup routine (synchronous file operation)
`
config.initSync();
console.log(config.getAll);
`
$3
> Get setting from cache. Return undefined if key was not found on cache and schema. WARNING: the JSON file was not read (the value is fectched from cache)
`
config.get('darkMode');
> false
`
$3
> Returns an object with the current settings
`
config.getAll;
> {"size": 2, "darkMode": false, "name": "World"}
`
$3
> Returns an object with the default settings defined on schema
`
config.getDefaults;
> {"size": 25, "darkMode": true, "name": "World"}
`
$3
> Returns the default settings defined on schema. Return undefined if key was not found on schema
`
config.getDefault('size');
> 25
config.getDefault('SomeInvalidKey');
> undefined
`
$3
> Get complete settings file path
`
config.getCompleteFilePath;
> c:\users\username\appdata\roaming\app\config.json
`
$3
> Validate key with schema
Returns the custom ElectronJSONSettingsStoreResult object.
`
const schema = {size: { type: 'number', positive: true, integer: true, default: 25, min: 10, max: 40 }}
config.validate('size', 12);
> {status: true, default: 25, errors: false}
config.validate('size', 50);
> {status: false, default: 25, errors: ["The 'size' field must be less than or equal to 40."]}
`
$3
> Sets the given key to cached memory.
WARNING: the file is not written. If you also want to write defaults to the file, you need to call writeSync() or write() method after.
Returns the custom ElectronJSONSettingsStoreResult object
`
// set a single key
config.set('debug', true);
// set multiple keys at once
config.set({debug: true, x: 5, y: -9});
// succefull operation ElectronJSONSettingsStoreResult returned object
config.set('size', 15);
> {status: true, default: 25, errors: false}
// if validate option is true
config.set('size', 50);
> {status: false, default: 25, errors: ["The 'size' field must be less than or equal to 40."]}
// if validate option is true and defaultOnFailValidation option is false (applies default)
config.set('size', 50);
> {status: true, default: 25, errors: 'Default setting was applied'}
`
$3
> Sets the given object to cached memory.
WARNING: the file is not written. If you also want to write defaults to the file, you need to call writeSync() or write() method after
`
config.setAll({debug: true, x: 5, y: -9});
`
$3
> Sets the given key to cached memory and write the changes to JSON file (sync file write operation).
`
config.setAndWriteSync('debug', true);
config.setAndWriteSync({debug: true, x: 5, y: -9});
`
$3
> Sets the given key to cached memory and write the changes to JSON file (async file write operation).
Returns the custom ElectronJSONSettingsStoreResult object
`
await config.setAndWrite('debug', true);
await config.setAndWrite({debug: true, x: 5, y: -9});
`
$3
> Write cached settings to file (sync file write operation).
Returns true if operation is success or a string with error
`
config.writeSync(); // success operation
> true
config.writeSync(); // in case of error
> 'Error: ENOENT: no such file...'
`
$3
> Write cached settings to file (async file write operation).
Returns true if operation is success or a string with error
`
await config.writeSync(); // success operation
> true
await config.writeSync(); // in case of error
> 'Error: ENOENT: no such file...'
`
$3
> Unsets the given key from the cached config.
Returns true if operation is success
`
config.unset('darkMode'); // success operation
> true
`
$3
> Checks if the given key is in the cached config.
Returns true if the key exists
`
config.has('darkMode');
> true
config.has('darkmode');
> false
`
$3
> Reset cached settings to default values defined in schema. WARNING: the file is not written. If you also want to write defaults to the file, you need to call writeSync() or write() method after.
`
config.reset();
`
$3
> Reset cached settings to default values defined in schema and write the changes to file (sync file write operation). Returns true if operation is success or a string with error
$3
> Reset cached settings to default values defined in schema and write the changes to file (async file write operation). Returns true if operation is success or a string with error
$3
> Unsets the given key from the cached config.
Returns true if operation success, false if error or watcher not active
`
config.disableFileWatcher(); // success operation
> true
`
Renderer process
- The use of this library in renderer process is optional but you cannot use this library only in renderer process. You must declare and init the library first in the main process.
- You always need to call the init method before start using this library.
- The majority of the methods are async functions who returns a promise because you need to wait for the main process to listen and reply to the IPC message.
$3
$3
Property | Type | Default | Description
---------------- | -------- | ---------- | ----------------------
emitEventOnUpdated | boolean | false | Emits event when settings is updated. Disable if you don't need to 'watch' settings change (can lead to a small performance improvment - less event listeners).
Listen the
updated event
If you enable emitEventOnUpdated option, an event is emitted when settings are updated. This option is a renderer process exclusive. You can listen to this event by using this code:
`
config.on('updated', settings => {
console.info('Settings updated! New Settings:');
console.table(settings);
// deal with the new cached settings object
});
`
* init()
* initSync()
* get(key)
* getAll
* getDefaults
* getDefault(key)
validate(key, value) async*
set(key, value) async*
setAll(data) async*
setAndWriteSync(key, value) async*
setAndWrite(key, value) async*
writeSync() async*
write() async*
unset(key) async*
* has(key)
reset() async*
resetAndWriteSync() async*
resetAndWrite() async*
disableFileWatcher() async*
$3
> Startup routine (async). Recommended method to not block the renderer process
$3
> Startup routine (sync). WARNING: Sending a synchronous message will block the whole renderer process until the reply is received, so use this method only as a last resort. It's much better to use the asynchronous version
$3
> Get setting from cache. Return undefined if key was not found on cache and schema. WARNING: the JSON file was not read (the value is fectched from cache)
`
config.get('darkMode');
> false
`
$3
> Returns an object with the current settings
`
config.getAll;
> {"size": 2, "darkMode": false, "name": "World"}
`
$3
> Returns an object with the default settings defined on schema
`
config.getDefaults;
> {"size": 25, "darkMode": true, "name": "World"}
`
$3
> Returns the default settings defined on schema. Return undefined if key was not found on schema
`
config.getDefault('size');
> 25
config.getDefault('SomeInvalidKey');
> undefined
`
$3
> Validate key with schema (this is a async function because I don't want to require validation module again on renderer process)
Returns the custom ElectronJSONSettingsStoreResult object.
`
const schema = {size: { type: 'number', positive: true, integer: true, default: 25, min: 10, max: 40 }}
await config.validate('size', 12);
> {status: true, default: 25, errors: false}
await config.validate('size', 50);
> {status: false, default: 25, errors: ["The 'size' field must be less than or equal to 40."]}
`
$3
> Sets the given key to cached memory.
WARNING: the file is not written. If you also want to write defaults to the file, you need to call writeSync() or write() method after.
Returns the custom ElectronJSONSettingsStoreResult object
`
// set a single key
await config.set('debug', true);
// set multiple keys at once
await config.set({debug: true, x: 5, y: -9});
// if validate option is true
await config.set('size', 50);
> {status: false, default: 25, errors: ["The 'size' field must be less than or equal to 40."]}
// if validate option is true and defaultOnFailValidation option is false (applies default)
await config.set('size', 50);
> { status: true, default: 25, errors: 'Default setting was applied' }
`
$3
> Sets the given object to cached memory.
WARNING: the file is not written. If you also want to write defaults to the file, you need to call writeSync() or write() method after
`
await config.setAll({debug: true, x: 5, y: -9});
`
$3
> Sets the given key to cached memory and write the changes to JSON file (sync file write operation on the main process, but this method uses an async operation to communicate with the main process, so the result of the function is a promise)
`
await config.setAndWriteSync('debug', true);
await config.setAndWriteSync({debug: true, x: 5, y: -9});
`
$3
> Sets the given key to cached memory and write the changes to JSON file (async file write operation on the main process).
Returns the custom ElectronJSONSettingsStoreResult object
`
await config.setAndWrite('debug', true);
await config.setAndWrite({debug: true, x: 5, y: -9});
`
$3
> Write cached settings to file (sync file write operation on the main process).
Returns true if operation is success or a string with error
`
await config.writeSync(); // success operation
> true
await config.writeSync(); // in case of error
> 'Error: ENOENT: no such file...'
`
$3
> Write cached settings to file (async file write operation on the main process).
Returns true if operation is success or a string with error
`
await config.writeSync(); // success operation
> true
await config.writeSync(); // in case of error
> 'Error: ENOENT: no such file...'
`
$3
> Unsets the given key from the cached config.
Returns true if operation is success
`
await config.unset('darkMode'); // success operation
> true
`
$3
> Unsets the given key from the cached config.
Returns true if operation success, false if error or watcher not active
`
await config.disableFileWatcher(); // success operation
> true
`
$3
> Checks if the given key is in the cached config.
Returns true if the key exists
`
config.has('darkMode');
> true
config.has('darkmode');
> false
`
$3
> Reset cached settings to default values defined in schema. WARNING: the file is not written. If you also want to write defaults to the file, you need to call writeSync() or write() method after.
`
await config.reset();
`
$3
> Reset cached settings to default values defined in schema and write the changes to file (sync file write operation on the main process). Returns true if operation is success or a string with error
$3
> Reset cached settings to default values defined in schema and write the changes to file (async file write operation on the main process). Returns true if operation is success or a string with error
The JSON Schema
- You need to define a JSON schema for the settings. I will recommend to check the fastest-validator documentation) to learn about the schema used.
- You must need to add the default value on each key
`
// invalid: no default value
const schema = {darkMode: {type: 'boolean' }};
// valid
const schema = {darkMode: {default: false, type: 'boolean' }};
// other samples
const schema = {
darkMode: {default: false, type: 'boolean' },
email: {default: 'john.doe@gmail.com', type: 'email },
id: { default: 2', type: 'number', positive: true, integer: true },
name: { default: 'john', type: 'string', min: 3, max: 255 },
mac: { default: '01:C8:95:4B:65:FE', type: 'mac' },
uuid: { default: '10ba038e-48da-487b-96e8-8d3b99b6d18a', type: 'uuid' },
url: { default: 'http://google.com', type: 'url' },
dob: { default: new Date(), type: 'date' }
};
`
> ### For more details please check this documentation)
File watching feature
- Watches for file changes (like if the user edits the JSON file with an external editor) and read the new settings.
- NOTE: This feature is in development, so be careful
The
ElectronJSONSettingsStoreResult object
- This object is returned when you call the validate or write methods
- For now I opted to not throw an error object because you can have more control with a custom object and it's easy to deal for begginer programmers, but in the future I can change this part.
`
// writing operation failed
config.setAndWriteSync('size', 20);
> {status: false, default: 25, default: 'Error ENOENT ...'}
// validation failed
config.validate('size', 50);
> {status: false, default: 25, errors: ["The 'size' field must be less than or equal to 40."]}
// validation passed (option validate is enabled)
config.set('size', 22);
> {status: true, default: 25, errors: false}
``