How to create unique codes for private groups in Firebase

July 21, 2022 (2y ago)

If you search Google with terms like "firestore unique 6 digit code", "firestore ensure usernames are unique" or "firestore unique field" you'll quickly find that it is not supported natively by Firebase. People will suggest doing things like slicing the document ID to get a unique code or even using the unique uuid, but that isn't great for users. Users need something more human readable.

So I have come up with a hacky little solution that will allow you to create unique ids or codes that are human readable in firestore.

Firestore data model

Let's say our application is a photo sharing application where users can share photos with private groups. Our data model will look like the below.

Collection: groups
  Doc: group
    name: 'group-1'
    groupCode: 'LVGHYE'
    members: [],

Collection: groupCodes
  doc: 'LVGHYE'
    group: group

collection: photos
  doc: photo
    group: group
    photoURL: 'some bucket address'

This data modal is going to allow us to check if a randomly generated code already exists in the groupCodes collection, if that code is unique we will add it as a doc to our groupCodes collection and link it to a group doc in our groups collection.

So how do we do that? Let's look first at a quick random code generator function.

Generate unique code for Firestore

To generate a unique code we need to do the client side, Firestore currently doesn't have a solution for this. However, the benefit of this is that we can make it human readable which is better for user experience than someone having to enter 4y5mJ*.

export const randomCode = () => {
  let code = "";
  const alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
  const codeLength = 6;

  for (let i = 0; i < codeLength; i++) {
    code += alpha.charAt(Math.floor(Math.random() * alpha.length));
  }

  return code;
};

The above function will generate a unique 6 digit capitalised code. We are going to use this function to generate a unqiue code at the time the user needs it.

So let's look at how we can ensure this code is unique in Firestore.

Ensuring unique field in firestore

The chances of the code we generate in the randomCode() function not being unique are quiet low since 26^6 generates 300+ million unique codes. However, we want to be 100% sure that a user will get a unique code. Could you imagine if a user got access to someones private photo's, that would be a very bad experience for those involved.

To ensure the code is unique we will run a checkCodeUnique function at the time a user goes to create a new group.

import { db, auth } from "../../firebase";
import { collection, getDocs } from "firebase/firestore";

let groupCode = randomCode();

const checkCode = async () => {
  const docRef = doc(db, "groupCodes", groupCode);
  const docSnap = await getDoc(docRef);

  if (docSnap.exists()) {
    console.log("Document data already exists");
    groupCode = randomCode();
  } else {
    console.log("No such document! with ", groupCode);
    await setDoc(doc(db, "groupCodes", groupCode), {
      group: "",
    });
  }
};

Let's go through what is happening above. First, we call our randomCode() function to set a groupCode, this happens as soon as the user access this component.

Next we setup our checkCodeUnique function. The first thing we do in this function look for a doc with the randomly generated groupCode in our groupCodes collection (that sentence is a monthful). Then we conditionally conditionally check if it exists. If it does exist we will regenerate the random code. If it doesn't exist we will then create a new doc in our groupsCode collection and set the id to be the randomly generated groupCode so no one else can use it again in the future.

So we have a way for ensuring our codes are unique, now we need to link the code to group and vice-versa.

Creating private groups

Now we have a unique code as the id of our groupCode doc in our groupCodes collection, we need to assign that group code to a group. This is a one-to-one relationships, as one groupCode can only have one group.

Below is the code to do this, have a look over it and I'll explain what is happening below.

import { collection, getDoc, doc, addDoc, setDoc } from "firebase/firestore";

const writeNewGroup = async () => {
  await checkCode();

  try {
    let groupRef = await addDoc(collection(db, "groups"), {
      name: groupName,
      owner: user.uid,
      groupCode: groupCode,
      members: [user.uid],
    });

    await setDoc(doc(db, "groupCodes", groupCode), {
      group: groupRef.id,
    });
  } catch (e) {
    console.error("Error adding document: ", e);
  }
};

So the first thing we do in this function is we call our checkCode() function to ensure we have a unique code for the group. The next thing we do is we create a new group doc. In this group doc we set the field groupCode to the unique code we just created. Next, we set the group field in the groupCode doc to the id of the group doc we just created which sets up our one-to-one relationship.

That is all there is to it, you now have a nice little hacky way to create unique, private, human readable codes for your application.

//TODO: // I might want to add some images of what the firestore setup looks like // change the setDoc method to and update doc method