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
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 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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
92e9bfe2f23110ad14a8a85b00946229e2fd0b53b8ed6178d80a1b1052ea2598
|
|
| MD5 |
fa2e8f0b17cb0d6e7ad572a375daf34a
|
|
| BLAKE2b-256 |
604cbd30cbcfe23847d3fed2124825896ee3cc7123bbc761ac0578efdf3ce15c
|
Provenance
The following attestation bundles were made for zynk-0.1.0.tar.gz:
Publisher:
publish.yml on SethBurkart123/zynk
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
zynk-0.1.0.tar.gz -
Subject digest:
92e9bfe2f23110ad14a8a85b00946229e2fd0b53b8ed6178d80a1b1052ea2598 - Sigstore transparency entry: 760804558
- Sigstore integration time:
-
Permalink:
SethBurkart123/zynk@ada779b31bad3be3365ea9483adbeeb35369ef83 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/SethBurkart123
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@ada779b31bad3be3365ea9483adbeeb35369ef83 -
Trigger Event:
push
-
Statement type:
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3893ef4bdf4685948e3119e3e065d0b1160fda83b39f00d9867049320e179e60
|
|
| MD5 |
c0357c5f32e921db6828401dfe7ac519
|
|
| BLAKE2b-256 |
3ddf2141b29927159c9696e46eae1b5cff4fe2ba3dcba014546a0f79bdfecf23
|
Provenance
The following attestation bundles were made for zynk-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on SethBurkart123/zynk
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
zynk-0.1.0-py3-none-any.whl -
Subject digest:
3893ef4bdf4685948e3119e3e065d0b1160fda83b39f00d9867049320e179e60 - Sigstore transparency entry: 760804563
- Sigstore integration time:
-
Permalink:
SethBurkart123/zynk@ada779b31bad3be3365ea9483adbeeb35369ef83 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/SethBurkart123
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@ada779b31bad3be3365ea9483adbeeb35369ef83 -
Trigger Event:
push
-
Statement type: