Skip to main content

TYTX Logo

PyPI npm Python 3.10+ License Status
Tests Coverage Documentation

genro-tytx

A lightweight multi-transport typed data interchange system.

TYTX eliminates manual type conversions between Python and JavaScript, and makes switching to MessagePack for better performance as simple as changing a parameter.

You send a Decimal from Python, JavaScript receives a string. You convert it back. Every. Single. Time. TYTX fixes this—types flow automatically between Python and JavaScript, over JSON, XML, or MessagePack.

The Pain You Know

# Your Python API
return {"price": Decimal("99.99"), "due_date": date(2025, 1, 15)}
// Your JavaScript client
const data = await response.json();
// data.price is "99.99" (string) - need to convert
// data.due_date is "2025-01-15" (string) - need to convert

const price = new Decimal(data.price);      // Manual conversion
const dueDate = new Date(data.due_date);    // Manual conversion

This leads to:

  • Conversion code scattered everywhere
  • Bugs when someone forgets to convert
  • Financial calculations with floating-point errors
  • Different date formats causing off-by-one-day bugs

The TYTX Solution

# Server - just return native types
return {"price": Decimal("99.99"), "due_date": date(2025, 1, 15)}
// Client - types arrive ready to use
const data = await fetchTytx('/api/order');
data.price      // → Decimal (not string)
data.due_date   // → Date (not string)

Zero conversion code. Types just work.

30-Second Demo

Python:

pip install genro-tytx
from decimal import Decimal
from datetime import date
from genro_tytx import to_tytx, from_tytx

# Encode
data = {"price": Decimal("99.99"), "date": date(2025, 1, 15)}
encoded = to_tytx(data)
# '{"price": "99.99::N", "date": "2025-01-15::D"}::JS'

# Decode
decoded = from_tytx(encoded)
# {"price": Decimal("99.99"), "date": date(2025, 1, 15)}

JavaScript:

npm install genro-tytx big.js
import { fetchTytx } from 'genro-tytx';
import Big from 'big.js';

const result = await fetchTytx('/api/invoice', {
    body: { price: new Big('99.99'), date: new Date() }
});
// result.total → Big (ready to use)

Untyped JSON codec

Besides the typed to_tytx / from_tytx API, TYTX exposes a plain JSON codec for the untyped path — no type suffixes, just fast JSON (orjson when available, stdlib otherwise):

from genro_tytx import json_dumps, json_loads

json_dumps({"a": 1})        # -> b'{"a":1}'   (UTF-8 bytes)
json_loads('{"a": 1}')      # accepts str or bytes -> {"a": 1}

json_dumps returns UTF-8 bytes (ready for ASGI/WebSocket send), json_loads accepts str or bytes. Use these when you want plain JSON; use to_tytx / from_tytx when you want typed values to flow.

Transport MIME types

The TYTX media types and the content-type resolver are available as package constants, so HTTP layers do not hardcode the strings:

from genro_tytx import TRANSPORT_MIME, MIME_TRANSPORT, get_transport

TRANSPORT_MIME["json"]                       # 'application/vnd.tytx+json'
MIME_TRANSPORT["application/vnd.tytx+xml"]   # 'xml'
get_transport("application/vnd.tytx+json")   # 'json'  (substring match, standard MIME resolves too)

Custom types

register_type lets an external package teach TYTX a new type — a serialize hook (object → string) and a deserialize hook (string → object) under a custom suffix. The package registers at its own import time, so TYTX gains no dependency on it:

from genro_tytx import register_type, to_tytx, from_tytx

class Point:
    def __init__(self, x, y): self.x, self.y = x, y

register_type(Point, "PT", lambda p: f"{p.x},{p.y}",
              lambda s: Point(*map(int, s.split(","))))

to_tytx([1, Point(5, 6), "k"])   # '[1,"5,6::PT","k"]::JS'
from_tytx('5,6::PT')             # Point(5, 6)

The custom type flows like any built-in scalar, including nested inside dicts and lists.

When the type owns its serialization, register_class reads the hooks from the class itself — usable as a decorator. It needs __tytx_suffix__, an instance to_tytx() and a from_tytx classmethod (from_tytx must be a classmethod: decoding starts from the suffix and rebuilds the instance from scratch):

