Skip to content

Human-readable, prefixed unique identifiers for database models

License

Notifications You must be signed in to change notification settings

golddick/dropid

Repository files navigation

DropID

npm version npm downloads License: MIT GitHub stars https://github.com/golddick/dropid.git Human-readable, prefixed unique identifiers for your database models.

dropid('user')        // → user_a3f2b9c1d4e5
dropid('post', 'app') // → app_post_x7k9m2n4p1q8

Why DropID?

Before (UUIDs):

550e8400-e29b-41d4-a916-446655440000

Good luck debugging that in your logs.

After (DropID):

user_a3f2b9c1d4e5

Instantly know what you're looking at.

Features

  • 🎯 Human-readable - Know what type of object an ID represents at a glance
  • 🔒 Collision-resistant - Uses nanoid under the hood (cryptographically secure)
  • 🚀 ORM-agnostic - Works with Drizzle, Prisma, TypeORM, or plain JavaScript
  • Lightweight - Single dependency (nanoid), ~2KB bundle
  • 🎨 Customizable - Configure length, alphabet, and separators
  • 🏢 Multi-tenant ready - Built-in prefix support for organization-scoped IDs
  • 📦 TypeScript-native - Full type safety out of the box

Installation

npm install dropid
# or
yarn add dropid
# or
pnpm add dropid

Quick Start

import { dropid } from 'dropapi';

// Basic usage
const userId = dropid('user');
// → user_a3f2b9c1d4e5

// With prefix (for multi-tenant apps)
const postId = dropid('post', 'acme');
// → acme_post_x7k9m2n4p1q8

// Use anywhere you need an ID
const newUser = {
  id: dropid('user'),
  name: 'John Doe',
  email: 'john@example.com',
};

Usage with ORMs

Drizzle ORM

import { pgTable, text, timestamp } from 'drizzle-orm/pg-core';
import { dropid } from 'dropapi';

export const users = pgTable('users', {
  id: text('id').primaryKey().$defaultFn(() => dropid('user')),
  name: text('name').notNull(),
  email: text('email').notNull(),
  createdAt: timestamp('created_at').defaultNow(),
});

export const posts = pgTable('posts', {
  id: text('id').primaryKey().$defaultFn(() => dropid('post', 'blog')),
  title: text('title').notNull(),
  authorId: text('author_id').references(() => users.id),
});

Prisma

// In your application code
import { dropid } from 'dropapi';

const user = await prisma.user.create({
  data: {
    id: dropid('user'),
    name: 'Jane Smith',
    email: 'jane@example.com',
  },
});

const post = await prisma.post.create({
  data: {
    id: dropid('post'),
    title: 'My First Post',
    authorId: user.id,
  },
});

TypeORM

import { Entity, PrimaryColumn, Column } from 'typeorm';
import { dropid } from 'dropapi';

@Entity()
export class User {
  @PrimaryColumn()
  id: string = dropid('user');

  @Column()
  name: string;

  @Column()
  email: string;
}

API Reference

dropid(modelName, prefix?, options?)

Generates a unique ID with optional prefix.

Parameters:

  • modelName (string, required): The name of your model/table (e.g., 'user', 'post')
  • prefix (string, optional): Additional prefix for multi-tenant or namespacing
  • options (DropIdOptions, optional): Override global configuration

Returns: string - The generated ID

Examples:

dropid('user')                           // → user_a3f2b9c1d4e5
dropid('post', 'blog')                   // → blog_post_x7k9m2n4p1q8
dropid('order', 'shop', { length: 16 })  // → shop_order_k8j3m9n2l4p6q1r7

configure(options)

Set global configuration that applies to all subsequent dropid() calls.

Parameters:

  • options (DropIdOptions): Configuration object

Options:

interface DropIdOptions {
  length?: number;      // Length of random ID part (default: 12)
  alphabet?: string;    // Characters to use (default: '0-9a-z')
  separator?: string;   // Separator between parts (default: '_')
}

Examples:

import { configure, dropid } from 'dropaphi';

// Use longer IDs
configure({ length: 16 });
dropid('user'); // → user_a3f2b9c1d4e5k7j9

// Use dashes
configure({ separator: '-' });
dropid('post'); // → post-x7k9m2n4p1q8

// Use custom alphabet
configure({ alphabet: '0123456789ABCDEF' });
dropid('order'); // → order_A3F2B9C1D4E5

createPrefixedId(prefix, options?)

Creates a reusable ID generator with a fixed prefix.

