GUN — primary keys in decentralized databases
This is a short article on how to translate the concept of primary keys from classical relational databasesystems like MySQL, into decentralized graph databases, such as GUN.
Short prefase about GUN
Gun is a decentralized peer-to-peer database system, that allow you to store information right there on the device, and then synchronize that information with others. It is really cool, because you don’t really need a server to create your app :)
This is just a short article because I just realized something (took me awhile, and perhaps it is just super obvious to you) but I think this will save you a lot of time down the road if you are just starting out using gun and decentralized graph databases.
Primary keys
In a classical database structure we usually have a table with information like first name, last name, phone, etc. To make sure that we have a way to uniquely identify a data entry we may give everything a unique ID. In SQL and the like you can auto-increment the key to make sure every item is automatically assigned an ID. This is what I did when first learning programming, but there are a lot of times when this is actually bad practice.
Instead what you should probably be doing is making some combination of the columns act as this unique identifier. E.g. first and last name could be combined to make sure that no user with the same first and last name is created. Or you could include also the phone in this key. The point is, that by doing it this way, we are already making some design decisions at the database layer, that may be helpful down the road to avoid certain types of errors.
Using keys in graph databases
In gun you can create lists of items by doing something like this:
gun.get("list-name").get("element-name").put(the_data);
// E.g if creating a list of expenses
gun.get("expenses").get("expense_id").put(expense_data);
For a long time I created lists of data by doing this:
const uuid = crypto.randomUUID();
const expense_data = {title: "new expense", ...}
gun.get("expenses").get(uuid).put(expense_data);
This is essentially equivalent to using an ID as a primary key instead of a combination of the data as a key. One issue with this approach is that if we need to delete the expense in the future (in decentralized systems this is done by overriding the value with null) then we need to figure out what the uuid is. Additionally we may get duplicate data. Consider a group with a certain number of members, that can join and leave the group. If we just use the naive approach the same member can join the same group multiple times. Here it would be beneficial to store a reference to the member, indexed by the member’s public key for instance.
In GUN we could store the reference to the user like so:
const group = gun.get("the-group") // somehow get a reference to the group.
const member_key = user.is?.pub;
group.get("members").get("member_key").put(user);
Using hashes
If you have a combination of data that need to act as the primary key, what you can do is create a hash of that data, and then use that as a key (or simply just concatenate the values if you really want i guess).
const expense_uuid = hash_data(expense.timestamp, expense.creator);
function hash_data(){
// ...
}
TLDR;
Figure out what constitutes the unique key of your data items, and let that be the key of your data. This way you ensure that no unwanted duplicate items are created, and that you can always find the key from the data.
Thanks for taking the time to read through this. Let me know if you found it helpful. Cheers!