from genro_tytx import register_class

@register_class
class Point:
    __tytx_suffix__ = "PT"
    def __init__(self, x, y): self.x, self.y = x, y
    def to_tytx(self): return f"{self.x},{self.y}"
    @classmethod
    def from_tytx(cls, s): return cls(*map(int, s.split(",")))

Installation

# Python
pip install genro-tytx

# JavaScript/TypeScript
npm install genro-tytx

# Recommended: decimal library for JS
npm install big.js  # lightweight, good for most cases
# or: npm install decimal.js  # more features

Real-World Example: Order Processing

A typical business scenario: process an order with 8 typed fields, return 6 typed results.

1. The Data

JavaScript Client has order data with proper types:

import Big from 'big.js';

const orderData = {
    unit_price: new Big('149.99'),      // Decimal
    quantity: 3,
    discount: new Big('10.00'),         // Decimal
    order_date: new Date(2025, 0, 15),  // Date
    delivery_date: new Date(2025, 0, 20),
    express: true,
    customer_id: 12345,
    notes: 'Handle with care'
};

// Expects response: { subtotal, tax, shipping, total, ship_date, arrival_date }

Python Server has business logic expecting proper types:

async def process_order(unit_price, quantity, discount, order_date,
                        delivery_date, express, customer_id, notes):
    """Business logic - expects Decimal and date types."""
    subtotal = unit_price * quantity
    tax = subtotal * Decimal('0.22')
    shipping = Decimal('15.00') if express else Decimal('5.00')
    total = subtotal - discount + tax + shipping
    ship_date = order_date + timedelta(days=1 if express else 3)
    return {
        'subtotal': subtotal, 'tax': tax, 'shipping': shipping,
        'total': total, 'ship_date': ship_date, 'arrival_date': delivery_date
    }

2. ❌ WITHOUT TYTX: 20 Manual Conversions

JavaScript - must convert to/from strings:

// Send: convert types → strings
const response = await fetch('/api/process_order', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
        unit_price: orderData.unit_price.toString(),           // Decimal → string
        discount: orderData.discount.toString(),               // Decimal → string
        order_date: orderData.order_date.toISOString().slice(0, 10),  // Date → string
        delivery_date: orderData.delivery_date.toISOString().slice(0, 10),
        quantity: orderData.quantity, express: orderData.express,
        customer_id: orderData.customer_id, notes: orderData.notes
    })
});

// Receive: convert strings → types
const json = await response.json();
const result = {
    subtotal: new Big(json.subtotal),     tax: new Big(json.tax),
    shipping: new Big(json.shipping),     total: new Big(json.total),
    ship_date: new Date(json.ship_date),  arrival_date: new Date(json.arrival_date)
};

Python - must convert to/from strings:

@app.post("/api/process_order")
async def handle_order(request: Request):
    json_data = await request.json()

    # Receive: convert strings → types
    unit_price = Decimal(json_data['unit_price'])
    discount = Decimal(json_data['discount'])
    order_date = date.fromisoformat(json_data['order_date'])
    delivery_date = date.fromisoformat(json_data['delivery_date'])

    result = await process_order(unit_price, json_data['quantity'], discount,
        order_date, delivery_date, json_data['express'],
        json_data['customer_id'], json_data['notes'])

    # Send: convert types → strings
    return JSONResponse({
        'subtotal': str(result['subtotal']), 'tax': str(result['tax']),
        'shipping': str(result['shipping']), 'total': str(result['total']),
        'ship_date': result['ship_date'].isoformat(),
        'arrival_date': result['arrival_date'].isoformat()
    })

Total: 20 manual conversions (4 JS→string + 4 string→Python + 6 Python→string + 6 string→JS).


3. ✅ WITH TYTX: Zero Conversions

JavaScript:

import { fetchTytx } from 'genro-tytx';

const result = await fetchTytx('/api/process_order', { body: orderData });
console.log(result.total.toFixed(2));  // Big, ready to use

Python:

from genro_tytx import asgi_data, to_tytx

