friendly-id is a Python library to generate really unique and url friendly IDs based on UUID and base62
Project description
FriendlyId
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:
.friendlyproperty for base62,.standardproperty 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
.standardproperty 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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
598be9171700a4feb65f9110dbfc493a4e2b70476b62f691675b9c11f2021282
|
|
| MD5 |
e4c66ee4d5748fb91a43d15602ea9cb0
|
|
| BLAKE2b-256 |
7a16616b889e98518b0e3a1e59bd78ba043a0f1c3b2a671a0c2c815d96cf8080
|
Provenance
The following attestation bundles were made for friendly_id-0.6.1.tar.gz:
Publisher:
cd.yml on edwardzjl/friendly-id
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
friendly_id-0.6.1.tar.gz -
Subject digest:
598be9171700a4feb65f9110dbfc493a4e2b70476b62f691675b9c11f2021282 - Sigstore transparency entry: 311138816
- Sigstore integration time:
-
Permalink:
edwardzjl/friendly-id@c123e458efe2f156de88bc48fc064ddad8d81c7e -
Branch / Tag:
refs/tags/v0.6.1 - Owner: https://github.com/edwardzjl
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
cd.yml@c123e458efe2f156de88bc48fc064ddad8d81c7e -
Trigger Event:
push
-
Statement type:
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a645e9e9275802911fe33834d49ca636d1dbd6c89068d7c114d8e720a0a340e5
|
|
| MD5 |
b3e471e44792ebbf1faaeee276ee677a
|
|
| BLAKE2b-256 |
2ed73acb275dd52c5b599ad1aa975b368d93e485a1d36d5c8b878510b0646895
|
Provenance
The following attestation bundles were made for friendly_id-0.6.1-py3-none-any.whl:
Publisher:
cd.yml on edwardzjl/friendly-id
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
friendly_id-0.6.1-py3-none-any.whl -
Subject digest:
a645e9e9275802911fe33834d49ca636d1dbd6c89068d7c114d8e720a0a340e5 - Sigstore transparency entry: 311138832
- Sigstore integration time:
-
Permalink:
edwardzjl/friendly-id@c123e458efe2f156de88bc48fc064ddad8d81c7e -
Branch / Tag:
refs/tags/v0.6.1 - Owner: https://github.com/edwardzjl
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
cd.yml@c123e458efe2f156de88bc48fc064ddad8d81c7e -
Trigger Event:
push
-
Statement type: