Top
Best
New

Posted by yakkomajuri 11/3/2025

Why we migrated from Python to Node.js(blog.yakkomajuri.com)
229 points | 281 commentspage 4
submeta 11/3/2025|
I was about to migrate a legacy system written in Python/ Flask to FastAPI and React (frontend). But the sentiments here seem to suggest that FastAPI is not the best solution if I need async? So go with Next.js?
oceansky 11/4/2025|
Fastapi is perfectly fine for async. In fact, it's built with async in mind.

I say that as someone who prefers JS promises: you likely won't face issues with either.

coldtea 11/3/2025||
If we ignore the ML/AI/array libs, where Python shines, the core development has really done nothing much for it since 3.0.

Despite MS, Guido and co throwing their weight, still none of the somewhat promised 5x speedup across the board (more like 1.5x at best), the async story is still a mess (see TFA), the multiple-interpreters/GIL-less is too little, too late, the ecosystem still doesn't settled on a single dependency and venv manager (just make uv standard and be done with it), types are a ham-fisted experience, and so on, and so forth...

rick1290 11/4/2025||
I like mikro orm - cool to see you use that. I do prefer django however.

I see express as the backend. Why not nestjs? And are you using openapi at all for generating your frontend client?

What i've discovered is - any backend + orm should expose an openapi spec'd backend... and your frontend can autogen your client for you. Allows you to move extremely quick with the help of ai.

tschellenbach 11/3/2025||
It is true that Python's typing and async support feels like someone adding extension to a house that was built 50 years ago.
danudey 11/3/2025|
After having used it two weeks ago for the first time: it feels as though async support in Python is basically a completely parallel standard library that uses the same python syntax with extra keywords all over the place. It's like if building code compliance required your 50 year old house to be updated have a wider staircase with deeper steps but you wanted to do so without affecting the existing stairs, so now you just have two staircases which are a little bit different and it feels like it takes up space unnecessarily.

I had to look for async versions of most of what I did (e.g. executing external binaries) and use those instead of existing functions or functionality, meaning it was a lot of googling "python subprocess async" or "python http request async".

If there were going to be some kind of Python 4.x in the future, I'd want some sort of inherent, goroutine-esque way of throwing tasks into the ether and then waiting on them if you wanted to. Let people writing code mark functions as "async'able", have Python validate that async'able code isn't calling non-async'able code, and then if you're not in an async runloop then just block on everything instead (as normal).

If I could take code like:

    def get_image(image):
        return_code = subprocess.check_call(["docker", "pull", image])
        if return_code:
            raise RuntimeError("idk man it broke")

    result = get_image(imagename)
    print(result)
And replace it with:

    def get_image(image):
        return_code = subprocess.check_call(["docker", "pull", image])
        if return_code:
            raise RuntimeError("idk man it broke")

    result = async get_image(imagename)
    print(result)
And just have the runtime automatically await the result when I try to access it if it's not complete yet then it would save me thousands of lines of code over the rest of my career trying to parallelize things in cumbersome explicit ways. Perhaps provide separate "async" runners that could handle things - if for example you do explicitly want things running in separate processes, threads, interpreters, etc., so you can set a default async runner, use a context manager, or explicitly threadpool.task(async get_image(imagename)).

Man, what a world that would be.

smilliken 11/4/2025|||
You can have this today or 15+ years ago using the excellent gevent library for Python. Python 3 should have just endorsed gevent as the blessed solution instead of adding function coloring and new syntax, but you can blissfully ignore all of that if you use gevent.
pdhborges 11/3/2025||||
Might as well just implement virtual threads: https://discuss.python.org/t/add-virtual-threads-to-python/9...
foobarian 11/3/2025||||
Not sure if this is a tongue in cheek post referring to how many modern languages already work like this? TS, Kotlin, C#, Java 29+, ...
zzzeek 11/3/2025||
Good decision, judging by their general level of impatience with things they would have hated my ORM :).

Also I think the node approach is probably still more performant than FastAPI but that's just a hunch.

Hopefully they won't have security issues because someone hijacked the node package that sets the font color to blue or passes the butter or something.

jgehrcke 11/3/2025||
Thank you for that comment, Mike :-) I was looking for that type of response here :-). As expressed many times: thank you for your work.
antod 11/3/2025||
Passes the butter is a euphemism for churn?
zzzeek 11/3/2025||
it's from rick and morty that was referenced here last week with a post about robots that pass butter
amai 11/4/2025||
Should be "Why we migrated from Django to Node.js".

Answer: Because Django doesn't support async by default.

robot-wrangler 11/3/2025||
Doing zero upfront research or planning and then bragging about it in public like this is pretty suspect, but I guess more to the point, glorifying "the pivot" like this is out of style anyway. You're now supposed to insist that whatever happened was the plan all along.
traverseda 11/3/2025||
>We did this so we can scale.

>Python async sucks

Python async may make certain types of IO-blocked tasks simpler, but it is not going to scale a web app. Now maybe this isn't a web app, I can't really tell. But this is not going to scale to a cluster of machines.

You need to use a distributed task queue like celery.

est 11/4/2025|
> You gotta write sync_to_async and async_to_sync everywhere.

I have a a simple wrapper that allows you write once and works for both sync/async https://blog.est.im/2025/stdout-04

More comments...