https://github.com/golddick/dropid.git
Human-readable, prefixed unique identifiers for your database models.
dropid('user') // → user_a3f2b9c1d4e5
dropid('post', 'app') // → app_post_x7k9m2n4p1q8Before (UUIDs):
550e8400-e29b-41d4-a916-446655440000
Good luck debugging that in your logs.
After (DropID):
user_a3f2b9c1d4e5
Instantly know what you're looking at.
- 🎯 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
npm install dropid
# or
yarn add dropid
# or
pnpm add dropidimport { 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',
};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),
});// 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,
},
});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;
}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 namespacingoptions(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_k8j3m9n2l4p6q1r7Set 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_A3F2B9C1D4E5Creates a reusable ID generator with a fixed prefix.
Parameters:
prefix(string, required): The prefix to useoptions(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_k2j8n9m3l1p5Alphabet 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-proofimport { 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);
});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_a3f2b9c1d4e5import { 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-k8j3m9n2l4p6q1r7DropID 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)
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
Default alphabet (0-9a-z) and separator (_) are URL-safe and work in:
- URLs without encoding
- Filenames
- Database queries
- Log files
- Generation speed: ~2-3 million IDs per second
- Memory usage: Negligible (single function call)
- Bundle size: ~2KB minified + gzipped
- No external dependencies except nanoid
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');| Feature | Drop-API-ID | UUID | ULID | CUID |
|---|---|---|---|---|
| Human-readable prefix | ✅ | ❌ | ❌ | ❌ |
| Customizable format | ✅ | ❌ | ❌ | Limited |
| Collision-resistant | ✅ | ✅ | ✅ | ✅ |
| Short length | ✅ | ❌ | ❌ | ✅ |
| Multi-tenant support | ✅ | ❌ | ❌ | ❌ |
| TypeScript native | ✅ | ✅ | ✅ | ✅ |
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
MIT © Gold Dick
- 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