From the first moment where I started using the Discord.js library, one thing in it fascinated me: "Collections". Discord.js Collections are a Map structure from JavaScript on top of which a bunch of useful methods are added, most notably those from JavaScript's Array structure.
Things like map, filter, reduce, find, sort... they made Maps so useful, so much more powerful, that I admired their design. It struck me at one point, that if such a structure were to be separated from Discord.js and perhaps made to be saved in a database, it would make interacting with data so easy that even a child could do it.
So when I started getting seriously into bot that required their own data to be saved, I turned to Amish Shah (Hydrabolt) and I said to him, I said "Listen, buddy, can I extract Collections and publish them as a separate module? That'd be awesome!" and Amish replied, like the great guy he is, "uhhhh sure, why not?"
And so, in May 2017, the djs-collection
module was born. It was a simple thing, just straight-up lifted from Discord.js' code (not illegally, mind you, I retained all proper licenses and credits to Hydrabolt!). The following month, I added persistence (saving to a database) to it and published djs-collection-persistent
, which then became my own defacto way to save data to a database.
But... let's be honest, npm install --save djs-collection-persistent
is a mouthful to type out. Plus, I was realizing that having those two as separate modules meant I had to update them separately and ensure they still worked individually... So at one point, I decided it was time to merge them.
Releasing a single repository meant that I could now change the name, because of the aformentioned "omg mile-long name" problem, and just the overall annoyance of writing it. So I settled on "well, they're enhanced maps, let's call it Enmap!". A quick search revealed Enmap's only other apparent meaning was that it was the name of a satellite, and I was guessing no one would confuse the two.
But I didn't want to force enmap users to have persistence, so at the same time I actually created a separate module called enmap-level
, which controlled the database layer and was completely optional. These modules I called Providers since, obviously, they provided data persistence and and API.
Enmap 0.4.0 was released at the beginning of October 2017, and since then has grown into a fairly solid module used by tens of thousands of people across the world, not only in discord.js bots but also in other projects built with Node. Its solidity and simplicity makes it the ideal storage for simple key/value pairs, and I'm extremely proud to have made it.
At the moment of writing this (2018-09-02) Enmap has been downloaded over 32,000 times and is growing by the minute with almost 10,000 downloads in August alone!
It's been a year now since I last wrote this post. And in case you were wondering, growth hasn't stopped! In fact, it's quite accelerated. One big change that I made was to go back to a single locked-in database provider, which I describe in Why SQLite Only?
Other than that, and adding some new features due to the switch to better-sqlite3, the main event is that just las month I reached a whopping 500,00 downloads for Enmap. Yes, that's a half million downloads for my little useful module that I started making for myself and ended up being useful for so many.
So one of the major changes from Enmap 3 to 4 is the removal of Providers. Providers were something I've had since Enmap 1.0 (when I converted from djs-collections-persistent), and had 2 advantages (2 reasons to have them in the first place).
It enabled supporting more databases, not only one. This gave more power to users, and, I thought, more capabilities.
It separated the memory enmap (non-persistent) from the database layer, so installing enmap didn't require installing sqlite.
But, after a year of updating Enmap, I realized that I'd painted myself in a corner with Providers. There came to light that there were multiple limitations to providers:
Features were limited to the "lowest common denominator", whatever was available to all providers. For instance, better-sqlite3 is a synchronous module that's nonblocking (which is a magical thing, really). But since all other providers required promises, then I had to use sqlite as a promise module.
Maintaining multiple providers is hard work. Every new feature would require updating all the providers (5 at this time), and there were many requests to create new providers which is an annoying, sometimes complicated task that adds even more work in the future.
There were features I wanted that simply weren't possible, physically, with the providers (like the fetchAll/autoFetch options).
In addition, the advantages became lesser with time. I realized most people were using leveldb at first, then most switch to sqlite when I updated guides to use that provider. Essentially, most people use whatever they're told to use. So, just forcing one database wasn't that much of an issue and didn't affect the majority of users.
Also, most people did use enmap with persistence, and those that didn't... well, most users have enmap to use with discord.js bots in the first place which gives them Collection - almost the same as a non-persistent enmap.
The reasoning behind removing all other providers and keeping sqlite was for specific features and capabilities inherent to the module I'm using, better-sqlite3.
better-sqlite3 is, as I mention, synchronous , which means, no callbacks, no promises. Just straight-up "make a request and it does it before the next line". No more need for "waiting" for things, resolving promises, etc.
The sync nature of better-sqlite3 means I can add an autoFetch feature. I can simply say "If the key isn't there, try to get the data", without requiring the user to resolve a promise. This is awesome.
By the same token, I can also add simple things like "get all keys in the database" using a getter. This means you can do enmap.indexes
and this is actually querying the database seamlessly without the user really knowing it does that. Same for enmap.count
and other features I'm planning.
So overall, I'm happy with my decision. It gives me more power, it gives users more features, and the people affected by the removal of the other providers are few and far between. Hell, enmap-pgsql has less than 1000 downloads on npm which is mostly mirrors and caches. It showed me that provider was pretty useless in the first place.
I recognize that some people might want to use enmap and can't use sqlite. This is for many valid reasons, for example using it on heroku which doesn't support sqlite and leveldb. For those users, I'm keeping the providers open for maintenance. If someone wants to maintain and update the V3 branch, or even fork the entire system and maintain it under a new name, I have no issue with that (assuming licenses are properly kept). I'll accept PRs on all enmap repositories, including backporting some features and adding new ones.
I'm also keeping the V3 docs in this gitbook so it can be maintained through gitbook and PRed on github.
You can still install any provider as you would before, and install enmap using npm i eslachance/enmap#v3
for the version 3 branch that will remain.
Want to help out? Join the Discord, fork and PR the Github for enmap, contribute!