Getting Started
Installation
Get safeids into your TypeScript project in under two minutes.
Install the package
safeids has no runtime dependencies. It works with any TypeScript project — Next.js, Remix, Node, tRPC, Prisma, you name it.
npmpnpmyarnbun
npm install safeids⚠️ Requires
TypeScript 4.7+ and strict: true in your tsconfig. The strict flag is what makes branded types incompatible with each other.Your first branded ID
Create a src/types/ids.ts file and define your domain entities:
src/types/ids.ts
import { brand, createId } from 'safeids'
// 1. Declare the branded type
export type UserId = brand<string, 'UserId'>
export type OrderId = brand<string, 'OrderId'>
// 2. Create a typed ID generator for each entity
export const newUserId = () => createId<UserId>('usr')
export const newOrderId = () => createId<OrderId>('ord')Use it everywhere
src/services/user.ts
import type { UserId } from '@/types/ids'
// Functions that need a UserId now say so explicitly
export async function getUser(id: UserId) {
return db.users.findOne({ id })
}
export async function deleteUser(id: UserId) {
await db.users.delete({ id })
}Watch TypeScript catch mistakes
import { newUserId, newOrderId } from '@/types/ids'
import { getUser } from '@/services/user'
const userId = newUserId() // type: UserId
const orderId = newOrderId() // type: OrderId
await getUser(userId) // ✓ fine
await getUser(orderId) // ✗ Error: Argument of type 'OrderId' is not
// assignable to parameter of type 'UserId'What the IDs look like at runtime
safeids generates human-readable prefixed IDs using crypto.randomUUID() or crypto.getRandomValues. The prefix is optional but highly recommended — it makes IDs instantly recognisable in logs, databases, and error messages.
import { createId } from 'safeids'
import type { UserId, OrderId } from '@/types/ids'
const userId = createId<UserId>('usr') // → "usr_k2p9xm"
const orderId = createId<OrderId>('ord') // → "ord_f7qrn1"
// Without a prefix (still typed, just no prefix)
const anon = createId<UserId>() // → "a8mn2q"