Unique ID Generator For NodeJS
npm install @toolkit-f/snowflake-id

A Snowflake ID generator for Node.js • No dependencies • TypeScript support
Snowflake IDs are 64-bit unique identifiers. Sortable by time and can be generated across multiple servers without coordination and collision.
```
| 41 bits - Timestamp | 10 bits - Machine ID | 12 bits - Sequence |
- Generates unique IDs across distributed systems
- Works with CommonJS and ESM
- Full TypeScript support
- Parse IDs to extract timestamp, machine ID, and sequence
| Feature | Snowflake ID | UUID v7 | UUID v4 | Auto Increment |
|---------|--------------|---------|---------|----------------|
| Sortable | Yes (Time) | Yes (Time) | No | Yes |
| Unique | Distributed | Global | Global | Single DB |
| DB Size | 8 bytes (BigInt) | 16 bytes (Bytes) | 16 bytes | 4/8 bytes |
| Index | Fast (B-Tree) | Fast (B-Tree) | Slow (Fragmented) | Fast |
| Performance | ~3M ops/sec | ~2M ops/sec | ~5M ops/sec | Database Limit |
| Coordination | None | None | None | Centralized |
> Perf Check: This library generates ~3 million IDs per second on a standard laptop (M1 Air).
---
bash
npm install @toolkit-f/snowflake-id
`$3
`bash
yarn add @toolkit-f/snowflake-id
`$3
`bash
pnpm add @toolkit-f/snowflake-id
`$3
`bash
bun add @toolkit-f/snowflake-id
`---
Quick Start
$3
`typescript
import { SnowflakeGenerator } from '@toolkit-f/snowflake-id';// Create a generator with a unique machine ID (0-1023)
const generator = new SnowflakeGenerator({ machineId: 1 });
// Generate as BigInt
const id = generator.nextId();
console.log(id); // 136941813297541120n
// Generate as String (recommended for JSON/APIs)
const idString = generator.nextIdString();
console.log(idString); // "136941813297541121"
`$3
`typescript
import { parseSnowflake } from '@toolkit-f/snowflake-id';const parts = parseSnowflake('136941813297545217');
console.log(parts);
// {
// id: 136941813297545217n,
// timestamp: 2024-01-16T09:09:25.000Z,
// machineId: 1,
// sequence: 1
// }
`$3
`javascript
const { SnowflakeGenerator } = require('@toolkit-f/snowflake-id');const generator = new SnowflakeGenerator({ machineId: 1 });
console.log(generator.nextIdString());
`---
Production Guide
$3
⚠️ Important: Always use .nextIdString() for databases and APIs. JavaScript's Number type cannot safely hold 64-bit integers. IDs will lose precision and become incorrect if you cast them to Number`javascript
// BAD - Precision loss guarantees bugs
const id = generator.nextId();
const numericId = Number(id); // GOOD - Always treat as string
const stringId = generator.nextIdString();
`#### PostgreSQL
Use
BIGINT to store IDs efficiently.
`sql
CREATE TABLE users (
id BIGINT PRIMARY KEY,
username TEXT
);
``javascript
// Correct: Pass as string, driver handles BIGINT conversion
const id = generator.nextIdString();
await db.query('INSERT INTO users (id, username) VALUES ($1, $2)', [id, 'alice']);
`#### MySQL
Use
BIGINT (64-bit integer).
`sql
CREATE TABLE orders (
id BIGINT PRIMARY KEY,
amount DECIMAL(10, 2)
);
`#### MongoDB
Store as
String to ensure compatibility with all clients.
`javascript
const id = generator.nextIdString();
await collection.insertOne({ _id: id, ... });
`$3
| Mistake | Consequence | Fix |
|---------|-------------|-----|
|
Number(id) | Data Corruption | Use String(id) or BigInt |
| machineId: 0 everywhere | ID Collisions | Unique ID per instance |
| Math.random() for ID | No Sorting | Use Snowflake |$3
#### NestJS
`typescript
// snowflake.provider.ts
import { Provider } from '@nestjs/common';
import { SnowflakeGenerator } from '@toolkit-f/snowflake-id';export const SNOWFLAKE_PROVIDER = 'SNOWFLAKE_GENERATOR';
export const snowflakeProvider: Provider = {
provide: SNOWFLAKE_PROVIDER,
useFactory: () => {
const machineId = parseInt(process.env.MACHINE_ID || '0', 10);
return new SnowflakeGenerator({ machineId });
},
};
`#### Prisma
`prisma
// schema.prisma
model User {
id BigInt @id
username String
}
`
Note: You must generate the ID in code before creating the record.#### TypeORM
`typescript
@Entity()
export class User {
@PrimaryColumn('bigint')
id: string; // TypeORM handles BigInt as string
}
`$3
To prevent ID collisions, every running instance must have a unique machineId (0-1023).#### Kubernetes (StatefulSet)
Use the collection ordinal from the hostname (e.g.,
web-0, web-1).
`javascript
const podName = process.env.HOSTNAME || 'web-0';
const machineId = parseInt(podName.split('-').pop() || '0', 10);
const generator = new SnowflakeGenerator({ machineId });
`#### Docker Swarm / Replicas
Inject via environment variables:
`yaml
services:
api:
environment:
- MACHINE_ID={{.Task.Slot}}
``javascript
const machineId = parseInt(process.env.MACHINE_ID || '0', 10);
const generator = new SnowflakeGenerator({ machineId });
`#### Auto-scaling Groups
Options:
1. Coordination Service: Use Redis/etcd to lease machineIds
2. Hash-based: Hash instance ID to 0-1023 range (collision possible)
3. Time-based: Use startup timestamp modulo 1024 (not recommended)
#### Multi-Region
Partition
machineId ranges by region:| Region | machineId Range |
|--------|----------------|
| us-east | 0-255 |
| us-west | 256-511 |
| eu-west | 512-767 |
| ap-south | 768-1023 |
$3
⚠️ Snowflake IDs allow public timestamp decoding.
Anyone with the ID can calculate exactly when a record was created.`javascript
const { parseSnowflake } = require('@toolkit-f/snowflake-id');
console.log(parseSnowflake('136941813297545217').timestamp);
// 2024-01-16T09:09:25.000Z
`* Do not use Snowflake IDs if the creation time must remain secret.
* Do not trust the timestamp for security verification (it can be spoofed by the client if they generate IDs).
* Consider using a separate random slug (e.g., UUID or NanoID) for public-facing URLs if business metrics (like order volume) need to be hidden.
---
API Reference
$3
#### Constructor
`typescript
new SnowflakeGenerator(config: SnowflakeConfig)
`| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
|
machineId | number | Yes | - | Unique ID for this machine/worker (0-1023) |
| epoch | number | No | 1704067200000 (Jan 1, 2024) | Custom epoch in milliseconds |
| clockMoveBackAction | 'throw' \| 'wait' | No | 'throw' | Behavior when system clock drifts backwards |Example:
`typescript
import { SnowflakeGenerator } from '@toolkit-f/snowflake-id';const generator = new SnowflakeGenerator({
machineId: 5,
epoch: 1609459200000, // Jan 1, 2021
clockMoveBackAction: 'wait' // Wait instead of throwing error
});
`---
####
nextId(): bigintGenerates the next unique ID as a
bigint.`typescript
const generator = new SnowflakeGenerator({ machineId: 1 });console.log(generator.nextId());
// 136941813297541120n
console.log(generator.nextId());
// 136941813297541121n
console.log(generator.nextId());
// 136941813297545216n
`---
####
nextIdString(): stringGenerates the next unique ID as a
string.`typescript
const generator = new SnowflakeGenerator({ machineId: 1 });console.log(generator.nextIdString());
// "136941813297541120"
console.log(generator.nextIdString());
// "136941813297541121"
`---
####
getMachineId(): numberReturns the configured machine ID.
`typescript
const generator = new SnowflakeGenerator({ machineId: 42 });console.log(generator.getMachineId());
// 42
`---
####
getEpoch(): numberReturns the configured epoch timestamp.
`typescript
const generator = new SnowflakeGenerator({ machineId: 1 });console.log(generator.getEpoch());
// 1704067200000
`---
####
getSequence(): numberReturns the current sequence number.
`typescript
const generator = new SnowflakeGenerator({ machineId: 1 });generator.nextId();
console.log(generator.getSequence());
// 0
generator.nextId();
console.log(generator.getSequence());
// 1
`---
####
getLastTimestamp(): numberReturns the timestamp of the last generated ID.
`typescript
const generator = new SnowflakeGenerator({ machineId: 1 });generator.nextId();
console.log(generator.getLastTimestamp());
// 1737017365000
`---
$3
`typescript
createGenerator(config: SnowflakeConfig): SnowflakeGenerator
`Alternative way to create a generator instance.
`typescript
import { createGenerator } from '@toolkit-f/snowflake-id';const generator = createGenerator({ machineId: 1 });
console.log(generator.nextId());
// 136941813297541120n
`---
$3
####
parseSnowflake(id, epoch?): SnowflakePartsDeconstructs a Snowflake ID into its components.
| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
|
id | bigint \| string | Yes | - | The Snowflake ID to parse |
| epoch | number | No | 1704067200000 | Custom epoch used during generation |Returns:
SnowflakeParts`typescript
import { parseSnowflake } from '@toolkit-f/snowflake-id';const parts = parseSnowflake('136941813297545217');
console.log(parts);
// {
// id: 136941813297545217n,
// timestamp: 2024-01-16T09:09:25.000Z,
// timestampMs: 1737017365000,
// machineId: 1,
// sequence: 1
// }
const parts2 = parseSnowflake(136941813297545217n);
console.log(parts2);
// {
// id: 136941813297545217n,
// timestamp: 2024-01-16T09:09:25.000Z,
// timestampMs: 1737017365000,
// machineId: 1,
// sequence: 1
// }
`---
####
stringifySnowflakeParts(parts): SnowflakePartsJSONConverts SnowflakeParts to a JSON-serializable format.
`typescript
import { parseSnowflake, stringifySnowflakeParts } from '@toolkit-f/snowflake-id';const parts = parseSnowflake('136941813297545217');
const json = stringifySnowflakeParts(parts);
console.log(json);
// {
// id: "136941813297545217",
// timestamp: "2024-01-16T09:09:25.000Z",
// timestampMs: 1737017365000,
// machineId: 1,
// sequence: 1
// }
console.log(JSON.stringify(json));
// '{"id":"136941813297545217","timestamp":"2024-01-16T09:09:25.000Z","timestampMs":1737017365000,"machineId":1,"sequence":1}'
`---
####
getTimestamp(id, epoch?): DateExtracts the Date object from a Snowflake ID.
| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
|
id | bigint \| string | Yes | - | The Snowflake ID |
| epoch | number | No | 1704067200000 | Custom epoch |`typescript
import { getTimestamp } from '@toolkit-f/snowflake-id';console.log(getTimestamp('136941813297545217'));
// 2024-01-16T09:09:25.000Z
console.log(getTimestamp(136941813297545217n));
// 2024-01-16T09:09:25.000Z
`---
####
getMachineId(id): numberExtracts the machine ID from a Snowflake ID.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
|
id | bigint \| string | Yes | The Snowflake ID |`typescript
import { getMachineId } from '@toolkit-f/snowflake-id';console.log(getMachineId('136941813297545217'));
// 1
console.log(getMachineId(136941813297545217n));
// 1
`---
####
getSequence(id): numberExtracts the sequence number from a Snowflake ID.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
|
id | bigint \| string | Yes | The Snowflake ID |`typescript
import { getSequence } from '@toolkit-f/snowflake-id';console.log(getSequence('136941813297545217'));
// 1
console.log(getSequence(136941813297545217n));
// 1
`---
####
isValidSnowflake(id, epoch?, relaxed?): booleanValidates if a value is a valid Snowflake ID.
| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
|
id | unknown | Yes | - | Value to validate |
| epoch | number | No | 1704067200000 | Custom epoch |
| relaxed | boolean | No | false | Skip timestamp range check |`typescript
import { isValidSnowflake } from '@toolkit-f/snowflake-id';console.log(isValidSnowflake('136941813297545217'));
// true
console.log(isValidSnowflake(136941813297545217n));
// true
console.log(isValidSnowflake('invalid'));
// false
console.log(isValidSnowflake('abc123'));
// false
console.log(isValidSnowflake(-1n));
// false
// Relaxed mode (skip timestamp validation)
console.log(isValidSnowflake('999999999999999999999', undefined, true));
// true
`---
####
snowflakeToString(id): stringConverts a bigint Snowflake ID to string.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
|
id | bigint | Yes | The Snowflake ID |`typescript
import { snowflakeToString } from '@toolkit-f/snowflake-id';console.log(snowflakeToString(136941813297545217n));
// "136941813297545217"
`---
####
stringToSnowflake(str): bigintConverts a string Snowflake ID to bigint.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
|
str | string | Yes | The Snowflake ID string |`typescript
import { stringToSnowflake } from '@toolkit-f/snowflake-id';console.log(stringToSnowflake('136941813297545217'));
// 136941813297545217n
// Throws error for invalid input
stringToSnowflake('invalid');
// Error: Invalid Snowflake ID string: "invalid"
`---
####
compareSnowflakes(a, b): -1 | 0 | 1Compares two Snowflake IDs. Useful for sorting.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
|
a | bigint \| string | Yes | First Snowflake ID |
| b | bigint \| string | Yes | Second Snowflake ID |Returns:
-1 if a < b, 0 if a === b, 1 if a > b`typescript
import { compareSnowflakes } from '@toolkit-f/snowflake-id';console.log(compareSnowflakes('136941813297545216', '136941813297545217'));
// -1
console.log(compareSnowflakes('136941813297545217', '136941813297545217'));
// 0
console.log(compareSnowflakes('136941813297545218', '136941813297545217'));
// 1
// Sorting example
const ids = ['136941813297545218', '136941813297545216', '136941813297545217'];
ids.sort(compareSnowflakes);
console.log(ids);
// ['136941813297545216', '136941813297545217', '136941813297545218']
`---
####
snowflakeFromTimestamp(date, machineId?, sequence?, epoch?): bigintCreates a Snowflake ID from a specific timestamp. Useful for database range queries.
| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
|
date | Date \| number | Yes | - | Timestamp to create ID from |
| machineId | number | No | 0 | Machine ID (0-1023) |
| sequence | number | No | 0 | Sequence number (0-4095) |
| epoch | number | No | 1704067200000 | Custom epoch |`typescript
import { snowflakeFromTimestamp } from '@toolkit-f/snowflake-id';// From Date object
const id1 = snowflakeFromTimestamp(new Date('2024-06-15T12:00:00.000Z'));
console.log(id1);
// 59918327808000000n
// From timestamp number
const id2 = snowflakeFromTimestamp(1718452800000);
console.log(id2);
// 59918327808000000n
// With machine ID and sequence
const id3 = snowflakeFromTimestamp(new Date('2024-06-15T12:00:00.000Z'), 5, 10);
console.log(id3);
// 59918327808020490n
// Database range query example
const startOfDay = snowflakeFromTimestamp(new Date('2024-06-15T00:00:00.000Z'));
const endOfDay = snowflakeFromTimestamp(new Date('2024-06-15T23:59:59.999Z'));
console.log(
SELECT * FROM items WHERE id >= ${startOfDay} AND id <= ${endOfDay});
// SELECT * FROM items WHERE id >= 59914215014400000 AND id <= 60010106326016000
`---
$3
`typescript
import {
DEFAULT_EPOCH,
MAX_MACHINE_ID,
MAX_SEQUENCE,
MACHINE_ID_SHIFT,
TIMESTAMP_SHIFT
} from '@toolkit-f/snowflake-id';console.log(DEFAULT_EPOCH);
// 1704067200000 (Jan 1, 2024 00:00:00 UTC)
console.log(MAX_MACHINE_ID);
// 1023
console.log(MAX_SEQUENCE);
// 4095n
console.log(MACHINE_ID_SHIFT);
// 12n
console.log(TIMESTAMP_SHIFT);
// 22n
`---
$3
`typescript
import type {
SnowflakeConfig,
SnowflakeParts,
SnowflakePartsJSON
} from '@toolkit-f/snowflake-id';// SnowflakeConfig
interface SnowflakeConfig {
machineId: number; // 0-1023
epoch?: number; // Custom epoch (ms)
clockMoveBackAction?: 'throw' | 'wait';
}
// SnowflakeParts
interface SnowflakeParts {
id: bigint;
timestamp: Date;
timestampMs: number;
machineId: number;
sequence: number;
}
// SnowflakePartsJSON
interface SnowflakePartsJSON {
id: string;
timestamp: string;
timestampMs: number;
machineId: number;
sequence: number;
}
`---
Error Handling
`typescript
import { SnowflakeGenerator, stringToSnowflake, parseSnowflake } from '@toolkit-f/snowflake-id';// Invalid machine ID
try {
new SnowflakeGenerator({ machineId: 2000 });
} catch (e) {
console.log(e.message);
// "machineId must be integer 0-1023, got 2000"
}
// Future epoch
try {
new SnowflakeGenerator({ machineId: 1, epoch: Date.now() + 100000 });
} catch (e) {
console.log(e.message);
// "epoch cannot be in the future"
}
// Invalid string ID
try {
stringToSnowflake('not-a-number');
} catch (e) {
console.log(e.message);
// 'Invalid Snowflake ID string: "not-a-number"'
}
// Invalid parse input
try {
parseSnowflake('abc123');
} catch (e) {
console.log(e.message);
// 'Invalid Snowflake ID: "abc123"'
}
`---
🤝 Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
1. Fork the repository
2. Create your feature branch (
git checkout -b feature/amazing-feature)
3. Commit your changes (git commit -m 'Add some amazing feature')
4. Push to the branch (git push origin feature/amazing-feature`)---