Parameters:

  • prefix (string, required): The prefix to use
  • options (DropIdOptions, optional): Configuration

Returns: (modelName: string) => string

Examples:

import { createPrefixedId } from 'dropaphi';

// Create organization-specific generators
const acmeId = createPrefixedId('acme');
const globexId = createPrefixedId('globex');

acmeId('user');   // → acme_user_a3f2b9c1d4e5
acmeId('post');   // → acme_post_x7k9m2n4p1q8

globexId('user'); // → globex_user_k2j8n9m3l1p5

Presets

Alphabet presets:

import { alphabets } from 'dropaphi';

alphabets.alphanumeric // '0123456789abcdefghijklmnopqrstuvwxyz' (default)
alphabets.base58       // Bitcoin-style (no 0/O, 1/l/I confusion)
alphabets.hex          // '0123456789abcdef'
alphabets.base64url    // URL-safe base64
alphabets.numeric      // '0123456789' (not recommended)

Length presets:

import { lengths } from 'dropaphi';

lengths.short      // 8 chars  - ~4 years to 1% collision
lengths.medium     // 12 chars - ~200 years to 1% collision (default)
lengths.long       // 16 chars - ~10^12 years to 1% collision
lengths.extraLong  // 21 chars - essentially collision-proof

Advanced Usage

Multi-tenant SaaS

import { createPrefixedId } from 'dropaphi';

function getTenantIdGenerator(tenantId: string) {
  return createPrefixedId(tenantId);
}

// In your request handler
app.post('/api/:tenantId/users', async (req, res) => {
  const genId = getTenantIdGenerator(req.params.tenantId);
  
  const user = {
    id: genId('user'),           // → acme_user_x7k9m2n4p1q8
    name: req.body.name,
    tenantId: req.params.tenantId,
  };
  
  await db.users.insert(user);
  res.json(user);
});

Different Alphabets

import { configure, dropid, alphabets } from 'dropaphi';

// Base58 (no confusing characters)
configure({ alphabet: alphabets.base58 });
dropid('token'); // → token_Kx7mN3pQ2rT8

// Hexadecimal
configure({ alphabet: alphabets.hex });
dropid('log'); // → log_a3f2b9c1d4e5

Per-Call Options

import { dropid } from 'dropaphi';

// Override global config for specific calls
const shortId = dropid('temp', undefined, { length: 6 });
// → temp_a3f2b9

const customId = dropid('special', 'app', { 
  length: 16, 
  separator: '-' 
});
// → app-special-k8j3m9n2l4p6q1r7

Security

Collision Resistance

DropID uses nanoid which is cryptographically secure:

  • Default (12 chars): ~200 years to 1% collision at 1,000 IDs/second
  • 16 chars: ~10^12 years to 1% collision at 1,000 IDs/second
  • 21 chars: Even safer (nanoid default)

No Sequential Enumeration

Unlike sequential IDs (user_1, user_2, user_3), Drop-API-ID generates random IDs that prevent:

  • Enumerating all users
  • Guessing valid IDs
  • Inferring business metrics

URL Safety

Default alphabet (0-9a-z) and separator (_) are URL-safe and work in:

  • URLs without encoding
  • Filenames
  • Database queries
  • Log files

Performance

  • Generation speed: ~2-3 million IDs per second
  • Memory usage: Negligible (single function call)
  • Bundle size: ~2KB minified + gzipped
  • No external dependencies except nanoid

TypeScript Support

DropID is written in TypeScript and provides full type safety:

import { dropid, configure, createPrefixedId, DropIdOptions } from 'dropaphi';

// All functions are fully typed
const id: string = dropid('user');

// Options are type-checked
const options: DropIdOptions = {
  length: 12,
  alphabet: '0123456789abcdefghijklmnopqrstuvwxyz',
  separator: '_',
};

configure(options);

// Generators are typed
const genId: (modelName: string) => string = createPrefixedId('acme');

Comparison

Feature Drop-API-ID UUID ULID CUID
Human-readable prefix
Customizable format Limited
Collision-resistant
Short length
Multi-tenant support
TypeScript native

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)
  5. Open a Pull Request

License

MIT © Gold Dick

Acknowledgments

  • Built with nanoid by Andrey Sitnik
  • Inspired by Stripe's ID format
  • Thanks to the open source community

Made with ❤️ by developers who are tired of debugging UUIDs

About

Human-readable, prefixed unique identifiers for database models

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published