Back
Firestore - How to create document with id?
Discover bepo
Back

Firestore - How to create document with id?

Published on 
September 8, 2022

Exploring how Firestore store the data

In Firestore, the data you store inside the database is grouped inside a unit of storage called a Document.
In practice, it means that whenever you wanna add data to the database, you have to create a new document (or update an existing one).

Each Document is stored inside a bag, commonly called a Collection.
Contrary to PostgreSQL, where the tables need to exist prior to adding information into them, Firestore collection are born when you insert your first document, and cease to exist whenever you delete the last one.

Now, let me ask you a question: did you ever think about where your data was located inside your database?

If you are an aficionado of PostgreSQL or Mongo, it's very unlikely you ever thought about it, or at least you just know the data is inside the collection/table.

In Firestore, the location of the data is highly important because it's what guarantees its unicity.
The documents are indeed stored in the database with a path-based system, similar to how files are located on your computer.

A unique path can only refer to one or zero document (if the document does not exist).

The screenshot below shows an example of this path-based system in Firestore's interface.

Data is stored with a path-based system in Firestore


Note that we could also store another information in the path, using the identifier is just a convention (and a good practice).
The screenshot below show the same example where I decided to store the email as the path delimiter (note that the @ is url-escaped in the path):

Data stored with an email as a path delimiter

Consequently, in order to store the data, Firestore needs two information: the path and the data itself.

To refer to the place where you want to store the information, you can create a Reference.
Firestore has two types of reference: Document reference and Collection reference.

Document reference allows modifying or creating a document thanks to its known location in the database.
Collection reference allows acting on the collection, such as pushing new documents or browsing documents without worrying about their actual location.

const usersCollectionRef = db.collection("users");
const existingDocRef = db.collection("users/some-user-id");

const newDocRef = await usersCollectionRef.add({
    name: "Axel",
    country: "France"
});

// This is the same as calling add (in fact, add calls doc + set in the background)
const newDocRef2 = await usersCollectionRef.doc().set({
    name: "Maxime",
    country: "France"
});

console.log(newDocRef.id); // a generated id
console.log(newDocRef2.id); // another generated id
console.log(existingDocRef.id); // a developer generated id [some-user-id]

When you add a document to the collection just like in the snippet, an id is automatically generated for your document.
However, it is never available inside the data of your Firestore document, as you can only access it through the reference of the document, as shown above.

This is the main source of confusion for beginners expecting to find the id of their document alongside the other data

How to create document with id?

The problem we are trying to solve is quite simple once we have the correct mental model.
We would like to have the id available in the reference of the document AND inside the data themselves.

To do so, only one solution, you have to duplicate the information.

const usersCollectionRef = db.collection("users");
// I often use the NPM package nanoid to generate id
// See: https://github.com/ai/nanoid
const id = nanoid();

// doc takes a single parameter, the identifier to use in the path
const newDocRef = await usersCollectionRef.doc(id).set({
	uid: id,
  email: "axel@bepohq.com",
  role: "customer"
  createdAt: new Date(),
  updatedAt: new Date()
});

const doc = await newDocRef.get();
const userData = doc.data();

console.log(newDocRef.id); // our generated nanoid
console.log(userData.uid); // our generated nanoid
console.log(newDocRef.id === userData.uid); // true

This setup is really powerful when combined with Firebase Authentication. 
Indeed, it becomes really easy to look for information about a specific user, as we know exactly where to look for thanks to the Firebase id being in the path!

A user stored in Firestore thanks to the Firebase Authentication id generation
axel founder of bepo
by Axel
Founder of Bepo

Founder @bepohq building products with ♥️ I also share my journey as an indiehacker on social media.

Summary

Read our other articles

Continue reading the same serie