Node.js is a perfect match for our web project, but there are few computational tasks for which we would prefer Python. We also already have a Python code for them. We are highly concerned about speed, what is the most elegant way how to call a Python "worker" from node.js in an asynchronous non-blocking way?
-
3Hi, could you share with us what did you choose and how it worked out for you? There are libraries in Python which we all love to use while keeping the performance and non-blocking options. Thanks – Maziyar Oct 01 '13 at 03:57
-
What about simply spawn/fork a process and communicate through system IO, like this one suggests: http://www.sohamkamani.com/blog/2015/08/21/python-nodejs-comm/ ? – lkahtz Sep 29 '17 at 13:10
-
1There is a new bridging library named PyNode that allows you to call Python and get JS types returned. It’s demonstrated here https://thecodinginterface.com/blog/bridging-nodejs-and-python-with-pynode/ – SciGuyMcQ Aug 22 '19 at 15:57
-
**For those finding this on Google**, checkout https://github.com/extremeheat/JSPyBridge/ as explained in this seperate answer: https://stackoverflow.com/a/68427624/11173996 – Extreme Jul 18 '21 at 09:12
8 Answers
This sounds like a scenario where zeroMQ would be a good fit. It's a messaging framework that's similar to using TCP or Unix sockets, but it's much more robust (http://zguide.zeromq.org/py:all)
There's a library that uses zeroMQ to provide a RPC framework that works pretty well. It's called zeroRPC (http://www.zerorpc.io/). Here's the hello world.
Python "Hello x" server:
import zerorpc
class HelloRPC(object):
'''pass the method a name, it replies "Hello name!"'''
def hello(self, name):
return "Hello, {0}!".format(name)
def main():
s = zerorpc.Server(HelloRPC())
s.bind("tcp://*:4242")
s.run()
if __name__ == "__main__" : main()
And the node.js client:
var zerorpc = require("zerorpc");
var client = new zerorpc.Client();
client.connect("tcp://127.0.0.1:4242");
//calls the method on the python object
client.invoke("hello", "World", function(error, reply, streaming) {
if(error){
console.log("ERROR: ", error);
}
console.log(reply);
});
Or vice-versa, node.js server:
var zerorpc = require("zerorpc");
var server = new zerorpc.Server({
hello: function(name, reply) {
reply(null, "Hello, " + name, false);
}
});
server.bind("tcp://0.0.0.0:4242");
And the python client
import zerorpc, sys
c = zerorpc.Client()
c.connect("tcp://127.0.0.1:4242")
name = sys.argv[1] if len(sys.argv) > 1 else "dude"
print c.hello(name)

- 26,263
- 3
- 40
- 75

- 3,525
- 2
- 20
- 20
-
4Can zerorpc handle multiple states inthe event there are multiple client sessions? – user1027169 Oct 31 '13 at 07:46
-
Good answer, sample examples, plentiful explanation and what was i looking for. TY. +1 – Gaurav Gandhi May 01 '16 at 02:04
-
1if you are new like me, install the dependencies they way mentioned here - http://ianhinsdale.com/code/2013/12/08/communicating-between-nodejs-and-python/ – Darpan Aug 30 '16 at 22:25
-
-
2Nice hello world demo! Another similar solution below using Rabbitmq. https://medium.com/@HolmesLaurence/integrating-node-and-python-6b8454bfc272 – pangyuteng Feb 24 '18 at 21:51
-
The documentation says: "Security: there is none" That means that ANY process can connect. This is not acceptable for any real world appliations. It's so sad! – Regis May May 04 '19 at 16:47
-
No, it just means that it is designed to run on a private, internal network. – djheru May 04 '19 at 20:46
For communication between node.js and Python server, I would use Unix sockets if both processes run on the same server and TCP/IP sockets otherwise. For marshaling protocol I would take JSON or protocol buffer. If threaded Python shows up to be a bottleneck, consider using Twisted Python, which provides the same event driven concurrency as do node.js.
If you feel adventurous, learn clojure (clojurescript, clojure-py) and you'll get the same language that runs and interoperates with existing code on Java, JavaScript (node.js included), CLR and Python. And you get superb marshalling protocol by simply using clojure data structures.

- 1,048,767
- 296
- 4,058
- 3,343

- 2,654
- 20
- 17
-
2Do you know if something like this will work on Heroku, which has an ephemeral filesystem? – cm2 Jul 06 '13 at 01:46
If you arrange to have your Python worker in a separate process (either long-running server-type process or a spawned child on demand), your communication with it will be asynchronous on the node.js side. UNIX/TCP sockets and stdin/out/err communication are inherently async in node.

- 42,060
- 10
- 89
- 98
I've had a lot of success using thoonk.js along with thoonk.py. Thoonk leverages Redis (in-memory key-value store) to give you feed (think publish/subscribe), queue and job patterns for communication.
Why is this better than unix sockets or direct tcp sockets? Overall performance may be decreased a little, however Thoonk provides a really simple API that simplifies having to manually deal with a socket. Thoonk also helps make it really trivial to implement a distributed computing model that allows you to scale your python workers to increase performance, since you just spin up new instances of your python workers and connect them to the same redis server.

- 69
- 1
- 1
I'd consider also Apache Thrift http://thrift.apache.org/
It can bridge between several programming languages, is highly efficient and has support for async or sync calls. See full features here http://thrift.apache.org/docs/features/
The multi language can be useful for future plans, for example if you later want to do part of the computational task in C++ it's very easy to do add it to the mix using Thrift.

- 9,512
- 2
- 33
- 45
I'd recommend using some work queue using, for example, the excellent Gearman, which will provide you with a great way to dispatch background jobs, and asynchronously get their result once they're processed.
The advantage of this, used heavily at Digg (among many others) is that it provides a strong, scalable and robust way to make workers in any language to speak with clients in any language.

- 6,084
- 5
- 32
- 52
Update 2019
There are several ways to achieve this and here is the list in increasing order of complexity
- Python Shell, you will write streams to the python console and it will write back to you
- Redis Pub Sub, you can have a channel listening in Python while your node js publisher pushes data
- Websocket connection where Node acts as the client and Python acts as the server or vice-versa
- API connection with Express/Flask/Tornado etc working separately with an API endpoint exposed for the other to query
Approach 1 Python Shell Simplest approach
source.js file
const ps = require('python-shell')
// very important to add -u option since our python script runs infinitely
var options = {
pythonPath: '/Users/zup/.local/share/virtualenvs/python_shell_test-TJN5lQez/bin/python',
pythonOptions: ['-u'], // get print results in real-time
// make sure you use an absolute path for scriptPath
scriptPath: "./subscriber/",
// args: ['value1', 'value2', 'value3'],
mode: 'json'
};
const shell = new ps.PythonShell("destination.py", options);
function generateArray() {
const list = []
for (let i = 0; i < 1000; i++) {
list.push(Math.random() * 1000)
}
return list
}
setInterval(() => {
shell.send(generateArray())
}, 1000);
shell.on("message", message => {
console.log(message);
})
destination.py file
import datetime
import sys
import time
import numpy
import talib
import timeit
import json
import logging
logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO)
size = 1000
p = 100
o = numpy.random.random(size)
h = numpy.random.random(size)
l = numpy.random.random(size)
c = numpy.random.random(size)
v = numpy.random.random(size)
def get_indicators(values):
# Return the RSI of the values sent from node.js
numpy_values = numpy.array(values, dtype=numpy.double)
return talib.func.RSI(numpy_values, 14)
for line in sys.stdin:
l = json.loads(line)
print(get_indicators(l))
# Without this step the output may not be immediately available in node
sys.stdout.flush()
Notes: Make a folder called subscriber which is at the same level as source.js file and put destination.py inside it. Dont forget to change your virtualenv environment

- 5,433
- 4
- 57
- 90
UPDATE 2023
I create a library that allows you to use python with nodejs
Javascript lbrary: https://github.com/7HR4IZ3/js_bridge
Python Library: https://github.com/7HR4IZ3/py_bridge
Example From javascript
const { python } = require("js_bridge");
const py = python();
async function myPythonFunction() {
let math = await py.import("math");
let result = await math.sqrt(16);
console.log(result);
}
myPythonFunction();
Example from Python
from py_bridge import nodejs
node1 = nodejs(port=7000)
node2 = nodejs(port-7001)
node1.setup(name="my_node")
node2.setup(name="nodejs2") # name parameter is for imports
node1.console.log("Hello from node1")
node2.console.log("Hello from node2")
fs = node1.require("fs") # also supports 'from my_node import fs'
print(fs.readSync("./mytext.txt"))

- 21
- 3