Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:

- name: Sync dependencies (locked)
run: |
uv sync --locked --all-groups
uv sync --locked --all-groups --all-extras

- name: Build & install native extension (maturin develop)
uses: PyO3/maturin-action@v1
Expand Down Expand Up @@ -55,7 +55,7 @@ jobs:

- name: Sync dependencies (locked)
run: |
uv sync --locked --all-groups
uv sync --locked --all-groups --all-extras

- name: Build & install native extension (maturin develop)
uses: PyO3/maturin-action@v1
Expand Down
57 changes: 50 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
# TypeID Python

[![Run Tests](https://github.com/akhundMurad/typeid-python/actions/workflows/test.yml/badge.svg)](https://github.com/akhundMurad/typeid-python/actions/workflows/test.yml)
[![PyPI Downloads](https://static.pepy.tech/personalized-badge/typeid-python?period=total&units=INTERNATIONAL_SYSTEM&left_color=BLACK&right_color=GREEN&left_text=downloads)](https://pepy.tech/projects/typeid-python)
[![PyPI - Version](https://img.shields.io/pypi/v/typeid-python?color=green)](https://pypi.org/project/typeid-python/)
[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/typeid-python?color=green)](https://pypi.org/project/typeid-python/)
[![PyPI Downloads](https://static.pepy.tech/personalized-badge/typeid-python?period=total&units=INTERNATIONAL_SYSTEM&left_color=BLACK&right_color=GREEN&left_text=downloads)](https://pepy.tech/projects/typeid-python)
![GitHub License](https://img.shields.io/github/license/akhundMurad/typeid-python)

> [!WARNING]
> `main` may contain unreleased changes. For stable usage, use the latest release tag.

A **high-performance Python implementation of [TypeIDs](https://github.com/jetpack-io/typeid)** — type-safe,
sortable identifiers based on **UUIDv7**.
Expand All @@ -21,11 +23,12 @@ This library provides a Python package with Rust acceleration.
## Key features

- ✅ UUIDv7-based, time-sortable identifiers
- ✅ Type-safe prefixes (`user_`, `order_`, …)
- ✅ Human-readable and URL-safe
- ✅ Schema-based ID explanations (JSON / YAML)
- ✅ Fast generation & parsing (Rust-accelerated)
- ✅ Multiple integrations (Pydantic, FastAPI, ...)
- ✅ Type-safe prefixes (`user_`, `order_`, ...)
- ✅ Human-readable and URL-safe
- ✅ CLI tools (`new`, `encode`, `decode`, `explain`)
- ✅ Schema-based ID explanations (JSON / YAML)
- ✅ Fully offline, no external services

## Performance
Expand Down Expand Up @@ -70,8 +73,9 @@ Included:
### Other optional extras

```console
$ pip install typeid-python[yaml] # YAML schema support
$ pip install typeid-python[cli] # CLI tools
$ pip install typeid-python[yaml] # YAML schema support
$ pip install typeid-python[cli] # CLI tools
$ pip install typeid-python[pydantic] # Pydantic integration
```

Extras are **strictly optional**.
Expand Down Expand Up @@ -149,6 +153,45 @@ Encode:
$ typeid encode 0188bac7-4afa-78aa-bc3b-bd1eef28d881 --prefix user
```

## Framework integrations

TypeID is **framework-agnostic by design**.
Integrations are provided as optional adapters, installed explicitly and kept separate from the core.

### Available integrations

* **Pydantic (v2)**
Native field type with validation and JSON Schema support.

```bash
pip install typeid-python[pydantic]
```

```python
from typing import Literal
from pydantic import BaseModel
from typeid.integrations.pydantic import TypeIDField

class User(BaseModel):
id: TypeIDField[Literal["user"]]
```

* **FastAPI**
Works automatically via Pydantic (request/response models, OpenAPI).

```bash
pip install typeid-python[fastapi]
```

* **SQLAlchemy**
Column types for storing TypeIDs (typically as strings).

```bash
pip install typeid-python[sqlalchemy]
```

All integrations are **opt-in via extras** and never affect the core package.

## ✨ `typeid explain` — understand any ID

```console
Expand Down
72 changes: 72 additions & 0 deletions docs/integrations/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Framework Integrations

TypeID is designed to be framework-agnostic.
The core package does not depend on any web framework, ORM, or serialization library.

Integrations are provided as **optional** adapters that translate between TypeID and specific ecosystems.
They are installed explicitly and never affect the core unless imported.

---

## Available integrations

### Pydantic (v2)

Native support for using TypeID in Pydantic v2 models.

* Prefix-aware validation
* Accepts `str` and `TypeID`
* Serializes as string
* Clean JSON Schema / OpenAPI output

```bash
pip install typeid-python[pydantic]
```

See: [Pydantic v2](https://akhundmurad.github.io/typeid-python/integrations/pydantic/) documentation page for details and examples.

### FastAPI

FastAPI builds on Pydantic v2, so TypeID works automatically in:

* request models
* response models
* OpenAPI schemas

No separate FastAPI-specific adapter is required.

```bash
pip install typeid-python[fastapi]
```

See: [Pydantic v2](https://akhundmurad.github.io/typeid-python/integrations/fastapi/) documentation page for details and examples.

### SQLAlchemy

Column types for storing TypeIDs in relational databases.

Typical usage stores the full TypeID string (`prefix_suffix`) for clarity and debuggability.

```bash
pip install typeid-python[sqlalchemy]
```

See: [Pydantic v2](https://akhundmurad.github.io/typeid-python/integrations/sqlalchemy/) documentation page for details and examples.

## Design notes

* The TypeID core never imports framework code
* Validation rules live in the core, not in adapters
* Integrations are thin and easy to replace or extend

This keeps the library stable even as frameworks evolve.

## Adding new integrations

If you want to integrate TypeID with another framework:

* keep the adapter small,
* avoid duplicating validation logic,
* depend on the TypeID core as the single source of truth.

Community integrations are welcome.
179 changes: 179 additions & 0 deletions docs/integrations/pydantic.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
# Pydantic v2 integration

TypeID ships with an **optional Pydantic v2 adapter**.
It lets you use `TypeID` in Pydantic models without pulling Pydantic into the TypeID core.

The adapter:

* validates values using the TypeID core,
* optionally enforces a fixed prefix,
* serializes TypeIDs as strings,
* exposes sensible JSON Schema metadata.

---

## Installation

```bash
pip install typeid-python[pydantic]
```

This installs the latest version of Pydantic v2.

## Basic usage

Use `TypeIDField` with a fixed prefix.

```python
from typing import Literal
from pydantic import BaseModel
from typeid.integrations.pydantic import TypeIDField

class User(BaseModel):
id: TypeIDField[Literal["user"]]

u = User(id="user_01ke82dtesfn9bjcrzyzz54ya9")
assert str(u.id) == "user_01ke82dtesfn9bjcrzyzz54ya9"
```

## Accepted inputs

You can pass either a string or a `TypeID` instance.

```python
from typing import Literal
from pydantic import BaseModel
from typeid.integrations.pydantic import TypeIDField

class User(BaseModel):
id: TypeIDField[Literal["user"]]

u = User(id="user_01ke82dtesfn9bjcrzyzz54ya9")
assert u.id is not None
```

```python
from typing import Literal
from pydantic import BaseModel
from typeid import TypeID
from typeid.integrations.pydantic import TypeIDField

class User(BaseModel):
id: TypeIDField[Literal["user"]]

tid = TypeID.from_string("user_01ke82dtesfn9bjcrzyzz54ya9")
u = User(id=tid)

assert u.id == tid
```

In both cases, `id` is stored as a `TypeID` object inside the model.

## Prefix validation

The prefix in `TypeIDField[...]` is enforced.

```python
import pytest
from typing import Literal
from pydantic import BaseModel, ValidationError
from typeid.integrations.pydantic import TypeIDField

class Order(BaseModel):
id: TypeIDField[Literal["order"]]

with pytest.raises(ValidationError):
Order(id="user_01ke82dtesfn9bjcrzyzz54ya9")
```

This fails with a validation error because the prefix does not match.

This is useful when you want the model itself to encode domain meaning
(e.g. *this field must be a user ID, not just any ID*).

## Serialization

When exporting a model, TypeIDs are always serialized as strings.

```python
from typing import Literal
from pydantic import BaseModel
from typeid.integrations.pydantic import TypeIDField

class User(BaseModel):
id: TypeIDField[Literal["user"]]

u = User(id="user_01ke82dtesfn9bjcrzyzz54ya9")
data = u.model_dump(mode="json")

assert data == {"id": "user_01ke82dtesfn9bjcrzyzz54ya9"}
```

```python
from typing import Literal
from pydantic import BaseModel
from typeid.integrations.pydantic import TypeIDField

class User(BaseModel):
id: TypeIDField[Literal["user"]]

u = User(id="user_01ke82dtesfn9bjcrzyzz54ya9")
json_data = u.model_dump_json()

assert json_data == '{"id":"user_01ke82dtesfn9bjcrzyzz54ya9"}'
```

This keeps JSON output simple and predictable.

## JSON Schema / OpenAPI

The generated schema looks roughly like this:

```yaml
id:
type: string
format: typeid
description: TypeID with prefix 'user'
examples:
- user_01ke82dtesfn9bjcrzyzz54ya9
```

Notes:

* The schema does not hard-code internal regex details.
* Actual validation is handled by the TypeID core.
* The schema is meant to document intent, not re-implement parsing rules.

## Why `Literal["user"]`?

The recommended form is:

```text
TypeIDField[Literal["user"]]
```

This works cleanly with:

* Ruff
* Pyright / MyPy
* IDE type checkers

Using `Literal` makes the prefix a real compile-time constant and avoids
annotation edge cases.

## FastAPI

FastAPI uses Pydantic v2, so no extra integration is needed.

TypeID fields work automatically in request and response models,
including OpenAPI output, as soon as you use them in a Pydantic model.

## Design notes

* The TypeID core does not import Pydantic.
* All framework-specific code lives in `typeid.integrations.pydantic`.
* Parsing and validation rules live in the core, not in the adapter.

This keeps the integration small and easy to maintain.

*That’s it — no magic, no hidden behavior.*
2 changes: 1 addition & 1 deletion docs/reference/cli.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# CLI

::: typeid.cli
::: typeid.cli.main
2 changes: 1 addition & 1 deletion docs/reference/factory.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# Factory

::: typeid.factory
::: typeid.core.factory
3 changes: 3 additions & 0 deletions docs/reference/integrations/pydantic/v2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Pydantic V2 Integration

::: typeid.integrations.pydantic.v2
2 changes: 1 addition & 1 deletion docs/reference/typeid.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# TypeID

::: typeid.typeid.TypeID
::: typeid.TypeID
Loading