npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

esem-bridge

v0.1.5

Published

Import Python modules in JavaScript and call them directly.

Readme

esem

Import Python in JavaScript. No APIs needed.

import { python } from "esem-bridge";

const { predict } = await python("./model.py");
const result = await predict({ age: 22, country: "NG" });
console.log(result); // { score: 87, risk: "low" }

No FastAPI. No Express endpoint. No subprocess boilerplate. No JSON over HTTP.

Just import and call.


The problem

Building with Python and JavaScript together is annoying.

Python is better for AI, ML, data processing, and scientific computing. JavaScript is better for frontends, Node.js backends, and real-time apps. But connecting both usually means:

  • wrapping Python in FastAPI
  • running a Python server separately
  • creating HTTP endpoints
  • serializing/deserializing manually
  • deploying and monitoring two services

That's too much infrastructure for what should be a function call.

Install

npm install esem-bridge

Requires Node.js 18+ and Python 3.8+.


Usage

Option 1 — python() helper (recommended)

import { python } from "esem-bridge";

const tools = await python("./tools.py");

// Call functions
const result = await tools.add(2, 3);        // 5
const msg = await tools.greet("Crane");      // "Hello, Crane!"

// Destructure
const { add, greet } = await python("./tools.py");

Option 2 — python: import syntax

import tools from "python:./tools.py";

const result = await tools.add(2, 3);

Requires running with the loader hook:

node --experimental-loader esem-bridge/loader yourfile.js

Or use the CLI after installing esem-bridge:

npx esem run yourfile.js

To run the CLI without installing first:

npx --package esem-bridge esem run yourfile.js

Examples

AI/ML in a Node.js app

# model.py
def predict_score(user):
    # your ML logic here
    return { "score": 87, "risk": "low" }
import { python } from "esem-bridge";

const { predict_score } = await python("./model.py");
const result = await predict_score({ age: 22, country: "NG" });
console.log(result); // { score: 87, risk: "low" }

Next.js API route calling Python logic

// app/api/price/route.js
import { python } from "esem-bridge";

export async function POST(req) {
  const body = await req.json();
  const { calculatePrice } = await python("./pricing.py");
  const price = await calculatePrice(body);
  return Response.json({ price });
}

Using Python classes

# calculator.py
class Calculator:
    def __init__(self, precision=2):
        self.precision = precision

    def add(self, a, b):
        return round(a + b, self.precision)
const { Calculator } = await python("./calculator.py");

const calc = await Calculator(2);              // instantiate with precision=2
const result = await calc.add(1.234, 2.345);   // 3.58

Object attributes can be read and written from JavaScript:

# user.py
class User:
    def __init__(self):
        self.name = "John"
const { User } = await python("./user.py");

const user = await User();
console.log(await user.name); // "John"

user.name = "Mayowa";
console.log(await user.name); // "Mayowa"

Using async Python functions

# async_tools.py
import asyncio

async def fetch_score(user_id):
    await asyncio.sleep(0.1)
    return { "user_id": user_id, "score": 87 }
const { fetch_score } = await python("./async_tools.py");

const result = await fetch_score("user_123");
console.log(result); // { user_id: "user_123", score: 87 }

Using installed Python packages

const { python } = await import("esem-bridge");
const np = await python("numpy");              // pip-installed packages work too

Reading module constants

# config.py
VERSION = "1.0.0"
SETTINGS = {
    "debug": True,
    "ports": [3000, 3001],
}

Module values are loaded lazily, so await them when reading:

const config = await python("./config.py");

console.log(await config.VERSION);   // "1.0.0"
console.log(await config.SETTINGS);  // { debug: true, ports: [3000, 3001] }

Destructured module values are promises too:

const { VERSION } = await python("./config.py");
console.log(await VERSION);

Error handling

Python errors cross the bridge cleanly:

# tools.py
def parse_data(raw):
    if not raw:
        raise ValueError("Input cannot be empty")
    return process(raw)
import { python, PythonError } from "esem-bridge";

const { parse_data } = await python("./tools.py");

try {
  await parse_data(null);
} catch (err) {
  console.log(err.message);          // "Input cannot be empty"
  console.log(err.pythonTraceback);  // full Python traceback
  console.log(err.name);             // "PythonError(ValueError)"
}

Type mappings

| Python | JavaScript | |--------------|----------------| | None | null | | bool | boolean | | int | number | | float | number | | str | string | | list | Array | | dict | object | | class inst. | proxy object |


CLI

npx esem run index.js        # run with python: import support
npx esem --version
npx esem --help

Configuration

Set ESEM_PYTHON to use a specific Python binary (e.g. inside a venv):

ESEM_PYTHON=.venv/bin/python npx esem run index.js

Worker lifecycle

You do not need to call shutdown() in short scripts. esem automatically lets Node.js exit after your Python calls finish.

In a long-running application, the Python worker remains available for later calls. You can still release it early when you know it is no longer needed:

import { shutdown } from "esem-bridge";

shutdown();

How it works

esem spawns a Python worker process when you first call python(). The same worker is reused while your Node.js application is running, so there is no cold start on every call. When no Python calls are active, the worker does not prevent Node.js from exiting naturally.

Communication is JSON-RPC over stdin/stdout. When you call a proxied function, a message goes to the worker, Python executes it, and the result comes back serialized. The round-trip is microseconds on local processes.

JS runtime
  → python() call
  → bridge spawns Python worker (once)
  → JSON-RPC over stdin/stdout
  → Python loads module, runs function
  → result serialized back to JS
  → await resolves

Anything your Python code prints goes to stderr, prefixed with [python], so it never interferes with the bridge.

Object instances (classes) live in Python. JS holds a reference ID. When you call methods on the proxy, they route to the real Python object.


What's next

  • Python importing JavaScript
  • TypeScript type generation from Python type hints
  • NumPy array optimization (zero-copy)
  • Pandas DataFrame support
  • Bun support
  • VS Code extension

License

MIT