localForage - Documentation

Introduction

What is localForage?

localForage is a fast and simple asynchronous JavaScript library for persisting key-value pairs in the browser’s localStorage, IndexedDB, and WebSQL. It provides a unified API across these different storage mechanisms, abstracting away the complexities of managing each individually. This allows developers to write code that works consistently across various browsers and devices, without needing to worry about the underlying storage technology. localForage automatically determines the best available storage mechanism for the user’s browser and gracefully falls back to others if necessary.

Why use localForage?

Key Features

Supported Browsers and Environments

localForage supports most modern browsers, including:

While it primarily targets browsers, it can also be used in Node.js environments with the appropriate adapter. However, direct Node.js support is not a core feature; using it would require a suitable adapter/shim for filesystem or similar storage.

Installation

localForage can be installed using npm or yarn:

npm install localforage
# or
yarn add localforage

For use in a browser environment without a package manager, you can include the library via a <script> tag. Download the minified version from the project’s distribution and include it in your HTML file:

<script src="path/to/localforage.min.js"></script>

After installation, you can start using the localForage API in your JavaScript code.

Core API

setItem(key, value)

Stores a value associated with a given key. The key must be a string. The value can be any valid JavaScript data type, though complex objects will be serialized (typically using JSON.stringify). The method returns a Promise that resolves when the operation is complete. It rejects with an error if the operation fails (e.g., due to storage limitations or quota issues).

localforage.setItem('myKey', 'myValue').then(() => {
  console.log('Value stored successfully!');
}).catch(err => {
  console.error('Error storing value:', err);
});

getItem(key)

Retrieves the value associated with a given key. The key must be a string. The method returns a Promise that resolves with the stored value. If the key doesn’t exist, it resolves with null. It rejects with an error if the operation fails.

localforage.getItem('myKey').then(value => {
  console.log('Retrieved value:', value);
}).catch(err => {
  console.error('Error retrieving value:', err);
});

removeItem(key)

Removes the value associated with a given key. The key must be a string. The method returns a Promise that resolves when the operation is complete. It rejects with an error if the operation fails (e.g., the key doesn’t exist).

localforage.removeItem('myKey').then(() => {
  console.log('Value removed successfully!');
}).catch(err => {
  console.error('Error removing value:', err);
});

clear()

Removes all key-value pairs from the storage. Returns a Promise that resolves when the operation is complete and rejects if an error occurs.

localforage.clear().then(() => {
  console.log('Storage cleared successfully!');
}).catch(err => {
  console.error('Error clearing storage:', err);
});

key(n)

Returns the nth key in the storage. n is a zero-based index. Returns a Promise that resolves with the key (as a string) at the specified index or null if the index is out of bounds. It rejects with an error if an error occurs during the operation.

localforage.key(0).then(key => {
  console.log('First key:', key);
}).catch(err => {
  console.error('Error getting key:', err);
});

length()

Returns the number of key-value pairs in the storage. Returns a Promise that resolves with the number of items. Rejects with an error if an error occurs.

localforage.length().then(length => {
  console.log('Number of items:', length);
}).catch(err => {
  console.error('Error getting length:', err);
});

iterate(callback)

Iterates over each key-value pair in the storage, calling the provided callback function for each one. The callback function receives the value and the key as arguments. The method returns a Promise that resolves when all items have been iterated, or rejects if an error occurs.

localforage.iterate((value, key) => {
  console.log('Key:', key, ', Value:', value);
}).then(() => {
  console.log('Iteration complete');
}).catch(err => {
  console.error('Error during iteration:', err);
});

keys()

Returns a Promise that resolves with an array of all keys in the storage. Rejects with an error if an error occurs.

localforage.keys().then(keys => {
  console.log('All keys:', keys);
}).catch(err => {
  console.error('Error getting keys:', err);
});

Advanced Usage

Working with different storage drivers

localForage prioritizes IndexedDB, falling back to WebSQL (deprecated) and then localStorage. You can explicitly specify a driver if needed, though this is generally unnecessary. To force a specific driver, use the localforage.setDriver() method before any other localForage operations. The available drivers are: localforage.LOCALSTORAGE, localforage.INDEXEDDB, and localforage.WEBSQL.

// Force the use of localStorage
localforage.setDriver(localforage.LOCALSTORAGE).then(() => {
  // Your localForage operations here
}).catch(err => {
  console.error('Error setting driver:', err);
});

Note that forcing a specific driver might lead to reduced functionality or performance if that driver isn’t supported or suitable for the data being stored.

Driver Configuration

