Skip to content

Repository files navigation

Snowflake ID Generator

A configurable TypeScript library for generating unique, sortable Snowflake IDs as bigint values.

  • Custom epoch and bit allocation
  • Numeric or structured node identities
  • Monotonic generation within each generator
  • Stateless encoding and decoding

Installation

npm install @pokedotdev/snowflake

Quick start

Configure the format when creating a Snowflake:

import { Snowflake } from '@pokedotdev/snowflake';

// Defaults: 2020-01-01T00:00:00.000Z epoch and 41/10/12 timestamp/node/sequence bits.
const snowflake = new Snowflake({
	epoch: Date.UTC(2026, 0, 1),
	bits: {
		timestamp: 41,
		node: 10,
		sequence: 12,
	},
});

const generator = snowflake.generator({ node: 17 });
const id = generator.generate();

Reuse one generator per node identity. Separate generators with the same identity can produce collisions.

Structured node identities

Use an object for bits.node to divide the node section into named fields:

const snowflake = new Snowflake({
	epoch: Date.UTC(2026, 0, 1),
	bits: {
		timestamp: 41,
		node: {
			worker: 5,
			process: 5,
		},
		sequence: 12,
	},
});

const generator = snowflake.generator({
	node: { worker: 17, process: 3 },
});

Configured field names and declaration order define the node layout. Every node identity must provide the same fields, but its property order does not matter.

Zero-width node

Set scalar bits.node to 0 when the format has only one implicit node. Its identity is always 0, so generator options and the encoded node component may be omitted:

const snowflake = new Snowflake({
	bits: { timestamp: 41, node: 0, sequence: 12 },
});

const generator = snowflake.generator();
const id = snowflake.encode({ timestamp: Date.now(), sequence: 0 });
const components = snowflake.decode(id);
// components.node === 0

The remaining 10 bits can be added to sequence if needed.

snowflake.generator({}) and snowflake.generator({ node: 0 }) are also valid. No other node identity is accepted.

Encoding and decoding

Encoding and decoding use the same format without requiring a generator:

const id = snowflake.encode({
	timestamp: Date.now(),
	node: { worker: 17, process: 3 },
	sequence: 0,
});

const components = snowflake.decode(id);
// { timestamp: <Unix milliseconds>, node: { worker: 17, process: 3 }, sequence: 0 }

Public timestamps are absolute Unix milliseconds. Only the encoded timestamp section is relative to the configured epoch.

ID layout

Each Snowflake uses a fixed layout with three parts:

  1. Timestamp: ID creation time since the configured epoch.
  2. Node: which node, machine, or process created it.
  3. Sequence: distinguishes IDs created in the same millisecond.

IDs use at most 63 bits and are always non-negative.

Systems that exchange IDs must use the same epoch and bit allocation. Structured nodes must also use the same fields in the same order.

Configuration rules

Custom bits must include node and sequence. timestamp is optional and defaults to 41 bits.

Part Width
Timestamp 1–41 bits
Scalar node 0–53 bits
Structured node field 1–53 bits
Sequence 0–53 bits
  • Total width: The complete layout must not exceed 63 bits.
  • Epoch: Must be a non-negative safe integer in Unix milliseconds and cannot be in the future.
  • Values: Timestamps, node values, and sequences must be non-negative safe integers that fit their configured widths.
  • Zero-width scalar node: Represents one implicit node with identity 0. Positive scalar widths still require an explicit identity.
  • Structured nodes: The layout cannot be empty, and field names must be non-empty and non-integer-like. The node section may exceed 53 total bits if every field and the complete layout remain within their limits.
  • Zero-width sequence: Allows one ID per node per millisecond. Another generation attempt waits until the clock advances.

API

new Snowflake(options?)

Defines an immutable format. Without options, it uses the default epoch and bit allocation. Configuration is copied at construction, so later changes to the original options have no effect.

snowflake.generator(options?)

Creates a stateful generator for one node identity. Options are optional only for a zero-width scalar node; positive scalar and structured layouts require node. Its IDs are unique and monotonic. Sequence overflow waits for a later millisecond, and clock rollback never moves generated timestamps backward.

snowflake.encode(components)

Encodes a timestamp, node identity, and sequence into a bigint. The node property may be omitted only for a zero-width scalar node.

snowflake.decode(id)

Decodes a bigint into its timestamp, node identity, and sequence.

Errors

Formats, node identities, components, and IDs are validated before use.

  • ConfigurationError covers invalid formats, node identities, and encoding components.
  • InvalidIdError covers values that cannot be decoded.
  • Both extend SnowflakeError.
import { InvalidIdError, Snowflake } from '@pokedotdev/snowflake';

const snowflake = new Snowflake();

try {
	snowflake.decode(-1n);
} catch (error) {
	if (error instanceof InvalidIdError) {
		console.error(error.message);
	}
}

License

MIT

Releases

Contributors

Languages