Storing data with Keyv

Keyvopen in new window is a simple key-value store that works with multiple backends. It's fully scalable for sharding and supports JSON storage.

Installation

npm install --save keyv
1

Keyv requires an additional package depending on which persistent backend you would prefer to use. If you want to keep everything in memory, you can skip this part. Otherwise, install one of the below.

npm install --save @keyv/redis
npm install --save @keyv/mongo
npm install --save @keyv/sqlite
npm install --save @keyv/postgres
npm install --save @keyv/mysql
1
2
3
4
5

Create an instance of Keyv once you've installed Keyv and any necessary drivers.

const Keyv = require('keyv');

// One of the following
const keyv = new Keyv(); // for in-memory storage
const keyv = new Keyv('redis://user:pass@localhost:6379');
const keyv = new Keyv('mongodb://user:pass@localhost:27017/dbname');
const keyv = new Keyv('sqlite://path/to/database.sqlite');
const keyv = new Keyv('postgresql://user:pass@localhost:5432/dbname');
const keyv = new Keyv('mysql://user:pass@localhost:3306/dbname');
1
2
3
4
5
6
7
8
9

Make sure to handle connection errors.

keyv.on('error', err => console.error('Keyv connection error:', err));
1

For a more detailed setup, check out the Keyv readmeopen in new window.

Usage

Keyv exposes a familiar Mapopen in new window-like API. However, it only has set, get, delete, and clear methods. Additionally, instead of immediately returning data, these methods return Promises that resolve with the data.

(async () => {
	// true
	await keyv.set('foo', 'bar');

	// bar
	await keyv.get('foo');

	// undefined
	await keyv.clear();

	// undefined
	await keyv.get('foo');
})();
1
2
3
4
5
6
7
8
9
10
11
12
13

Application

Although Keyv can assist in any scenario where you need key-value data, we will focus on setting up a per-guild prefix configuration using Sqlite.

TIP

This section will still work with any provider supported by Keyv. We recommend PostgreSQL for larger applications.

Setup

const Discord = require('discord.js');
const Keyv = require('keyv');

const client = new Discord.Client();
const prefixes = new Keyv('sqlite://path/to.sqlite');
const globalPrefix = '.';
1
2
3
4
5
6

Command handler

This guide uses a very basic command handler with some added complexity to allow for multiple prefixes. Look at the command handling guide for a more robust command handler.

client.on('message', async message => {
	if (message.author.bot) return;

	let args;
	// handle messages in a guild
	if (message.guild) {
		let prefix;

		if (message.content.startsWith(globalPrefix)) {
			prefix = globalPrefix;
		} else {
			// check the guild-level prefix
			const guildPrefix = await prefixes.get(message.guild.id);
			if (message.content.startsWith(guildPrefix)) prefix = guildPrefix;
		}

		// if we found a prefix, setup args; otherwise, this isn't a command
		if (!prefix) return;
		args = message.content.slice(prefix.length).trim().split(/\s+/);
	} else {
		// handle DMs
		const slice = message.content.startsWith(globalPrefix) ? globalPrefix.length : 0;
		args = message.content.slice(slice).split(/\s+/);
	}

	// get the first space-delimited argument after the prefix as the command
	const command = args.shift().toLowerCase();
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

Prefix command

Now that you have a command handler, you can make a command to allow people to use your prefix system.

client.on('message', async message => {
	// ...
	if (command === 'prefix') {
		// if there's at least one argument, set the prefix
		if (args.length) {
			await prefixes.set(message.guild.id, args[0]);
			return message.channel.send(`Successfully set prefix to \`${args[0]}\``);
		}

		return message.channel.send(`Prefix is \`${await prefixes.get(message.guild.id) || globalPrefix}\``);
	}
});


 
 
 
 
 
 
 
 
 

1
2
3
4
5
6
7
8
9
10
11
12

You will probably want to set up additional validation, such as required permissions and maximum prefix length.

Usage

User07/06/2021
.prefix
Guide Bot07/06/2021
Prefix is .
User07/06/2021
.prefix $
Guide Bot07/06/2021
Successfully set prefix to $
User07/06/2021
$prefix
Guide Bot07/06/2021
Prefix is $

Next steps

Various other applications can use Keyv, such as guild settings; create another instance with a different namespaceopen in new window for each setting. Additionally, it can be extendedopen in new window to work with whatever storage backend you prefer.

Check out the Keyv repositoryopen in new window for more information.

Resulting code

If you want to compare your code to the code we've constructed so far, you can review it over on the GitHub repository here open in new window.