Sqids (pronounced "squids") generates short, unique, URL-safe IDs from numbers. This implementation follows the Sqids specification and adds opt-in BigInt support for the full unsigned 64-bit integer range.
- Features
- Use cases
- Installation
- Quick start
- BigInt support
- Configuration
- Example APIs
- Development
- License
- π’ Multiple numbers β encode one or more non-negative numbers into one ID
- πͺ BigInt support β use the full unsigned 64-bit integer range
- β‘ Quick decoding β decode IDs back into their original values
- π Randomized output β sequential inputs produce nonconsecutive IDs
- π ID padding β configure a minimum ID length
- π€ Custom alphabets β control the characters used to generate IDs
- π‘οΈ Blocklists β regenerate IDs containing blocked words
- π URL safe β use generated IDs safely in URLs
- π Portable β Sqids supports 40+ programming languages
Sqids works well for:
- Public URL identifiers and link shorteners
- Internal identifiers such as event or object references
- Reversible database lookups by numeric primary key
Warning
Sqids is not encryption. Anyone can decode an ID, so do not use it for secrets or to hide sensitive information.
Requires Node.js 18 or newer.
Using pnpm:
pnpm add @mdhthahmd/sqidsUsing npm:
npm install @mdhthahmd/sqidsUsing Yarn:
yarn add @mdhthahmd/sqidsimport Sqids from "@mdhthahmd/sqids";
const sqids = new Sqids();
const id = sqids.encode([1, 2, 3]); // "86Rf07"
const numbers = sqids.decode(id); // [1, 2, 3]Note
Because of the algorithm's design, multiple IDs can decode to the same sequence of numbers. If IDs must be canonical, re-encode the decoded numbers and verify that the generated ID matches.
Enable BigInt mode when values may exceed Number.MAX_SAFE_INTEGER. BigInt mode accepts values from 0n through 2n ** 64n - 1n and returns bigint[] when decoding.
import Sqids from "@mdhthahmd/sqids";
const sqids = new Sqids({ mode: "bigint" });
const values = [0n, 9_007_199_254_740_992n, 18_446_744_073_709_551_615n];
const id = sqids.encode(values);
const decoded = sqids.decode(id);
console.log(decoded); // [0n, 9007199254740992n, 18446744073709551615n]Number mode and BigInt mode generate the same ID for values within the safe-integer range:
new Sqids().encode([1, 2, 3]) ===
new Sqids({ mode: "bigint" }).encode([1n, 2n, 3n]); // trueimport Sqids from "@mdhthahmd/sqids";
const sqids = new Sqids({
minLength: 10,
});
const id = sqids.encode([1, 2, 3]); // "86Rf07xd4z"
const numbers = sqids.decode(id); // [1, 2, 3]import Sqids from "@mdhthahmd/sqids";
const sqids = new Sqids({
alphabet: "FxnXM1kBN6cuhsAvjW3Co7l2RePyY8DwaU04Tzt9fHQrqSVKdpimLGIJOgb5ZE",
});
const id = sqids.encode([1, 2, 3]); // "B4aajs"
const numbers = sqids.decode(id); // [1, 2, 3]import Sqids from "@mdhthahmd/sqids";
const sqids = new Sqids({
blocklist: new Set(["86Rf07"]),
});
const id = sqids.encode([1, 2, 3]); // "se8ojk"
const numbers = sqids.decode(id); // [1, 2, 3]All validation and encoding failures throw SqidsError, which extends the built-in Error class.
import Sqids, { SqidsError } from "@mdhthahmd/sqids";
try {
new Sqids().encode([-1]);
} catch (error) {
if (error instanceof SqidsError) {
console.error(error.message);
}
}The workspace includes two consumer applications that use the published @mdhthahmd/sqids package:
examples/hono-apiβ Node.js ESM API on port 3000examples/nestjs-apiβ NestJS CommonJS API on port 3001
Start both APIs in watch mode:
pnpm dev:examplesFor manual testing, open bruno/requests/api as a collection in Bruno and select the hono or nestjs environment. Run each folder's encode request before its decode request.
The recommended setup is the repository's Dev Container. It provides Node.js 24.19, pnpm 11.22, GitHub CLI, Biome, Vitest Explorer, Bruno, and the configured coding-agent tools.
- Install Docker and a Dev Container-compatible editor, such as VS Code with the Dev Containers extension.
- Clone the repository and open it in VS Code.
- Run Dev Containers: Reopen in Container from the command palette.
- Wait for
pnpm install --frozen-lockfileto finish automatically. - Run
pnpm test:allto verify the workspace.
To apply changes made to .devcontainer/devcontainer.json, run Dev Containers: Rebuild Container.
Install Node.js 24.19 and pnpm 11.22, then run:
pnpm install --frozen-lockfile
pnpm test:all| Path | Purpose |
|---|---|
lib |
Published Sqids package, source, tests, and benchmarks |
examples/hono-api |
ESM consumer example |
examples/nestjs-api |
CommonJS consumer example |
bruno/requests/api |
Bruno collection for both example APIs |
| Command | Description |
|---|---|
pnpm build |
Build the library and all example applications |
pnpm dev:lib |
Rebuild the library in watch mode |
pnpm dev:examples |
Start both example APIs in watch mode |
pnpm test:all |
Build, type-check, test, and run Biome CI checks |
pnpm test:coverage |
Run the library tests with coverage |
pnpm test:package |
Validate the packed npm package and its types |
pnpm benchmark |
Run the library's Vitest benchmarks |
pnpm check |
Format and lint the entire repository with Biome |
pnpm clean |
Remove generated workspace build output |
The benchmark suite covers number and BigInt operations, verified round trips, UUID-shaped batch inputs, construction, padding, custom alphabets, and blocklist retries.
Semantic-release owns package versions, tags, changelog entries, and publication. Use Conventional Commit messages on next; fix: creates a patch prerelease and feat: creates a minor prerelease.
Promote next to main using exactly one of these strategies:
- Regular merge: preserve the original commits and use a non-releasing merge message such as
chore: merge next into main. Semantic-release analyzes the preservedfix:andfeat:commits. - Squash merge: use one Conventional Commit PR title, such as
fix: validate package inputs. The squash commit replaces the original commits, so its title determines the stable release.
Do not squash and then merge the same changes again. Do not use fix: or feat: for a regular merge commit when the preserved commits already contain those types; doing so duplicates the release note. After a stable release, update the local main and next branches independently before starting the next prerelease.