@ralalabs/rid
v1.0.1
Published
Fast deterministic reversible ID encoding for numbers and strings using AES-128-CTR with base62 output
Maintainers
Readme
@ralalabs/rid
Fast, deterministic, reversible ID encoding for Node.js and edge runtimes.
@ralalabs/rid turns internal IDs into short opaque tokens and decodes them back with the same secret key. It supports both numeric IDs and string IDs works in Node.js and Cloudflare Workers, and keeps the API intentionally small.
Features
- fast encode and decode for hot-path usage
- supports both
numberandstringIDs - works across runtimes: Node.js, browsers with WebCrypto, Cloudflare Workers, and other edge runtimes
- deterministic output for the same input and key
- short URL-safe base62 tokens
- optional prefixes such as
media-,season-, orepisode- - same high-level API for encode and decode
- small surface area with stricter typed helpers available when needed
Install
npm install @ralalabs/rid
# or
pnpm add @ralalabs/ridQuick start
import { createCodec, generateKey } from '@ralalabs/rid';
const key = process.env.RID_KEY;
const codec = createCodec({
key,
prefix: 'media',
// default separator is '-'
});
const rid = await codec.encode('kv:ro');
const value = await codec.decode(rid);
console.log(rid);
console.log(value);encode() and decode() are the default high-level methods. They accept both number and string IDs and return the original value type on decode.
Prefixes
Prefixes let you namespace tokens by domain or entity type.
const mediaCodec = createCodec({ key: RID_KEY, prefix: 'media' });
const userCodec = createCodec({ key: RID_KEY, prefix: 'user' });
await mediaCodec.encodeNumber(1); // media-...
await userCodec.encodeNumber(1); // user-...Prefix validation is enforced on decode.
Custom separators are also supported. Use non-alphanumeric separators only, such as -, _, :, ::, or ~. This keeps prefix parsing unambiguous because the token body is base62.
API
createCodec(options)
Creates a codec instance.
Options
| Option | Type | Required | Description |
| ----------- | ---------------------- | -------- | ------------------------------------------------------------------------------ |
| key | string \| Uint8Array | yes | 128-bit key as a 32-char hex string or 16-byte array |
| prefix | string | no | optional token namespace, for example med |
| separator | string | no | separator used after prefix, default -. Must not contain letters or numbers. |
Codec methods
| Method | Returns | Notes |
| --------------------- | ---------------------------------- | ----------------------------------------------------------- |
| encode(value) | Promise<string> | accepts number or string |
| decode(token) | Promise<number \| string> | returns the original ID type |
| encodeNumber(value) | Promise<string> | strict numeric helper |
| decodeNumber(token) | Promise<number> | strict numeric helper; throws if token contains a string ID |
| encodeString(value) | Promise<string> | strict string helper |
| decodeString(token) | Promise<string> | strict string helper; throws if token contains a numeric ID |
| encodeBatch(values) | Promise<string[]> | batch encode |
| decodeBatch(tokens) | Promise<Array<number \| string>> | batch decode |
generateKey()
Returns a random 128-bit key as a lowercase 32-character hex string.
import { generateKey } from '@ralalabs/rid';
const key = await generateKey();RidError
All package validation and decode failures throw RidError.
ID support
Numbers
- non-negative integers only
- maximum value:
Number.MAX_SAFE_INTEGER
Strings
- UTF-8 strings are supported
- maximum encoded string size:
65535bytes before encryption - suitable for IDs such as
media:x:y,user:abc,show:season:1
Security notes
This package is meant for opaque reversible IDs. It helps hide internal identifiers from public URLs and APIs, but it is not an authorization mechanism.
Important properties:
- deterministic by design
- same key + same ID always produces the same token
- no MAC or authenticity guarantee
- malformed tokens are rejected through structural validation after decode
- tampered tokens usually fail validation, but this package should not be treated as a cryptographic integrity layer
Use it when you want:
- opaque public route IDs
- reversible IDs across services
- one secret key shared between Node.js and edge runtimes
Do not use it as your only control for:
- access control
- anti-tampering guarantees
- high-assurance cryptographic protocols
Runtime support
| Runtime | Backend |
| ------------------ | ------------- |
| Node.js 18+ | node:crypto |
| Cloudflare Workers | WebCrypto |
| Deno | WebCrypto |
| Modern browsers | WebCrypto |
Performance notes
The package is built for hot-path use:
- small API surface
- cached crypto adapter selection
- cached imported WebCrypto keys
- no JSON serialization in the encoded payload
- deterministic output means token length reflects payload size
- compact binary payload before base62 conversion
For the best type safety and clarity in application code, prefer encodeNumber / decodeNumber and encodeString / decodeString when the ID shape is known.
License
MIT