While automatic driver selection is usually sufficient, you can fine-tune the behavior of individual drivers. This is particularly useful for IndexedDB where you can customize database name and version. Configuration is done through the config option when initializing localForage (or using localforage.config() before any other operations).

localforage.config({
  name: 'myDatabase', // IndexedDB database name
  version: 1,         // IndexedDB database version
  storeName: 'keyvaluepairs', // IndexedDB store name
  description: 'My localForage database' // IndexedDB database description (optional)
}).then(function() {
    // Your code here
});

Refer to the localForage documentation for the complete list of configurable options for each driver. Incorrect configuration can lead to errors.

Error Handling

All localForage methods return Promises, allowing for robust error handling using .catch(). Errors can occur due to various reasons, such as storage quota limitations, browser restrictions, or driver-specific issues. Always include error handling in your code to gracefully handle potential failures.

localforage.setItem('myKey', 'myValue').then(() => {
  // Success
}).catch(err => {
  console.error('Error:', err); // Handle error appropriately
  // For example, display a user-friendly error message, retry, or fallback to alternative storage.
});

Asynchronous Operations and Promises

localForage utilizes Promises extensively for all its operations. This ensures that your application remains responsive, preventing blocking while waiting for storage operations to complete. Always use .then() to handle successful operations and .catch() for error handling. Avoid using .done() which is deprecated.

Batch Operations

For multiple setItem calls, consider using a single Promise.all to improve performance. This reduces the overhead of multiple asynchronous calls.

const promises = [
  localforage.setItem('key1', 'value1'),
  localforage.setItem('key2', 'value2'),
  localforage.setItem('key3', 'value3')
];

Promise.all(promises)
  .then(() => console.log('All items set'))
  .catch(error => console.error('Error setting items:', error));

Managing Storage Space

localForage relies on the browser’s built-in storage mechanisms. If storage space is limited, exceeding the quota will result in errors. To manage storage space:

Performance Considerations

For optimal performance:

Migrating from other storage solutions

Migrating from other storage solutions involves extracting data from the old system and importing it into localForage. The exact steps depend on the source storage solution but generally include:

  1. Data Extraction: Retrieve all data from the existing storage mechanism.
  2. Data Transformation (if needed): Convert data to a format compatible with localForage (usually JSON).
  3. Data Import: Use localforage.setItem() to insert the transformed data into localForage.
  4. Verification: Verify that the data has been migrated successfully.
  5. Old System Removal: After successful migration, remove the old storage mechanism if no longer needed. This step needs careful consideration, potentially involving a backup to prevent data loss.

Examples and Use Cases

Simple Key-Value Storage

This example demonstrates basic key-value storage using setItem and getItem.

localforage.setItem('username', 'john_doe').then(() => {
  localforage.getItem('username').then(username => {
    console.log('Username:', username); // Output: Username: john_doe
  });
});

This stores the string “john_doe” under the key “username” and then retrieves it. Error handling should be added for production use.

Storing Complex Data Structures

localForage handles complex data structures by serializing them using JSON.stringify. Ensure that your data is serializable before storing it.

const user = {
  name: 'Jane Doe',
  email: 'jane.doe@example.com',
  age: 30
};

localforage.setItem('user', user).then(() => {
  localforage.getItem('user').then(retrievedUser => {
    console.log('Retrieved User:', retrievedUser); // Output: Retrieved User: {name: "Jane Doe", email: "jane.doe@example.com", age: 30}
  });
});

Note that the retrieved retrievedUser will be a JavaScript object, not a JSON string.

Caching Data

localForage is well-suited for caching data fetched from a remote server. Store fetched data with a key representing the data’s source and update/remove it as appropriate.

function fetchData(url) {
  return fetch(url)
    .then(response => response.json())
    .then(data => {
      localforage.setItem('cachedData-' + url, data);
      return data;
    });
}

function getCachedData(url) {
  return localforage.getItem('cachedData-' + url)
    .then(data => data || fetchData(url)); // Fetch if not cached
}

getCachedData('/api/users')
  .then(data => console.log('Users:', data));

This example checks for cached data before making a network request.

Building Offline-First Applications

Offline-first applications rely on local storage to provide functionality even without an internet connection. localForage enables this by allowing you to store application data locally and synchronize it with a server when connectivity is restored.

// Store data locally when creating a new item.
function createItem(item) {
    localforage.setItem('item-' + item.id, item).then(() => {
        // Optionally, queue the item for synchronization with a server.
    });
}

