-
Notifications
You must be signed in to change notification settings - Fork 2
ID SimpleID
Human-readable date-based sequential ID generator for business documents and daily sequences.
- Overview
- Features
- Installation
- ID Structure
- API Reference
- Usage Examples
- Automatic Daily Reset
- Use Cases
- Best Practices
- Comparison with Other Sequential Systems
- Related Documentation
SimpleID generates human-readable sequential IDs that combine the current date with an incrementing counter. These IDs are perfect for business documents, invoices, orders, and any scenario where you need date-traceable, predictable identifiers that are easy to read and understand.
Why SimpleID?
- Human-readable: Date component makes IDs immediately understandable
- Predictable: Sequential counters provide ordered, traceable sequences
- Date-sortable: IDs naturally sort chronologically by date
- Business-friendly: Perfect for invoices, receipts, and documents
- Automatic reset: Counter resets daily for clean daily sequences
- Customizable: Adjust counter length and precision to your needs
Security note: SimpleID has no cryptographic or random component — the date is public and the counter is a small, sequential integer that's trivial to guess or enumerate. Never use it for session tokens, password-reset links, API keys, or anywhere the value must be hard to guess — use NanoID, ulid, or cuid2 for those.
| Feature | Support | Description |
|---|---|---|
| Date-based | ✅ | YYYYMMDD format for immediate date recognition |
| Sequential Counter | ✅ | Incrementing counter with customizable length |
| Daily Reset | ✅ | Counter automatically resets at midnight |
| Microsecond Precision | ✅ | Optional high-precision timestamps |
| BigInt Output | ✅ | Native BigInt for large numbers and precision |
| Zero-padded | ✅ | Consistent length for sorting and alignment |
| Custom Seed | ✅ | Start sequences at any number |
| Runtime Agnostic | ✅ | Works on Deno, Bun, Node.js, Cloudflare Workers, and browsers (no runtime-specific globals) |
Deno:
deno add @tundralibs/idBun:
bunx jsr add @tundralibs/idNode.js:
npx jsr add @tundralibs/idDirect import (Deno):
import { simpleID } from 'jsr:@tundralibs/id';The basic SimpleID format combines date and counter:
YYYYMMDD + NNNN
└─┬──┘ └─┬─┘
│ └─ Counter (zero-padded, customizable length)
└────────── Date (8 digits)
Example: 202412260042 breaks down as:
-
20241226- December 26, 2024 -
0042- 42nd ID of the day
When microsecond precision is enabled, the format includes timestamp:
YYYYMMDD + ΜΜΜΜΜΜ + NNN
└─┬──┘ └──┬───┘ └┬┘
│ │ └─ Counter (3+ digits)
│ └──────── Microseconds (6 digits)
└────────────────── Date (8 digits)
Example: 20241226143052000123 breaks down as:
-
20241226- December 26, 2024 -
143052- Microsecond timestamp component -
000123- 123rd ID
Different configurations produce different ID formats:
| Configuration | Format | Example | Use Case |
|---|---|---|---|
| Default | YYYYMMDDNNNN | 202412260001 |
Daily sequences |
| Custom length | YYYYMMDDNNNNNN | 20241226000001 |
High-volume orders |
| With microseconds | YYYYMMDDΜΜΜΜΜΜNNN | 20241226143052001 |
Event logging |
| Custom seed | YYYYMMDDNNNN | 202412261001 |
Invoice numbering |
Creates a date-based sequential ID generator function.
function simpleID(
seed?: number,
minLen?: number,
includeMicroseconds?: boolean,
): () => bigint;Parameters:
-
seed- Optional. Initial counter value (default:0)- Starting number for the sequence
- Useful for continuing existing sequences
- Must be a non-negative integer (negative integers are clamped to 0; NaN,
fractional, or Infinite values throw
InvalidOptionError) - Example: Set to
1000to start invoices at INV-202412261001
-
minLen- Optional. Minimum length of the counter component (default:4)- Counter is zero-padded to this length
- Must be an integer between 1 and 256
- The upper bound of
256is a deliberate sanity cap, not a technical maximum: it sits far below every runtime's real failure point (a padded counter that overflows the engine's string length, or a BigInt too large to build), while10^256IDs in a single day is already beyond astronomical. Values above256are rejected on purpose so the typedInvalidOptionErroris raised at construction on every runtime, rather than a rawRangeErrorsurfacing later at generation time. - Longer values prevent overflow in high-volume scenarios
- Example:
6produces counters like000001,000002
-
includeMicroseconds- Optional. Whether to include microsecond precision (default:false)- Adds 6-digit microsecond component to IDs
- Provides higher uniqueness for rapid generation
- Useful for event logging and high-frequency operations
- Increases ID length significantly
Returns: () => bigint - A generator function that produces sequential IDs
Throws:
-
InvalidOptionError- IfminLenis less than 1, greater than 256, or not an integer (NaN, a fractional value, or Infinity). A NaN or fractionalminLenis not silently accepted (which would emit a below-minimum counter), and an out-of-range value throws this typed error rather than a rawRangeErrorat generation time. -
InvalidOptionError- Ifseedis not an integer (NaN, a fractional value, or Infinity)
Generator Function:
The returned function generates the next ID in sequence:
import { simpleID } from '@tundralibs/id';
const gen = simpleID();
const id1 = gen(); // 202412260001n
const id2 = gen(); // 202412260002nSimple incrementing sequence that resets daily:
import { simpleID } from '@tundralibs/id';
const dailySeq = simpleID();
const id1 = dailySeq(); // 202412260001n
const id2 = dailySeq(); // 202412260002n
const id3 = dailySeq(); // 202412260003n
// Next day, counter automatically resets
// (assuming date has changed)
const nextDayId = dailySeq(); // 202412270001nGenerate professional invoice numbers with custom prefix:
import { simpleID } from '@tundralibs/id';
// Start at 1000 for professional appearance
const invoiceGen = simpleID(1000, 4);
const inv1 = invoiceGen(); // 202412261001n
const inv2 = invoiceGen(); // 202412261002n
// Format with prefix for display
const formatInvoice = (id: bigint) => `INV-${id}`;
console.log(formatInvoice(inv1)); // "INV-202412261001"
console.log(formatInvoice(inv2)); // "INV-202412261002"High-volume order tracking with longer counters:
import { simpleID } from '@tundralibs/id';
// 6-digit counter for high-volume businesses
const orderGen = simpleID(0, 6);
const order1 = orderGen(); // 20241226000001n
const order2 = orderGen(); // 20241226000002n
const order3 = orderGen(); // 20241226000003n
// Format for display
const formatOrder = (id: bigint) => `ORD-${id}`;
console.log(formatOrder(order1)); // "ORD-20241226000001"Event logging with microsecond accuracy:
import { simpleID } from '@tundralibs/id';
// Include microseconds for precision
const logGen = simpleID(0, 3, true);
const log1 = logGen(); // 20241226143052789001n
const log2 = logGen(); // 20241226143052789002n
const log3 = logGen(); // 20241226143052790001n
// Parse components for display
const parseLogId = (id: bigint) => {
const str = id.toString();
return {
date: str.slice(0, 8), // 20241226
micro: str.slice(8, 14), // 143052
counter: str.slice(14), // 789001
};
};
console.log(parseLogId(log1));
// { date: '20241226', micro: '143052', counter: '789001' }Continue sequences from a specific number:
import { simpleID } from '@tundralibs/id';
// Resume from last known ID
const lastId = 5432;
const resumeGen = simpleID(lastId, 4);
const next1 = resumeGen(); // 202412265433n
const next2 = resumeGen(); // 202412265434nSimpleID automatically resets the counter to zero at the start of each new day. This ensures:
-
Predictable daily sequences: Each new day starts fresh at
0001(the counter resets to 0, so the first ID of the day isYYYYMMDD0001regardless of the seed) - Date-based organization: IDs are naturally grouped by date
- Consistent length: Counter stays within expected ranges
- No manual intervention: Reset happens automatically
How it works:
The generator tracks the current date (YYYYMMDD format) and compares it on each ID generation:
import { simpleID } from '@tundralibs/id';
const gen = simpleID(0, 4);
// December 26, 2024
const id1 = gen(); // 202412260001n
const id2 = gen(); // 202412260002n
// ... time passes, date changes to December 27 ...
// December 27, 2024 - counter automatically reset
const id3 = gen(); // 202412270001n (counter reset to 1)
const id4 = gen(); // 202412270002nImportant notes:
- Reset occurs on first ID generation after midnight
- Counter resets to zero, not the initial seed. The seed only offsets the
first day's sequence; every new day begins at
YYYYMMDD0001regardless of seed - No IDs are lost during reset
- Works across time zones (uses system time)
SimpleID is ideal for scenarios requiring human-readable, date-traceable identifiers:
Perfect for invoices, receipts, quotes, and purchase orders:
import { simpleID } from '@tundralibs/id';
const invoiceGen = simpleID(1000, 4);
const receiptGen = simpleID(5000, 4);
const quoteGen = simpleID(2000, 4);
// Generate daily document numbers
const invoice = invoiceGen(); // INV-202412261001
const receipt = receiptGen(); // RCP-202412265001
const quote = quoteGen(); // QTE-202412262001Track orders with date-based references:
import { simpleID } from '@tundralibs/id';
const orderGen = simpleID(0, 6);
// Orders are naturally sorted by date
const order1 = orderGen(); // 20241226000001
const order2 = orderGen(); // 20241226000002Generate support tickets or event tickets:
import { simpleID } from '@tundralibs/id';
const ticketGen = simpleID(10000, 5);
// Support tickets
const ticket1 = ticketGen(); // TKT-2024122610001
const ticket2 = ticketGen(); // TKT-2024122610002High-frequency log entries with microsecond precision:
import { simpleID } from '@tundralibs/id';
const logGen = simpleID(0, 3, true);
// Precise event timestamps
const event1 = logGen(); // 20241226143052789001
const event2 = logGen(); // 20241226143052789002Generate daily report identifiers:
import { simpleID } from '@tundralibs/id';
const reportGen = simpleID(1, 3);
// Daily report numbering
const report1 = reportGen(); // RPT-20241226002
const report2 = reportGen(); // RPT-20241226003Select minLen based on expected daily volume:
- 4 digits (0001-9999): Up to ~10,000 IDs per day
- 5 digits (00001-99999): Up to ~100,000 IDs per day
- 6 digits (000001-999999): Up to ~1,000,000 IDs per day
import { simpleID } from '@tundralibs/id';
// Low volume (< 10K/day)
const lowVol = simpleID(0, 4);
// Medium volume (< 100K/day)
const medVol = simpleID(0, 5);
// High volume (< 1M/day)
const highVol = simpleID(0, 6);Start sequences at meaningful numbers:
import { simpleID } from '@tundralibs/id';
// Start at 1 for natural counting
const natural = simpleID(1, 4);
// Start at 1000 for professional appearance
const professional = simpleID(1000, 4);
// Start at 10000 to match legacy systems
const legacy = simpleID(10000, 5);Add prefixes and separators for better readability:
import { simpleID } from '@tundralibs/id';
const gen = simpleID(1000, 4);
const id = gen();
// Add prefix
const withPrefix = `INV-${id}`;
// Add separators for readability
const formatted = id.toString().replace(
/(\d{8})(\d+)/,
'$1-$2',
);
// 20241226-1001Keep IDs as BigInt in databases for efficient sorting and indexing:
// PostgreSQL
CREATE TABLE invoices (
id BIGINT PRIMARY KEY,
amount DECIMAL(10,2)
);
// MongoDB
{
_id: ObjectId(),
invoiceId: Long("202412261001"),
amount: 100.00
}Be aware that daily reset uses system time:
// Explicit timezone handling if needed
const getDateInTz = (tz: string) => {
return new Date().toLocaleString('en-US', { timeZone: tz });
};
// For multi-region systems, consider UTC-based generationEnable microseconds only when needed:
import { simpleID } from '@tundralibs/id';
// Standard business documents (NO microseconds)
const invoiceGen = simpleID(1000, 4, false);
// High-frequency logging (YES microseconds)
const logGen = simpleID(0, 3, true);Design counter length with future growth in mind:
import { simpleID } from '@tundralibs/id';
// Current: 100 orders/day
// Growth: 1000 orders/day expected
// Use 5 digits for headroom
const orderGen = simpleID(0, 5);| Feature | SimpleID | SequenceID | Database AUTO_INCREMENT | UUID v7 |
|---|---|---|---|---|
| Human-readable date | ✅ | ✅ | ❌ | |
| Daily reset | ✅ | ❌ | ❌ | ❌ |
| Sequential counter | ✅ | ✅ | ✅ | |
| Sortable | ✅ | ✅ | ✅ | ✅ |
| Distributed-safe | ✅ | ❌ | ✅ | |
| Predictable length | ✅ | ✅ | ❌ | ✅ |
| No database required | ✅ | ✅ | ❌ | ✅ |
| Microsecond precision | ✅ (optional) | ✅ | ❌ | ✅ |
When to use SimpleID:
- ✅ Business documents requiring date-traceable references
- ✅ Single-server applications with daily reset needs
- ✅ Human-readable identifiers for customer-facing systems
- ✅ Daily sequences (invoices, orders, tickets)
- ✅ Systems where date visibility is important
When to use alternatives:
- ❌ Distributed systems requiring coordination → Use SequenceID or ULID
- ❌ Need globally unique without date component → Use NanoID or UUID
- ❌ MongoDB-specific requirements → Use ObjectID
- ❌ Continuous sequences across days → Use SequenceID
- ID Package Overview - Main documentation and feature comparison
- NanoID - Compact, URL-safe unique identifiers
- ObjectID - MongoDB-inspired identifiers
- ULID - Universally unique lexicographically sortable IDs
- SequenceID - Continuous sequential IDs with timestamps
- Comparison Guide - Detailed comparison of all ID types
- Performance Benchmarks - Speed and efficiency metrics