structured-id
v0.2.0
Published
Generate and validate structured IDs — numeric or alphanumeric, with optional Luhn or mod36 checksums. Zero dependencies. TypeScript.
Maintainers
Readme
structured-id
Generate and validate structured IDs with ease and flexibility.
- ✅ Supports numeric (0–9) or alphanumeric (A–Z, 0–9) charsets
- ✅ Optional checksum (
luhnfor numeric,mod36for alphanumeric) - ✅ Flexible formatting (grouping, separators)
- ✅ Configurable RNG (
Math.random, Web Crypto viauseCrypto, or custom seeded RNG) - ✅ Pattern support (E.g.
PROMO-###-###with fixed and random parts) - ✅ Zero runtime dependencies • TypeScript types included • ESM + CJS
What is a Structured ID?
A Structured ID is an identifier generated with a predefined structure. Unlike a raw random string, a structured ID follows rules you define at generation time, such as:
- Length — total number of characters (e.g. 16, 20, 24).
- Grouping — split into chunks for readability (e.g.
1234-5678-9012-3456). - Charset — restrict characters (numeric only, or alphanumeric uppercase).
- Checksum (optional) — append a validation digit/character to detect typos.
- Patterns — define custom templates with
#placeholders for random chars and fixed text.
For example, you can generate a 16-character alphanumeric ID with a mod36 checksum, grouped into 4 groups of 4 characters each, making it easy to read and verify:
import { generateId, formatId, validateId } from "structured-id";
// generate a 16-character alphanumeric ID with mod36 checksum
const id = generateId({ charset: "alphanumeric", totalLength: 16, algorithm: "mod36" });
const formatted = formatId(id, { separator: "-", groupSize: 4, charset: "alphanumeric" });
console.log("Raw ID:", id);
// e.g. "AB12CD34EF56GH78"
console.log("Formatted ID:", formatted);
// e.g. "AB12-CD34-EF56-GH78"
console.log("Is valid?", validateId(id, { charset: "alphanumeric", algorithm: "mod36" }));
// true
// generate a promo code using a pattern
const promo = generateId({ pattern: "PROMO-###-###", charset: "alphanumeric" });
console.log("Promo code:", promo);
// e.g. "PROMO-AB1-C3D"
console.log("Promo code valid?", validateId(promo, { pattern: "PROMO-###-###", charset: "alphanumeric" }));
// trueStructured IDs are ideal for cases where codes need to be both human-friendly and machine-validated, such as:
- Anonymous user IDs
- Invite codes
- Voucher / coupon codes
- Access tokens
- Simple one-time passwords (OTPs)
The structured-id library provides a clean, dependency-free way to generate and validate these IDs.
Demo
You can try out structured-id in an interactive demo app:
https://structured-id-demo.vercel.app
Install
Install the package via npm:
npm i structured-idor using yarn:
yarn add structured-idQuick Start
import { generateId, validateId, formatId, normalizeId } from "structured-id";
// Generate a default ID: 16 random digits (numeric charset, no checksum)
const id = generateId();
console.log("Generated ID:", id);
// e.g. "1234567890123456"
// Validate the generated ID (numeric, no checksum)
const isValid = validateId(id);
console.log("Is valid?", isValid); // true
// Format the ID with spaces every 4 characters
const formatted = formatId(id, { separator: " " });
console.log("Formatted ID:", formatted); // e.g. "1234 5678 9012 3456"
// Normalize an ID by removing spaces and separators
const normalized = normalizeId("1234 5678-9012 3456");
console.log("Normalized ID:", normalized); // "1234567890123456"
// Generate a numeric ID with Luhn checksum
const luhnId = generateId({ algorithm: "luhn" });
console.log("Luhn ID:", luhnId);
console.log("Luhn ID valid?", validateId(luhnId, { algorithm: "luhn" })); // true
// Generate an alphanumeric ID (A–Z, 0–9) without checksum
const alphaNumId = generateId({ charset: "alphanumeric" });
console.log("Alphanumeric ID:", alphaNumId);
// Generate an alphanumeric ID with mod36 checksum
const alphaNumChkId = generateId({ charset: "alphanumeric", algorithm: "mod36" });
console.log("Alphanumeric with checksum:", alphaNumChkId);
console.log("Alphanumeric with checksum valid?", validateId(alphaNumChkId, { charset: "alphanumeric", algorithm: "mod36" })); // true
// Format an alphanumeric ID with dashes every 4 characters
console.log(formatId(alphaNumId, { separator: "-", groupSize: 4, charset: "alphanumeric" })); // e.g. "AB12-CD34-EF56-GH78"
// Generate an ID using Web Crypto RNG
const cryptoId = generateId({ useCrypto: true });
console.log("Crypto RNG ID:", cryptoId);Patterns
Structured-id supports patterns to define custom ID templates using # placeholders for random characters and literal characters for fixed text.
- Each
#in the pattern is replaced with a random character from the selected charset (numericoralphanumeric). - Literal characters (letters, digits, symbols) in the pattern are included as-is.
- If a checksum algorithm (
luhnormod36) is specified, the last#in the pattern is reserved for the checksum character. - When using a pattern, options like
groups,groupSize,separator, andtotalLengthare ignored.
Examples
Simple numeric pattern:
const id = generateId({ pattern: "###-###" });
console.log(id); // e.g. "123-456"Alphanumeric promo code pattern:
const promo = generateId({ pattern: "PROMO-###-###", charset: "alphanumeric" });
console.log(promo); // e.g. "PROMO-AB1-C3D"Numeric pattern with Luhn checksum:
const idWithLuhn = generateId({ pattern: "ID-####", algorithm: "luhn" });
console.log(idWithLuhn); // e.g. "ID-1234" where '4' is the checksum
console.log(validateId(idWithLuhn, { pattern: "ID-####", algorithm: "luhn" }));
// trueAlphanumeric pattern with mod36 checksum:
const code = generateId({ pattern: "CODE-###-#", charset: "alphanumeric", algorithm: "mod36" });
console.log(code); // e.g. "CODE-AB3-Z" where 'Z' is the checksum
console.log(validateId(code, { pattern: "CODE-###-#", charset: "alphanumeric", algorithm: "mod36" }));
// truePatterns provide a flexible way to mix fixed and random parts in your IDs while ensuring checksum validation when needed.
Options
You can customize ID generation, validation, and formatting using the following options:
| Option | Description | Default |
|--------------|----------------------------------------------------------------------------------------------|------------------|
| totalLength| Total length of the ID including checksum if used | 16 |
| charset | Character set to use: "numeric" (digits 0–9) or "alphanumeric" (A–Z, 0–9) | "numeric" |
| algorithm | Checksum algorithm to use: "luhn" (numeric), "mod36" (alphanumeric), or "none" | "none" |
| separator | Character to use as group separator in formatting (e.g., " ", "-") | undefined |
| groups | Number of groups used for formatting and default length calculation (groups * groupSize) | 4 |
| groupSize | Number of characters per group for formatting | 4 |
| rng | Custom random number generator function to use (defaults to Math.random) | Math.random |
| useCrypto | Use Web Crypto API for RNG instead of Math.random | false |
| pattern | A string template where # is replaced with a generated character and literals are preserved. The last # is reserved for checksum if an algorithm is used. When supplied, groups, groupSize, separator, and totalLength are ignored. | undefined |
validateId
Validate an ID string for correctness, optionally verifying checksum and charset.
import { validateId } from "structured-id";
const id = "79927398713"; // Example numeric ID with Luhn checksum
// Validate numeric ID with Luhn checksum
const valid = validateId(id, { algorithm: "luhn" });
console.log(valid); // true or falseOptions:
charset:"numeric"or"alphanumeric"(default"numeric")algorithm:"luhn","mod36", or"none"pattern: string template matching the generated pattern (if used)
formatId
Format an ID string by adding separators at regular intervals.
import { formatId } from "structured-id";
const id = "1234567890123456";
// Format with dashes every 4 characters
const formatted = formatId(id, { separator: "-", groupSize: 4 });
console.log(formatted); // "1234-5678-9012-3456"Options:
groups: number of groups (default4)charset:"numeric"or"alphanumeric"(default"numeric")separator: string to insert between groups (e.g.," ","-")groupSize: number of characters per group (default4)
Normalization helpers
Normalize ID strings by removing all whitespace and separators to obtain the raw ID.
import { normalizeId } from "structured-id";
const messyId = "1234 5678-9012 3456";
const normalized = normalizeId(messyId);
console.log(normalized); // "1234567890123456"Checksum helpers
You can use the included checksum helper functions directly if needed:
luhnChecksumDigit(id: string): string— Generates a Luhn checksum digit for a numeric string.luhnValidate(id: string): boolean— Validates a numeric string using the Luhn algorithm.mod36CheckChar(id: string): string— Generates a mod36 checksum character for an alphanumeric string.mod36Validate(id: string): boolean— Validates an alphanumeric string using mod36 checksum.
Example:
import { luhnValidate, luhnChecksumDigit } from "structured-id";
const partialId = "7992739871";
const checksum = luhnChecksumDigit(partialId);
const fullId = partialId + checksum;
console.log(luhnValidate(fullId)); // trueComparison Guide
| Charset | Algorithm | Description | ID Length | Checksum Length | Notes |
|---------------|-----------|-----------------------------------|-----------|-----------------|-------------------------------------|
| numeric | luhn | Numeric digits with Luhn checksum | 15 | 1 | Common for credit cards and IDs |
| alphanumeric| mod36 | Alphanumeric with mod36 checksum | 15 | 1 | Supports A–Z and digits 0–9 |
| numeric | none | Numeric digits only | 16 | 0 | Simple random numeric IDs |
| alphanumeric| none | Alphanumeric only | 16 | 0 | Random alphanumeric IDs |
Entropy and Rule of Thumb
| Charset | Bits per Character | Example ID Length | Approximate Entropy (bits) | |---------------|--------------------|-------------------|----------------------------| | Numeric (0–9) | 3.32 | 16 | 53 | | Alphanumeric (A–Z, 0–9) | 5.17 | 16 | 83 |
Rule of Thumb:
- Use numeric + Luhn for compatibility and simplicity when only digits are allowed.
- Use alphanumeric + mod36 when you need a larger character space and stronger entropy per character.
- Adjust
totalLengthto meet your entropy/security requirements.
RNG Options
By default, structured-id uses Math.random() as the random number generator, but you can enable Web Crypto RNG by setting useCrypto: true:
import { generateId } from "structured-id";
const id = generateId({ useCrypto: true });
console.log(id);You may also provide seeded or custom RNG functions for deterministic ID generation (useful for testing):
import { generateId } from "structured-id";
// Custom RNG example
const customRng = () => {
// deterministic or seeded RNG implementation
return 0.5;
};
const id = generateId({ rng: customRng });
console.log(id);License
MIT © Steve Foster