// Load data on application startup.
localforage.keys().then(keys => {
  const promises = keys.map(key => localforage.getItem(key));
  Promise.all(promises).then(items => {
    // Process items and update the UI.
  });
});

This showcases storing items locally, loading them at startup, and suggesting a mechanism to synchronize with a server later.

Integrating with other libraries

localForage integrates well with other JavaScript libraries. You can use it in conjunction with frameworks like React, Angular, Vue.js, and others. It also pairs well with libraries for handling data synchronization or background tasks. The integration usually involves using localForage’s API within the context of the other library’s methods and lifecycle. For example, you might use localForage to persist application state in a React application within a component’s useEffect hook or store data used by a Vue.js application within a Vuex store. No special integration code is typically required; you simply use localForage API calls within your existing application code.

Security Considerations

Data Protection

localForage itself does not provide encryption or any specific mechanisms for protecting data at rest. The security of data stored using localForage depends entirely on the browser’s security model and the security of the overall application. Sensitive data should never be stored directly using localForage without appropriate encryption. Consider using a library that provides encryption capabilities before storing sensitive data (e.g., encrypting the data before storing it in localForage and decrypting it upon retrieval). The responsibility for securing sensitive data rests entirely with the application developer. The browser’s storage mechanisms (localStorage, IndexedDB) are subject to the same security implications as other browser-based storage mechanisms.

Cross-Origin Resource Sharing (CORS)

CORS is not directly relevant to localForage because it operates within the context of a single origin (the website or application that’s using it). localForage only interacts with the browser’s storage mechanisms available to that origin. Data stored in localForage using one origin is not accessible to scripts from a different origin. This inherent browser-level security prevents cross-origin access.

Protecting against XSS attacks

Cross-Site Scripting (XSS) attacks are a significant web security risk. localForage itself does not directly prevent XSS attacks; rather, secure coding practices are crucial. XSS vulnerabilities arise from improperly handling user-supplied data. When using localForage, ensure that you sanitize and escape any user-provided input before storing it. Never directly insert user-supplied data into keys or values in localForage without appropriate validation and sanitization. Improper handling of data could allow an attacker to inject malicious scripts into your application’s data, potentially leading to compromised user data or other harmful consequences. Use a robust input validation and sanitization library to mitigate the risk of XSS vulnerabilities within the broader application context, including data handled by localForage.

Contributing

We welcome contributions to localForage! Whether it’s fixing bugs, adding features, or improving documentation, your help is valuable. Here’s how to get started:

Setting up the Development Environment

  1. Clone the repository: Clone the localForage repository from GitHub:

    git clone https://github.com/localForage/localForage.git
  2. Install dependencies: Navigate to the project directory and install the necessary packages using npm or yarn:

    npm install
    # or
    yarn install
  3. Run the development server (optional): A development server is usually available (check the project’s README) to aid in development and testing. Start it using the appropriate command. This might involve a command like npm start or yarn start.

Coding Style Guide

Follow the existing coding style in the project. Consistency is key. Pay attention to:

Refer to any existing style guides (e.g., a .editorconfig file or a CONTRIBUTING.md file) within the repository for more specific guidelines.

Testing

localForage has a comprehensive test suite. Before submitting a pull request, ensure that your changes don’t introduce regressions. Run the tests using the commands specified in the project’s README file. This typically involves using a command such as npm test or yarn test. The test suite often covers different browsers and edge cases. Addressing any test failures is crucial before merging your code.

Submitting Pull Requests

  1. Create a branch: Create a new branch for your changes:

    git checkout -b my-feature
  2. Make your changes: Implement your changes, ensuring code quality and thorough testing.

  3. Commit your changes: Write clear and concise commit messages explaining your changes.

    git add .
    git commit -m "Fix: Resolved issue #123"
  4. Push your branch: Push your branch to the remote repository:

    git push origin my-feature
  5. Create a pull request: Create a pull request on GitHub, clearly describing the changes you made and addressing any relevant issues. Provide a clear explanation of the problem you’re solving and how your changes address it. Also, mention any specific test cases that were added or modified. The maintainers may request changes; address these promptly and push updated commits to your branch.

Remember to follow the project’s contribution guidelines (if any) for more specific instructions.

Appendix

Glossary of Terms

Frequently Asked Questions (FAQ)

Release Notes

Release notes would typically be found in a separate file or section on the project’s website or repository. They detail changes, bug fixes, and new features in each release version of localForage.

License

localForage is typically licensed under either the MIT License or a similar permissive open-source license. Refer to the LICENSE file in the project’s repository for the exact terms and conditions of the license.