@app.post("/api/process_order")
async def handle_order(request: Request):
    data = await asgi_data(request.scope, request.receive)
    result = await process_order(**data['body'])
    return Response(content=to_tytx(result), media_type='application/vnd.tytx+json')

Total: 0 conversions. Types flow naturally.

Bonus: Switch to MessagePack in One Line

Need binary format for better performance? Just add transport: 'msgpack':

// JSON (default)
const result = await fetchTytx('/api/process_order', { body: orderData });

// MessagePack - same API, binary format
const result = await fetchTytx('/api/process_order', { body: orderData, transport: 'msgpack' });
# Server auto-detects transport from Content-Type header
# No code changes needed!

Supported Types

Python JavaScript Wire Format
Decimal Decimal (big.js) "99.99::N"
date Date (midnight UTC) "2025-01-15::D"
datetime Date "2025-01-15T10:30:00.000Z::DHZ"
time Date (epoch date) "10:30:00.000::H"

Native JSON types (string, number, boolean, null) pass through unchanged.

When to Use TYTX

Good fit:

  • Web apps with forms containing dates/decimals
  • Financial applications requiring decimal precision
  • APIs that send/receive typed data frequently
  • Excel-like grids with mixed types

Not needed:

  • APIs that only use strings and integers
  • Simple CRUD with no special types
  • Already using GraphQL/Protobuf with full type support

Documentation

I want to... Go to...
Try it in 5 minutes Quick Start
Use with FastAPI/Flask HTTP Integration
Understand the wire format How It Works
See API reference API Reference
Compare with alternatives Alternatives

License

Apache License 2.0 - Copyright 2025 Softwell S.r.l.

Download files

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

Source Distribution

genro_tytx-0.12.0.tar.gz (392.4 kB view details)

Uploaded Source

Built Distribution

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

genro_tytx-0.12.0-py3-none-any.whl (20.4 kB view details)

Uploaded Python 3

File details

Details for the file genro_tytx-0.12.0.tar.gz.

File metadata

  • Download URL: genro_tytx-0.12.0.tar.gz
  • Upload date:
  • Size: 392.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for genro_tytx-0.12.0.tar.gz
Algorithm Hash digest
SHA256 c063f544cb023fdc9f6d964169e2a836e2f6e028693d305bda00947ce894ae69
MD5 788ce7c285c9706e8f1438e0453324c6
BLAKE2b-256 5f7727d59f31767c17e8d06fe9319ff1b4461b98063954049f006be2fa24972a

See more details on using hashes here.

Provenance

The following attestation bundles were made for genro_tytx-0.12.0.tar.gz:

Publisher: publish.yml on genropy/genro-tytx

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

File details

Details for the file genro_tytx-0.12.0-py3-none-any.whl.

File metadata

  • Download URL: genro_tytx-0.12.0-py3-none-any.whl
  • Upload date:
  • Size: 20.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for genro_tytx-0.12.0-py3-none-any.whl
Algorithm Hash digest
SHA256 aa9278e397b2c4a7e19117a0a0f4dfede1ac98a5601ac24446e60f9520c71542
MD5 a61dedf93e2f4f2447b7308d6f710744
BLAKE2b-256 ef5a8ac0b2dac48c00442fe19b3b73cf265d1dcfa6be082b3553a7fc4e324a59

See more details on using hashes here.

Provenance

The following attestation bundles were made for genro_tytx-0.12.0-py3-none-any.whl:

Publisher: publish.yml on genropy/genro-tytx

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

Release history Release notifications | RSS feed

0.12.2

2 files

0.12.1

2 files

This release

0.12.0 This release

2 files

0.11.0

2 files

0.10.1

2 files

0.10.0

2 files

0.9.3

2 files

0.9.2

2 files

0.9.1

2 files

0.9.0

2 files

0.8.0

2 files

0.7.4

2 files

0.7.3

2 files

0.7.2

2 files

0.7.0

2 files

0.6.1

2 files

0.6.0

2 files

0.5.1

2 files

0.5.0

2 files

0.3.0

2 files

0.1.3

2 files

0.1.2

2 files

0.1.1

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page