PyFlow.ts - Bridge Python and TypeScript with ease. Use simple decorators to expose Python functions, methods, and classes to TypeScript with full type safety.
Data scientists and machine learning engineers work predominantly in Python, while web developers typically build frontends in TypeScript/JavaScript. This creates a painful gap in the development workflow:
Many teams spend days to weeks creating REST APIs, writing OpenAPI specs, generating clients, and managing infrastructure - just to connect Python ML models to frontend applications.
With PyFlow.ts: Simply decorate your Python functions and classes with @extensity
and extend them beyond the boarders of Python environment to make them available in TypeScript with full type safety. This eliminates the traditional barriers between data science and frontend development:
- Machine Learning to Production in Minutes: Deploy trained ML models to production frontends with zero API boilerplate
- Unified Development Experience: Data scientists can focus on Python algorithms while frontend developers consume them as if they were native TypeScript
- Frictionless Updates: When Python implementations change, TypeScript interfaces update automatically - no API versioning headaches
- Fast Iteration Cycles: Experiments go from notebook to production-ready web app in record time, enabling rapid prototyping
PyFlow.ts is particularly valuable for ML-powered applications, where Python's rich ecosystem meets the need for responsive, type-safe web interfaces.
- ✨ Simple Decorators: Mark Python objects with
@extensity
to expose them to TypeScript - 🔄 Automatic Type Conversion: Python type annotations are mapped to TypeScript types
- 🚀 Zero Boilerplate: No manual API endpoints or TypeScript interfaces needed
- 🔒 Type Safety: Full TypeScript type definitions for Python objects
- 🌐 FastAPI Backend: Generated API server for seamless communication
- 📦 Easy Integration: Works with any Python codebase
PyFlow.ts requires Python 3.10+ and can be installed via pip:
pip install pyflow-ts
Here's how to quickly test PyFlow.ts with your Python code:
Generate TypeScript code from your Python module:
pyflow init -m ./examples -o ./generated
This command:
- Takes your Python module (
examples/
) - Generates TypeScript code in the specified output directory (
generated
) - Configures the API server to use port 8002
Start the API server to expose your Python functions:
pyflow run -m ./examples -g ./generated
This command:
- Imports your Python module using the module path notation
- Starts a FastAPI server that exposes your decorated functions and classes
- Runs on port 8000 by default (or the port specified during initialization)
Test your TypeScript code with the test
command:
pyflow test -f demo.ts -g ./generated --debug
This command:
- Runs the specified TypeScript file (
demo.ts
) - Uses the generated TypeScript code in the directory (
examples/basic/generated
) - Connects to the API server on the specified port (default 8000)
- Provides verbose output with the
--debug
flag
The test
command automatically sets up a temporary environment with proper TypeScript and ESM support, allowing you to quickly test your code without manual configuration.
This quick start guide will walk you through using PyFlow.ts to bridge Python and TypeScript, using both the traditional approach and the new streamlined initialization process.
The easiest way to get started is to use the init
command, which sets up everything you need:
# Initialize a project for your Python module
pyflow init -m ./
This single command:
- Generates TypeScript code from your Python code
- Creates a complete Node.js project structure
- Sets up package.json with necessary dependencies
- Configures TypeScript correctly
- Creates a sample index.ts file
- Installs all required npm packages
When you decorate a class with @extensity
, all its public methods and attributes are automatically exported to TypeScript:
@extensity
class Calculator:
history: List[Dict[str, float]] = []
def calculate(self, a: float, b: float, operation: str) -> float:
# This method is automatically exported, no decorator needed
result = a + b if operation == "add" else a - b
self.history.append({"a": a, "b": b, "result": result})
return result
def _internal_method(self):
# This won't be exported because it starts with an underscore
pass
Only use method-level decorators when you want to expose specific methods from a class:
class DataService:
def __init__(self):
self.data = {}
@extensity
def get_public_data(self) -> Dict:
# Only this method will be exported
return {"public": "data"}
def internal_process(self):
# This won't be exported
pass
For standalone functions, simply add the decorator:
@extensity
def add(a: float, b: float) -> float:
return a + b
Do not decorate both a class and its methods - this is redundant:
@extensity # ✅ This is sufficient
class Example:
# ❌ Don't do this!
@extensity # Redundant - the class is already decorated
def some_method(self) -> str:
return "example"
When a class is decorated with @extensity
, all its public methods are automatically exported without needing individual decorators.
In some cases, you might want to manually register a class instead of using the @extensity
decorator. This can be useful for dynamically created classes or when you need more control over the exposed methods and registration process:
from pyflow.core import registry
class DynamicCalculator:
def add(self, a: float, b: float) -> float:
return a + b
@extensity
def subtract(self, a: float, b: float) -> float:
return a - b
# Manually register the class
registry.register_class(DynamicCalculator)
Let's walk through a complete example to understand the full workflow.
# calculator.py
from pyflow import extensity
from typing import List, Dict
@extensity
def add(a: float, b: float) -> float:
return a + b
@extensity
def subtract(a: float, b: float) -> float:
return a - b
@extensity
class Calculator:
history: List[Dict[str, float]] = []
def calculate(self, a: float, b: float, operation: str) -> float:
pass # rest of the implementation
def get_history(self) -> List[Dict[str, float]]:
return self.history
def clear_history(self) -> None:
self.history = []
# Initialize a complete project
pyflow init -m ./examples/basic -o ./examples/basic/generated
OR generate the TypeScript code manually:
# Generate TypeScript code only
pyflow generate -m ./examples/basic -o ./examples/basic/generated
If you used init
, the the a node project is initialized with npm dependencies.
Otherwise, it generates TypeScript code of the Python module.
To run the API server you can use the run
command in the local directory:
pyflow run -m ./
The generated index.ts
will already have imports set up. For our calculator example, you would use it like this:
// index.ts
import { add, subtract, multiply, divide, Calculator } from './generated/calculator';
async function runCalculator() {
console.log("Basic operations:");
console.log(`5 + 3 = ${await add(5, 3)}`);
console.log(`10 - 4 = ${await subtract(10, 4)}`);
console.log("\nUsing Calculator class:");
const calculator = new Calculator();
// Perform calculations
console.log(`Calculator: 10 + 5 = ${await calculator.calculate(10, 5, "add")}`);
console.log(`Calculator: 10 - 5 = ${await calculator.calculate(10, 5, "subtract")}`);
}
runCalculator().catch(console.error);
Running the calculator example should produce output like:
Basic operations:
5 + 3 = 8
10 - 4 = 6
Using Calculator class:
Calculator: 10 + 5 = 15
Calculator: 10 - 5 = 5
- Explore advanced features like directory scanning
- Try creating your own classes and functions with the
@extensity
decorator - Check out the examples directory for more complex use cases
PyFlow.ts uses a combination of Python introspection, FastAPI, and TypeScript code generation:
- Registration: The
@extensity
decorator registers Python functions and classes in a global registry - API Generation: A FastAPI application is generated with endpoints for each registered item
- TypeScript Generation: TypeScript interfaces, classes, and functions are generated based on Python type annotations
- Runtime Bridge: The TypeScript code communicates with the Python API server to execute the Python code
PyFlow.ts automatically maps Python types to TypeScript types. Common mappings include:
Python Type | TypeScript Type |
---|---|
str |
string |
int , float |
number |
bool |
boolean |
list[T] , List[T] |
T[] |
dict[K, V] , Dict[K, V] |
Record<K, V> |
tuple , Tuple |
Array or typed tuple |
Union[T, U] |
T | U |
Optional[T] |
T | null |
Classes | Interfaces with the same name |
pyflow run -m MODULE [--generate-dir GENERATE_DIR] [--host HOST] [--port PORT] [--reload]
pyflow generate -m MODULE -o OUTPUT_DIR
-m, --module
: Module to import, or directory to scan (required)-g, --generate-dir
: Directory containing generated TypeScript code (optional)--host
: Host to bind to (default: 0.0.0.0)--port
: Port to bind to (default: 8000)--reload
: Enable auto-reload for development--debug
: Enable debug mode for verbose output
If the specified port is already in use, PyFlow.ts will automatically attempt to find an available port.
-m, --module
: Module to import, or directory to scan (required)-o, --output
: Output directory for generated TypeScript code (required)--host
: Host to bind to (default:--port
: Port to bind to (default: 8000)--debug
: Enable debug mode for verbose output
PyFlow.ts can recursively scan directories to find and process Python files containing @extensity
decorators.
To process all Python files in a directory:
# Process a single directory
pyflow generate -m ./your_directory -o ./generated_direrctory
# Run server for all modules in a directory
pyflow run -m ./your_directory -g ./generated_direrctory
When specifying a directory, PyFlow.ts will:
- Recursively scan all subdirectories
- Import all Python files (excluding
__init__.py
and similar) - Register any classes or functions decorated with
@extensity
- Generate TypeScript interfaces and API endpoints for all registered items
This is particularly useful for larger projects with multiple modules.
You can specify a custom port for the PyFlow.ts server:
pyflow run -m ./your_module --port 8080
If the specified port is already in use, PyFlow.ts will automatically attempt to find an available port:
$ pyflow run -m ./your_module
Warning: Port 8000 is already in use.
Using alternative port 8001 instead.
This makes it easier to run multiple instances or work in environments where the default port might be occupied by other services.
- Python 3.10+
- TypeScript 4.5+ (for frontend usage)
While several technologies exist for exporting Python models to other environments, each has distinct advantages and limitations:
ONNX converts ML models for cross-platform deployment but has significant limitations:
- ✅ Good for deploying trained models to specific hardware/platforms
- ✅ Works well for standard architectures and established operations
- ❌ Often lags behind cutting-edge PyTorch/TensorFlow features
- ❌ Complex custom layers frequently require special handling
- ❌ Limited to model inference only, not full application logic
Framework-specific export tools have similar constraints:
- ✅ Optimized for their specific runtime environments
- ✅ Direct integration with their ecosystem
- ❌ Limited to their specific framework (vendor lock-in)
- ❌ Feature gap between Python and exported versions
- ❌ Require significant rework for custom operations
PyFlow.ts takes a fundamentally different approach:
- ✅ No Conversion Limitations: Uses the actual Python code, not a conversion
- ✅ Always Current: Access the latest ML techniques immediately, not months later
- ✅ Complete Workflow: Exposes your entire application logic, not just the model
- ✅ Custom Operations: Works with any Python code, not just standard operations
- ✅ Rapid Prototyping: Change the Python implementation without redoing the API
When to use PyFlow.ts:
- When you need rapid iteration between research and production
- When your models use cutting-edge features not yet supported in export formats
- When you need the full Python ML ecosystem, not just the model inference
- For applications where TypeScript/JavaScript is the frontend but Python handles ML
When other tools might be better:
- When you need maximum inference performance on edge devices
- For fully offline mobile applications with no API connection
- When deployment constraints prohibit running a Python server
PyFlow.ts bridges the gap between Python's ML ecosystem leadership and TypeScript's frontend capabilities, focusing on developer productivity and deployment speed rather than edge-case performance optimization.
Contributions are welcome! Please check out our contribution guidelines for details.
PyFlow.ts was inspired by various Python-TypeScript bridging solutions, including FastAPI's automatic OpenAPI generation and TypeScript client code generators.