I've just finished my third cup of coffee for the day, encouraging the thought, "There is no better time to write about Unstorage than now." So, here I am, writing about Unstorage.
Unstorage is an asynchronous Key-Value storage API packed with convenient features such as multi-driver mounting, built-in servers, and state snapshots. That's not all; Unstorage is designed with a tiny core, enabling compatibility with various ecosystems - the Browser, Node.js, Bun, Deno, Workers, etc.
The list of features goes on and on. It automatically handles JSON value serialization and deserialization and offers options for working with snapshots and metadata. It features default in-memory storage and tree-shaking utils to minimize clutter. As if these utilities weren't enough, Unstorage also includes HTTP Storage with a built-in server.
Even with all these options, it has a straightforward interface that is easy to learn and use. In this article, I'll be covering the basics of Unstorage and how you can use it in your projects. I also plan to write a follow-up article covering more advanced features.
Installing Unstorage is as simple as running the following command:
Alternatively, you can use Yarn or pnpm:
With Unstorage installed, let's create a new storage and do some basic operations with it:
As you can see, it's straightforward to get started with Unstorage. The
createStorage function returns a new storage instance that can be used to perform various operations on the storage. By default, Unstorage uses in-memory storage, but you can mount additional drivers to back your data to a filesystem, a database, or even a remote server. I will cover this in more detail later.
Also, remember that the storage instance is asynchronous, so all operations return a promise. Remembering this is a crucial point when working with Unstorage.
Detailed Overview of Unstorage's Interfaces
Unstorage provides an array of methods to interact with the storage. You can get a detailed overview of these by visiting their API documentation, but I'll cover the most important ones here with some examples.
For our example, let's assume we're developing an application that requires storing user data. We introduce
unstorage to handle all our storage-related operations.
Storing User Data
For storing user data, we use the
setItem function as follows:
We're using a key,
user:details, to store user data. This pattern is commonly used in Unstorage to namespace keys. You can use any key you want, but it's recommended to use a namespace to avoid conflicts with other keys.
Notice how we passed an object as the second argument to
setItem. Unstorage automatically serializes the value to JSON before storing it. This feature is convenient because it saves us from manually serializing the value.
Updating User Data
Say we want to update the user's email address. We will use the
setItem function again to accomplish this:
Retrieving User Data
Now, to retrieve the stored user data, we use the
Checking If An Item Exists
We can check if a key exists in our storage using the
Removing User Data
To remove a user's data, we use the
Getting All Keys
Suppose we want to get the keys of all the users stored in our system. We use the
getKeys function for this:
Clearing User Data
Now, if, for some reason, we want to remove all stored user data, clear everything, and start afresh, we use the
Be careful when using this function, as it will remove all data from the storage.
There are various other functions that you can use to interact with the storage. For example, you can use
setItems to get and set multiple items at once. You can also use
setItemRaw to access and update the value of a key in its raw format, without deserialization to a primitive type. This feature is particularly useful for binary data.
Additionally, Unstorage provides commands to add, get, and update metadata related to a specific key, which is useful if, for example, you want to store the creation time of a file or any other custom data. You can also remove custom metadata related to a key.
Working with Types
You might want to ensure correct data types when storing or retrieving data. You can use
unstorage with TypeScript to define types at the storage or individual key levels.
For example, let's define a new storage that only stores strings:
You can, of course, define types for individual keys as well:
By default, Unstorage uses in-memory storage. However, you can also mount additional drivers to back your data up to a filesystem, a database, or even a remote server. This feature allows you to combine multiple storage backends into one.
Unstorage provides several built-in drivers that you can use out of the box. Additionally, it's easy to create your custom drivers. Let's look at how we can mount a driver to our storage.
Unstorage's most powerful feature is its ability to mount different storage mechanisms at different mount points in a Unix-like fashion. When you perform operations on a
key that starts with a mount point, instead of using the default in-memory storage, the mounted driver for that mount point will be utilized.
Let's see this feature in action by mounting the filesystem driver to store some of our user data. First, we import
Now, we create default in-memory storage that we will use around our application:
Next, say we want to store some of our data in the filesystem. We can mount the filesystem driver to a mount point and use that mount point to store our data:
In the example above, we've mounted the filesystem at "/fs" and specified the base directory as "./userData". Now, storing any data using a key that starts with "/fs" will be stored in the filesystem instead of the in-memory storage.
You can mount multiple drivers to different mount points and use them to store data. For instance, say we'd also like to mount a Redis driver to store some of our data in Redis:
Now, storing any data using a key that starts with "/redis" will be stored in Redis instead of the in-memory storage.
Mounting at Root
You can also mount a driver at the root of the storage. This is useful if you want to use a driver for all your data. For example, we're using Unstorage in the browser and want to store all our data in the browser's LocalStorage. We can mount the LocalStorage driver at the root of the storage:
Now, any data that we store will be stored in the browser's LocalStorage.
Working with Namespaces
As I mentioned earlier, it's good practice to use namespaces when storing data in Unstorage. This is because Unstorage stores all data in a flat structure, so if you don't use namespaces, you might have conflicts between keys.
For example, we have used the namespace
user:details to store our user data. We can use the same pattern for other data as well, such as
user:posts to keep the user's posts,
user:comments to store the user's comments, and so on.
During development, writing the namespace every time can be cumbersome, so Unstorage provides a utility function called
prefixStorage that can be used to create a new storage instance with a prefix. Let's see how we can use it:
Now, we can use the userStorage just like we used the storage instance earlier, but all keys will be prefixed with "user:".
As you can see, Unstorage is a powerful tool that can be used to build complex applications. It provides a simple interface that's easy to learn and use, and it also provides some really powerful features that can be used to build complex applications.
We only covered the basics of Unstorage in this article, but there's a lot more to it. For instance, we didn't cover the
watch function that can be used to watch for changes in the storage. We also didn't cover the
snapshot function that can be used to create a snapshot of the storage and restore it later. You can learn more about these functions and other features of Unstorage by visiting their API documentation.
In the next article, I'll go deeper into Unstorage and combine a few of its features to build a practical setup for syncing data between a browser and a server. Stay tuned!
Interested in LogSnag?