Skip to main content

friendly-id is a Python library to generate really unique and url friendly IDs based on UUID and base62

Project description

FriendlyId

test friendly-id

Inspired by FriendlyID

What is the FriendlyId library?

The FriendlyID library provides a UUID subclass that uses base62 encoding for its string representation. This makes UUIDs more compact and URL-friendly, reducing their length from the standard 36 characters to at most 22. An example is shown below:

UUID                                        FriendlyID

c3587ec5-0976-497f-8374-61e0c2ea3da5   ->   5wbwf6yUxVBcr48AMbz9cb
|                                           |
36 characters                               22 characters or less

FriendlyID extends the standard UUID class, providing all the functionality of a regular UUID while displaying as a compact, URL-friendly string by default.

Key Features

  • Automatic friendly display: str(friendly_id) returns the base62 format
  • Access to both formats: .friendly property for base62, .standard property for UUID format
  • Drop-in replacement: Works with existing code that expects UUID objects
  • Convert from a FriendlyID back to the original UUID format

Why use a FriendlyID?

Universal Unique IDs (UUIDs) provide a non-sequential and unique identifier that can be generated separately from the source database. As a result, it is not possible to guess either the previous or next identifier. That's great, but, to achieve this level of security, a UUID is long (128 bits long) and looks ugly (36 alphanumeric characters including four hyphens which are added to make it easier to read the UUID), as in this example: 123e4567-e89b-12d3-a456-426655440000.

Such a format is:

  • Cannot be copied with just two mouse-clicks (you have to select manually the start and end positions)
  • difficult to read
  • difficult to remember

FriendlyID library solves these problems by extending the standard UUID class and overriding its string representation to use Base62 with alphanumeric characters in the range [0-9A-Za-z] into a compact representation which consists of a maximum of 22 characters (but in fact often contains fewer characters).

Usage

FriendlyId can be installed through PyPI:

python -m pip install friendly-id

Basic Usage

Generate a random FriendlyID:

from friendly_id import FriendlyID

# Generate a random FriendlyID
fuid = FriendlyID.random()
print(fuid)  # Prints base62 format, e.g., "5wbwf6yUxVBcr48AMbz9cb"
print(f"User ID: {fuid}")  # Perfect for URLs and display

Create from existing UUID:

import uuid
from friendly_id import FriendlyID

# Convert existing UUID
regular_uuid = uuid.uuid4()
fuid = FriendlyID.from_uuid(regular_uuid)
print(fuid)  # Base62 format

Create from base62 string:

from friendly_id import FriendlyID

# Create from friendly string
fuid = FriendlyID.from_friendly("5wbwf6yUxVBcr48AMbz9cb")
print(fuid.standard)  # c3587ec5-0976-497f-8374-61e0c2ea3da5

Access Different Formats

from friendly_id import FriendlyID

fuid = FriendlyID.random()

# Base62 format (default string representation)
print(str(fuid))       # e.g., "5wbwf6yUxVBcr48AMbz9cb"
print(fuid.friendly)   # Same as str(fuid)

# Standard UUID format
print(fuid.standard)   # e.g., "c3587ec5-0976-497f-8374-61e0c2ea3da5"

# Convert back to regular UUID
regular_uuid = fuid.to_uuid()
print(regular_uuid)    # Standard UUID object

UUID Compatibility

Since FriendlyID extends UUID, it works everywhere a UUID is expected:

import uuid
from friendly_id import FriendlyID

fuid = FriendlyID.random()

# All UUID properties and methods work
print(fuid.version)    # 4
print(fuid.hex)        # Hexadecimal representation
print(fuid.bytes)      # Bytes representation

# Type checking
isinstance(fuid, uuid.UUID)  # True
isinstance(fuid, FriendlyID)  # True

# Equality with regular UUIDs
regular_uuid = uuid.UUID(fuid.standard)
fuid == regular_uuid  # True

# Use in collections
uuid_set = {fuid, regular_uuid}  # Only one item (they're equal)

SQLAlchemy Integration

FriendlyID includes seamless SQLAlchemy integration through an optional extra:

pip install friendly-id[sqlalchemy]

FriendlyIDType

Stores UUIDs in the database's native UUID format while providing FriendlyID objects in Python:

from friendly_id import FriendlyID
from friendly_id.sqlalchemy_types import FriendlyIDType
from sqlalchemy import Text, create_engine
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column

class Base(DeclarativeBase):
    pass

class User(Base):
    __tablename__ = "users"

    id: Mapped[FriendlyID] = mapped_column(
        FriendlyIDType, primary_key=True, insert_default=FriendlyID.random
    )
    name: Mapped[str] = mapped_column(Text)
    email: Mapped[str] = mapped_column(Text)

# Usage
user = User(name="Alice", email="alice@example.com")
session.add(user)
session.commit()

print(user.id)  # Prints: 5wbwf6yUxVBcr48AMbz9cb (base62 format)
print(user.id.standard)  # Prints: c3587ec5-0976-497f-8374-61e0c2ea3da5

# Query by any format
alice = session.query(User).filter_by(id=user.id).first()  # FriendlyID
alice = session.query(User).filter_by(id=str(user.id)).first()  # base62 string
alice = session.query(User).filter_by(id=user.id.standard).first()  # UUID string

Database Compatibility

FriendlyIDType automatically selects the optimal storage format for each database:

  • PostgreSQL: Uses native UUID type for optimal performance and indexing
  • MySQL: Uses CHAR(36) for UUID string storage
  • SQLite: Uses TEXT for UUID string storage
  • Other databases: Falls back to string storage

Pydantic Integration

FriendlyID includes built-in Pydantic support for seamless integration with Pydantic models. Install the optional extra for enhanced features:

pip install friendly-id[pydantic]

Note: This library requires Pydantic v2.0 or higher. Pydantic v1 is no longer supported.

Basic Usage with Pydantic

from pydantic import BaseModel
from friendly_id.pydantic_types import PydanticFriendlyID as FriendlyID

class User(BaseModel):
    id: FriendlyID
    name: str
    email: str

# Create from various input types
user1 = User(id=FriendlyID.random(), name="John", email="john@example.com")
user2 = User(id="5wbwf6yUxVBcr48AMbz9cb", name="Jane", email="jane@example.com")  # base62
user3 = User(id="c3587ec5-0976-497f-8374-61e0c2ea3da5", name="Bob", email="bob@example.com")  # UUID

# Serialization automatically uses base62 format
print(user1.model_dump_json())
# {"id": "7mkedUHZ3nyAx11JWbR91z", "name": "John", "email": "john@example.com"}

Validation Features

FriendlyID automatically validates and converts:

  • Existing FriendlyID instances (pass-through)
  • Regular UUID objects
  • Base62 strings
  • Standard UUID strings
  • Rejects invalid formats with clear error messages

JSON Schema Support

FriendlyID provides proper JSON schema for OpenAPI generation:

schema = User.model_json_schema()
# FriendlyID fields include:
# - type: "string"
# - pattern: "^[0-9A-Za-z]+$" (base62 validation)
# - description: "A URL-friendly base62 encoded UUID"

Performance

FriendlyID involves a trade-off between CPU overhead and I/O efficiency:

  • CPU: ~6x slower base62 encoding (~3 microseconds per ID)
  • I/O: ~39% bandwidth savings (less than 22 vs 36 characters)
  • Database: Uses native UUID storage (no storage overhead)

Run the included benchmark to see detailed performance analysis:

python benchmark.py --count 1000

For detailed performance analysis, see PERFORMANCE.md.

Choosing Between FriendlyID and Standard UUID

FriendlyID presents a clear trade-off: 3 microseconds CPU overhead vs 14 characters I/O savings per ID.

Consider FriendlyID when:

  • Network bandwidth or data transfer costs are significant
  • User-facing URLs and identifiers matter for UX
  • Text-based logging and data export are frequent
  • Web APIs where transfer size matters
  • Mobile applications with bandwidth constraints

Consider Standard UUID when:

  • High-frequency serialization with minimal I/O
  • CPU resources are constrained or critical
  • Existing systems require standard UUID format
  • Microsecond-level performance is essential
  • Real-time systems where every cycle counts

Evaluation Factors

Consider your application's specific characteristics:

  • I/O vs CPU ratio: How much network/text processing vs computation?
  • Scale: Volume of UUID operations vs data transmission
  • Constraints: CPU limitations vs bandwidth costs
  • User experience: Does identifier readability matter?
  • Integration: Compatibility with existing systems

Run the benchmark with your expected workload to get concrete numbers for your specific use case.

Breaking Changes

⚠️ Important: 0.4.0 introduces breaking changes from previous versions:

  • str(FriendlyID) now returns base62 format instead of standard UUID format
  • Use .standard property when you need the standard UUID string format
  • JSON serialization will use base62 format by default
  • Update any code that expects str(uuid) to return standard UUID format

Migration Guide

If you were using the previous functional API:

# Old way (no longer available)
from friendly_id import friendly_id, encode, decode

id_str = friendly_id()
encoded = encode(some_uuid)
decoded = decode(some_string)

# New way
from friendly_id import FriendlyID

id_str = str(FriendlyID.random())
encoded = str(FriendlyID.from_uuid(some_uuid))
decoded = FriendlyID.from_friendly(some_string).to_uuid()

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

friendly_id-0.6.1.tar.gz (20.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

friendly_id-0.6.1-py3-none-any.whl (11.2 kB view details)

Uploaded Python 3

File details

Details for the file friendly_id-0.6.1.tar.gz.

File metadata

  • Download URL: friendly_id-0.6.1.tar.gz
  • Upload date:
  • Size: 20.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for friendly_id-0.6.1.tar.gz
Algorithm Hash digest
SHA256 598be9171700a4feb65f9110dbfc493a4e2b70476b62f691675b9c11f2021282
MD5 e4c66ee4d5748fb91a43d15602ea9cb0
BLAKE2b-256 7a16616b889e98518b0e3a1e59bd78ba043a0f1c3b2a671a0c2c815d96cf8080

See more details on using hashes here.

Provenance

The following attestation bundles were made for friendly_id-0.6.1.tar.gz:

Publisher: cd.yml on edwardzjl/friendly-id

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file friendly_id-0.6.1-py3-none-any.whl.

File metadata

  • Download URL: friendly_id-0.6.1-py3-none-any.whl
  • Upload date:
  • Size: 11.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for friendly_id-0.6.1-py3-none-any.whl
Algorithm Hash digest
SHA256 a645e9e9275802911fe33834d49ca636d1dbd6c89068d7c114d8e720a0a340e5
MD5 b3e471e44792ebbf1faaeee276ee677a
BLAKE2b-256 2ed73acb275dd52c5b599ad1aa975b368d93e485a1d36d5c8b878510b0646895

See more details on using hashes here.

Provenance

The following attestation bundles were made for friendly_id-0.6.1-py3-none-any.whl:

Publisher: cd.yml on edwardzjl/friendly-id

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page