Introduction
Everyone needs at some point to generate unique and random IDs. But please, don't ever use Math.floor((Math.random() * 100) + 1);
: it is not cryptographically secure and not even random.
Thankfully, many packages and tools exist that provide a secure and reliable way to generate unique IDs. For this tutorial, we will use the nanoid package.
The Flow Operation we will build is quite simple:
- Generate a unique ID based on two optional parameters; length and alphabet
- Return the ID
Before we begin, I strongly suggest you to read the awesome Directus Documentation and get familiar with Directus Extensions.
Create a new Flow Operation extension
Go to the root of your project and run the npx create-directus-extension@latest
, choose the operation
extension type, name it "nanoid" and hit enter.
This will scaffold a new Directus Flow Operation extension for you with the default template to help you get started.
We just need to tweak the extension's package.json a little bit to make sure that our extension's built files land into the right directory:
{
...,
"directus:extension": {
"type": "operation",
"path": {
"app": "../extensions/operations/nanoid/app.js",
"api": "../extensions/operations/nanoid/api.js"
},
},
...
}
Run npm run dev inside the package directory and you're all set.
Note: you don't need to install the nanoid package as it is already installed by default with Directus.
Building our unique ID generator
Let's begin with the API entrypoint (src/api.ts)
import { defineOperationApi } from "@directus/extensions-sdk";
import { customAlphabet, nanoid } from "nanoid";
type Options = {
length: number;
alphabet: string;
};
export default defineOperationApi({
id: "operation-nanoid",
handler: ({ length, alphabet }) => {
const size = Number(length) || 21;
return alphabet ? customAlphabet(alphabet, size)() : nanoid(size);
},
});
What we did is dead simple:
- Our handler function takes a length and an alphabet parameter that will be passed from the front-end
- We make sur that the length is an actual Number and fallback to the default value if no value is passed (21)
- If a custom alphabet is passed, we generate the unique ID with it, otherwise we use Nanoid's default alphabet
Let's finish with the app entrypoint (src/app.ts):
import { defineOperationApp } from "@directus/extensions-sdk";
export default defineOperationApp({
id: "operation-nanoid",
name: "Nanoid",
icon: "123",
description: "Generate an unique string ID with Nanoid",
overview: ({ length, alphabet }) => [
{
label: "Length",
text: length,
},
{
label: "Alphabet",
text: alphabet,
},
],
options: [
{
field: "length",
name: "Length",
type: "integer",
meta: {
width: "half",
interface: "input",
options: {
min: 1,
},
},
schema: {
default_value: 21,
},
},
{
field: "alphabet",
name: "Custom alphabet",
type: "string",
meta: {
width: "half",
interface: "input",
note: "Leave empty for default Nanoid alphabet.",
},
},
],
});
Et voilà! Your unique ID generator Flow Operation is now operational.
Just keep in mind that the more you reduce the length of your ID, the more you increase the probability of collisions. So take this in account, and use the Nanoid Collision Calculator before deciding of the size of your IDs.
You can install the package directly through npm by running npm i directus-extension-nanoid
If you're struggling with your Directus instance, get in touch for assistance.