Skip to main content

Bridge Python backends to TypeScript frontends with automatic type-safe client generation

Project description

Zynk

A modern Python-TypeScript bridge with automatic type generation and hot-reloading.

Zynk creates a seamless developer experience for building "Local First" web applications where Python handles the backend logic and TypeScript/React handles the UI. Inspired by PyTauri's command system, but designed for standard web frameworks.

📦 Installation

pip install zynk

Or with development dependencies:

pip install zynk[dev]

🚀 Quick Start

1. Define Your Commands (Python)

# users.py
from pydantic import BaseModel
from zynk import command

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

@command
async def get_user(user_id: int) -> User:
    """Get a user by ID."""
    return User(id=user_id, name="Alice", email="alice@example.com")

@command
async def list_users() -> list[User]:
    """List all users."""
    return [
        User(id=1, name="Alice", email="alice@example.com"),
        User(id=2, name="Bob", email="bob@example.com"),
    ]

2. Create Your Bridge (Python)

# main.py
from zynk import Bridge

# Import modules to register their commands (side-effect imports)
import users

app = Bridge(
    generate_ts="../frontend/src/generated/api.ts",
    host="127.0.0.1",
    port=8000,
)

if __name__ == "__main__":
    app.run(dev=True)  # Hot-reload enabled

3. Use the Generated Client (TypeScript)

// Auto-generated in frontend/src/generated/api.ts
import { initBridge } from './generated/api';
import { getUser, listUsers } from './generated/api';

// Initialize connection
initBridge('http://127.0.0.1:8000');

// Type-safe API calls
const user = await getUser({ userId: 123 });
console.log(user.name);  // TypeScript knows this is a string!

const users = await listUsers();
users.forEach(u => console.log(u.email));

📖 API Reference

Python API

@command Decorator

Register a function as a Zynk command:

from zynk import command

@command
async def my_command(arg1: str, arg2: int = 10) -> dict:
    """Command docstring becomes JSDoc."""
    return {"result": arg1, "count": arg2}

# With custom name
@command(name="custom_name")
async def internal_function() -> str:
    return "Hello"

Bridge Class

Main server class:

from zynk import Bridge

app = Bridge(
    generate_ts="../frontend/src/api.ts",  # Where to generate TypeScript
    host="127.0.0.1",                       # Server host
    port=8000,                              # Server port
    cors_origins=["*"],                     # CORS configuration
    title="My API",                         # API title
    debug=False,                            # Debug mode
)

app.run(dev=True)  # dev=True enables hot-reload

Channel for Streaming

Send real-time updates to the frontend:

from zynk import command, Channel

@command
async def stream_updates(channel: Channel[dict]) -> None:
    """Stream data to the client."""
    for i in range(10):
        await channel.send({"count": i})
        await asyncio.sleep(1)

TypeScript API

initBridge(baseUrl: string)

Initialize the bridge connection (call once at app startup):

import { initBridge } from './generated/api';

initBridge('http://127.0.0.1:8000');

Generated Functions

Each Python command becomes a TypeScript function:

// Python: async def get_user(user_id: int) -> User
// Becomes:
export async function getUser(args: { userId: number }): Promise<User>;

Error Handling

import { getUser, BridgeRequestError } from './generated/api';

try {
    const user = await getUser({ userId: 999 });
} catch (err) {
    if (err instanceof BridgeRequestError) {
        console.error(err.code);    // "EXECUTION_ERROR"
        console.error(err.message); // "User not found"
    }
}

Streaming Channels

import { streamUpdates } from './generated/api';

const channel = streamUpdates({ topic: "news" });

channel.subscribe((data) => {
    console.log("Received:", data);
});

channel.onError((err) => {
    console.error("Stream error:", err);
});

channel.onClose(() => {
    console.log("Stream closed");
});

// Later: close the stream
channel.close();

🧪 Running the Kitchen Sink Example

# Clone the repository
git clone https://github.com/zynk/zynk
cd zynk

# Install Python dependencies
pip install -e .

# Start the backend (from examples/kitchen-sink/backend)
cd examples/kitchen-sink/backend
python main.py

# In another terminal, start the frontend
cd examples/kitchen-sink/frontend
npm install
npm run dev

Open http://localhost:5173 to see the demo!

📄 License

MIT License - see LICENSE file for details.

🤝 Contributing

Contributions are welcome! Please read our contributing guidelines before submitting PRs.

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

zynk-0.1.0.tar.gz (23.3 kB view details)

Uploaded Source

Built Distribution

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

zynk-0.1.0-py3-none-any.whl (22.2 kB view details)

Uploaded Python 3

File details

Details for the file zynk-0.1.0.tar.gz.

File metadata

  • Download URL: zynk-0.1.0.tar.gz
  • Upload date:
  • Size: 23.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for zynk-0.1.0.tar.gz
Algorithm Hash digest
SHA256 92e9bfe2f23110ad14a8a85b00946229e2fd0b53b8ed6178d80a1b1052ea2598
MD5 fa2e8f0b17cb0d6e7ad572a375daf34a
BLAKE2b-256 604cbd30cbcfe23847d3fed2124825896ee3cc7123bbc761ac0578efdf3ce15c

See more details on using hashes here.

Provenance

The following attestation bundles were made for zynk-0.1.0.tar.gz:

Publisher: publish.yml on SethBurkart123/zynk

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

File details

Details for the file zynk-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: zynk-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 22.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for zynk-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 3893ef4bdf4685948e3119e3e065d0b1160fda83b39f00d9867049320e179e60
MD5 c0357c5f32e921db6828401dfe7ac519
BLAKE2b-256 3ddf2141b29927159c9696e46eae1b5cff4fe2ba3dcba014546a0f79bdfecf23

See more details on using hashes here.

Provenance

The following attestation bundles were made for zynk-0.1.0-py3-none-any.whl:

Publisher: publish.yml on SethBurkart123/zynk

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