🔖

DropID

Human-readable, prefixed unique identifiers for your database models

v1.0.0MIT LicenseTypeScript

Overview

DropID is a lightweight TypeScript library that generates human-readable, prefixed unique identifiers for database models. Instead of cryptic UUIDs like 550e8400-e29b-41d4-a916-446655440000, you get readable IDs like user_a3f2b9c1d4e5.

Before (UUID)

550e8400-e29b-41d4-a916-446655440000

Hard to read, no context

After (DropID)

user_a3f2b9c1d4e5

Instantly readable, contextual

Installation

npm install drop-api-id

Or using yarn: yarn add drop-api-id

Quick Start

Basic Usage

import { dropid } from 'drop-api-id';

// Generate a simple ID
const userId = dropid('user');
// → user_a3f2b9c1d4e5

// With prefix (for multi-tenant apps)
const orderId = dropid('order', 'acme');
// → acme_order_x7k9m2n4p1q8

// With custom options
const customId = dropid('post', 'blog', { length: 16 });
// → blog_post_k8j3m9n2l4p6q1r7

API Reference

dropid(modelName, prefix?, options?)

Generates a unique ID with optional prefix.

Parameters:
  • modelName (string, required) - The model/table name
  • prefix (string, optional) - Additional prefix for namespacing
  • options (object, optional) - Configuration options
Returns:

string - The generated unique ID

configure(options)

Set global configuration for all subsequent ID generations.

configure({
  length: 16,           // Length of random part
  alphabet: '0-9a-z',   // Characters to use
  separator: '-'        // Separator between parts
});

createPrefixedId(prefix, options?)

Creates a reusable ID generator with a fixed prefix.

const acmeId = createPrefixedId('acme');

acmeId('user');   // → acme_user_a3f2b9c1d4e5
acmeId('order');  // → acme_order_x7k9m2n4p1q8

ORM Integration

Drizzle ORM

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

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 orders = pgTable('orders', {
  id: text('id').primaryKey().$defaultFn(() => dropid('order', 'shop')),
  userId: text('user_id').references(() => users.id),
  total: text('total').notNull(),
});

Prisma

import { dropid } from 'drop-api-id';

const user = await prisma.user.create({
  data: {
    id: dropid('user'),
    name: 'John Doe',
    email: 'john@example.com',
  },
});

const order = await prisma.order.create({
  data: {
    id: dropid('order', 'shop'),
    userId: user.id,
    total: '99.99',
  },
});

TypeORM

import { Entity, PrimaryColumn, Column, BeforeInsert } from 'typeorm';
import { dropid } from 'drop-api-id';

@Entity()
export class User {
  @PrimaryColumn()
  id: string;

  @Column()
  name: string;

  @Column()
  email: string;

  @BeforeInsert()
  generateId() {
    this.id = dropid('user');
  }
}

Real-World Examples

Multi-Tenant SaaS Application

import { createPrefixedId } from 'drop-api-id';

// Create tenant-specific ID generators
function getTenantIdGenerator(tenantId: string) {
  return createPrefixedId(tenantId);
}

// In your API route
app.post('/api/:tenantId/users', async (req, res) => {
  const genId = getTenantIdGenerator(req.params.tenantId);
  
  const user = {
    id: genId('user'),           // → acme_user_a3f2b9c1d4e5
    workspaceId: genId('workspace'), // → acme_workspace_x7k9m2n4
    ...req.body
  };
  
  await db.users.insert(user);
  res.json(user);
});

Using Different Alphabets

import { dropid, configure, alphabets } from 'drop-api-id';

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

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

// Reset to default
configure({ alphabet: alphabets.alphanumeric });

Security & Performance

Collision Resistance

  • • 12 chars: ~200 years at 1K/sec
  • • 16 chars: ~10^12 years at 1K/sec
  • • Uses nanoid (crypto-secure)

Performance

  • • 2-3M IDs per second
  • • Zero network latency
  • • Only 2KB bundle size

🔒 Security Benefits

  • ✓ No sequential enumeration - prevents guessing attacks
  • ✓ URL-safe characters - works in URLs without encoding
  • ✓ Database-friendly - works as VARCHAR primary key

Need help?

Check out our full documentation or reach out to support.