NanoID - A URL Friendly Unique Identifier

NanoID - A URL Friendly Unique Identifier

What is Nano Id and how is it different from UUID?

Featured on daily.dev

Introduction

In every software system, we need unique Ids to distinguish between several objects from one another.

Recently, I wrote about the unique Id generation for a large scale distributed environment. In that article, we touched a little on UUIDs.

In this blog post, I will share an alternative to UUIDs that can help you fit the requirements that UUIDs fulfills and overcomes some of their shortcomings as well.

Introducing Nano ID

It is a tiny, secure, URL-friendly, unique string ID generator.

  • Nano ID has smaller size as compared to UUID. This size reduction impacts a lot. Making use of NanoID is easier for transferring information and storage space. In a large-scale system, these numbers can make a lot of difference.

  • NanoID uses a cryptographically strong API which is a lot safer compared to Math.Random() which are insecure. These API modules use unpredictable hardware generated random identifiers.

  • NanoID utilizes its very own "uniform formula" throughout the application of the ID generator instead of making use of an arbitrary % alphabet which is a popular mistake to make when coding an ID generator (The spread will not be even in some cases).

  • NanoID uses a larger alphabet resulting in short but unique identifiers.

  • NanoID permits designers to utilize personalized alphabets. This is another additional function of Nano ID. You can alter the literals or the dimension of the id as shown below (Specifying personalized letter as '1234567890ABCDEF' & dimension of the Id as 10):

import { alphabet } from 'nanoid';
const nanoid = alphabet ('1234567890ABCDEF', 10);
model.id = nanoid();
  • NanoID doesn’t much rely on any kind of third-party dependencies, which means, it ends up being a lot more steady which is helpful to maximize the package scope over time as well as make it much less vulnerable to the issues that come along with dependencies.

  • NanoID is available in various programs languages, which include - C#, C++, Dart & Flutter, Go, Java, PHP, Python, Ruby, Rust, Swift, etc.

Benchmark

image.png

Example usages

Generating both NanoID or UUID is pretty straightforward. In JavaScript, you have NPM packages that will help you to generate them. You can get NanoId from here => github.com/ai/nanoid

  • The main module uses URL-friendly symbols (A-Za-z0-9_-) and returns an ID with 21 characters:
import { nanoid } from "nanoid";
model.id = nanoid() // X2JaSYP7_Q2leGI9b-MyA
  • You can also specify the number of characters you want:
nanoid(9); // "wMeKBp6th"
  • You can also change the used alphabet for generating hashes to your own if you have specific requirements as seen above:
const alphabet = '0123456789ABCDEF';
generate(alphabet, 9); // F65BF3050

Risk of collision

Even though it can generate over 2.2 million unique IDs per second with its default alphabet, there is still a chance of generating the same multiple Ids.

But what are the odds of that happening?

You can calculate that based on the given parameters easily here and here.

You'll notice that this probability comes out to be extremely small.

Some disadvantages

  • Being non-human readable can be one of the disadvantages in some cases

Imagine that a customer calls and is asked to provide the identifier, having to spell a complete NanoID is not a pleasant experience. When compared to UUID, NanoID is way shorter and readable but still cannot be used in such cases where the end customer needs to use it.

  • It can't be used as primary key in any SQL Database tables

If you use NanoID as a table’s primary key, there will be problems if you use the same column as a clustered index. This is because NanoIDs are not sequential.

This is because the nature of NanoID(or even UUID) is that it's random and a clustered index physically orders the records by the key, so for every insert if there are indexes on the table, the database must make sure the new entry is also found via these indexes and to keep the index order and tree balance.

For this SQL has to reorder the records on disk and therefore remove clustering from this index. But when you have something sequential like time - clustering is almost free and easy to do after inserting a new record.


A few words of wisdom

Any approach in a Software World is always going to be subjective. It’s up to your requirements to weigh in the tradeoffs and choose the approach that works for you. No design is concrete enough to continue forever, so given the constraints, we have chosen a certain design, and depending on how it works for us we might evolve it further as well.

👋 Thanks for reading and Happy learning…

Did you find this article valuable?

Support Apoorv Tyagi by becoming a sponsor. Any amount is appreciated!