Understanding Browser Data Storage in Node.js Applications

This is how user authentication typically works:

First, the user will fill out a form, providing the necessary credentials. The credentials will be transferred to a controller in the backend.

The controller will try to match the credentials to the ones stored in the database. If the match is successful, the user is successfully authenticated. In this case, the controller will send a piece of user information back to the frontend, which will be stored inside the browser.

As long as this information exists, the user is considered authenticated and will not be required to provide credentials again.

So, before we can talk about how to create a user authentication system, you must first understand how to store a piece of data inside the user's browser, and that's what we are going to cover in this lesson.

Cookies

There are mainly four ways to store data in the browser: cookies, localStorage, sessionStorage, and IndexedDB.

A cookie is a small piece of string data stored in the user's browser. It is primarily used for user authentication and personalization. You can set a cookie using the document.cookie setter like this:

javascript
1document.cookie = "<name>=<value>";

The cookie name is unique. If you set multiple cookies with the same name, only the latest one will be preserved.

javascript
1document.cookie = "user=John";
2document.cookie = "user=Doe";
3
4console.log(document.cookie);
text
1user=Doe
Reminder: We are now working in the browser environment, so run the above code demo in the JavaScript Console.

Setting expiration date

Besides the name, you can also set attributes for the cookie.

javascript
1document.cookie = "<name>=<value>; <attribute>=<value>";

For example, if you want the cookie to expire at a certain time, you can set an expires attribute.

javascript
1document.cookie = "user=John; expires=Tue, 19 Jan 2038 03:14:07 GMT";

The date must be set in this exact format, so it is best to use the date.toUTCString() method.

javascript
1const date = new Date("2025-08-25T12:10:26+06:00");
2
3let expires = "expires=" + expirationDate.toUTCString();
4document.cookie = "user=John; " + expires;

Alternatively, you could use the' max-age' attribute to specify how long you want the cookie to be valid instead of setting an expiration date directly.

javascript
1document.cookie = "user=John; max-age=3600";

In this case, the cookie will be valid for 1 hour (3600 seconds).

Of course, there are other attributes you could set depending one your specific use case. You can find a full list of cookie attributes here.

Reading cookies

However, there is another question: how do we read from a cookie? Besides the document.cookie getter and setter, there are no built-in functions that allow you to work with cookies.

But remember, a cookie is just a string that follows a specific format, which means you could extract data from it using the regular expressions we discussed previously. Consider this example:

javascript
1document.cookie = "theme=dark";
2document.cookie = "number=42";
3document.cookie = "status=active";
4
5console.log(document.cookie);
text
1number=42; status=active; theme=dark;

Three cookies, theme, number, and status, are set for this specific web application. As you can see, the document.cookie getter will combine all three of them together.

You can create a helper function to retrieve an individual cookie.

javascript
1function getCookie(name) {
2  let regex = new RegExp("(^|; )" + encodeURIComponent(name) + "=([^;]*)");
3  let match = document.cookie.match(regex);
4  return match ? decodeURIComponent(match[2]) : null;
5}
6
7let theme = getCookie("theme");
8let number = getCookie("number");
9let status = getCookie("status");
10
11console.log(theme); // -> "dark"
12console.log(number); // -> "42"
13console.log(status); // -> "active"

Cookies are also accessible from both the client side and server side, which makes it perfect for verifying if a user is authenticated. We will talk more about accessing cookies on the server side later.

localStorage and sessionStorage

A cookie is typically around 4KB, which is not very much. If your application requires more storage, consider using localStorage instead, which allows for 5MB per domain.

Data inside localStorage will be stored in key/value pairs, which can be set using the setItem() method.

javascript
1localStorage.setItem("username", "JohnDoe");

And getItem() retrieves a stored item:

javascript
1localStorage.getItem("username");
text
1JohnDoe

Items stored in the localStorage can only be accessed from the client side.

sessionStorage works just like localStorage, except it stores temporary data. Once the browser page is closed, or in technical terms, when the session is over, the stored data will be lost.

javascript
1sessionStorage.setItem("username", "JohnDoe");
2
3sessionStorage.getItem("username");
text
1JohnDoe

And just like localStorage, sessionStorage is only accessible from the client side.

IndexedDB

IndexedDB is a fully featured database built into the browser. In most case, you don't need this kind of power. It is designed for web apps that needs to run offline.

It is impossible to cover everything about this database in one lesson, and frankly, it possesses too much power for this course. So, instead of discussing it in detail, we are only going to give one example below.

javascript
1// Open the database myDatabase with version number 1. A new database will be created if myDatabase does not exist
2let request = indexedDB.open("myDatabase", 1);
3
4// This event is triggered if the database needs to be upgraded, meaning when the database is created for the first time or if a new version is requested
5request.onupgradeneeded = function (event) {
6  let db = event.target.result;
7  let objectStore = db.createObjectStore("users", { keyPath: "id" });
8  objectStore.createIndex("username", "username", { unique: true });
9};
10
11// This event is triggered when the database is successfully opened
12request.onsuccess = function (event) {
13  let db = event.target.result;
14  let transaction = db.transaction(["users"], "readwrite");
15  let objectStore = transaction.objectStore("users");
16  let user = { id: 1, username: "JohnDoe" };
17  objectStore.add(user);
18};

If you are interested, check out the complete API of IndexedDB for more details.

Summary

FeatureCookiesLocalStorageSessionStorageIndexedDB
PurposeSession management, personalization, trackingStoring user preferences, application stateTemporary storage for a single sessionStoring large amounts of structured data
Capacity~4KB per cookie~5MB per domain~5MB per domainHundreds of MBs per domain
AccessibilityClient and server-sideClient-side onlyClient-side onlyClient-side only
PersistenceConfigurable expirationPersistent until explicitly deletedDuration of the page session (tab/window)Persistent until explicitly deleted
SecurityCan use HttpOnly and Secure flagsVulnerable to XSSVulnerable to XSSVulnerable to XSS
ScopeShared across all tabs/windowsShared across all tabs/windowsUnique to each tab/windowShared across all tabs/windows