DEV Community

Cover image for Node vs Go: API Showdown

Node vs Go: API Showdown

Caio Borghi on January 01, 2024

Disclaimer and Introduction How were the metrics gathered? Tech Stack Flow Diagram Manual RDS Setup Environment Initialization Monitoring Proce...
Collapse
 
adesoji1 profile image
Adesoji1

Node js 😃😃😃😋🥳

Collapse
 
fullstackchris profile image
Chris Frewin

I don't know, looks more like 🤡🤡🤡🤡 to me.

Don't get me wrong, I love Node for a nice quick script or something, but if you're going to build an API that is gonna have any traffic at all, might as well go with the strongly typed & compiled backends (.NET, go, etc.)

I disagree with comments in here claiming that just because JavaScript is popular it would by definition speed up development. You have to consider bug fixes due to lack of type system, or if trying to avoid those issues with TypeScript, which brings in extra tooling overhead. Other languages are just as easy, if not easier, to get a beta API up and running. I mean just look how far .NET has come in making it truly as simple as possible: learn.microsoft.com/en-us/training...

Or a framework like Gin with Golang: gist.github.com/ezaurum/5b4803114d...

These examples are easily equivalent in complexity to any express or native node 'hello world' set of endpoints

Collapse
 
piboistudios profile image
Gabriel Hayes • Edited

We use JSDoc at my shop, and only use Typescript for d.ts files (which usually we have generated out by some tool relevant to the task at hand)

Type issues are not the problem. If you're facing type issues solely because a compiler won't stop you, that's indicative of your own ability, not the language.

Biggest issues you run into with JS are null/undefined errors (which you'll face basically in any language if you fail to null check, happens to the best of us).

I have never once been writing code and been like "wow, I have zero clue what this type is. I am at a total loss because a compiler won't tell me"

Usually it's like... Writes code expectations a Number

Gets a Promise "Ohhh noooooo"

Just kills me when people falsely exaggerate the usefulness of a type system.

It's more performant. It definitely does not save you time.

I JSDoc function signatures where it's useful, but it's super nice that I don't have to type every arbitrary function, especially when you get to super complex generic types (Promise>>>, yayyyyyy)

Thread Thread
 
adesoji1 profile image
Adesoji1

i totally understand

Collapse
 
adesoji1 profile image
Adesoji1

i totally understand

Collapse
 
wuya666 profile image
wuya666

Both Node and Go are good, that's why I choose Rust :p

Collapse
 
oseifrimpong profile image
Obed Osei Frimpong

🤣🤣🤣🤣🤣

Collapse
 
jmir17 profile image
Josep Mir

What about development times, in the long run, at enterprise level software that realistically have to deal with such big loads? Any insights 😄

Collapse
 
ocodista profile image
Caio Borghi

Thank you for this comment!

To me, it depends on the focus of the project. If it's an enterprise focused on performance where milliseconds matter, I would choose Go.

If not, Node will be faster to implement, as JavaScript is way more popular than Golang.

It'll also be easier to hire and scale the team because there are way more JS developers than Go developers.

But that's a tough question, and I may be biased, I've been a JS developer for the past 5 years, I love Go and I really hope it thrives in the long run, but at the moment, I would still choose JS over any other language because of the scalability issue.

Hope this changes though, Go it's an awesome language 😁

Collapse
 
blinkinglight profile image
M
Collapse
 
ocodista profile image
Caio Borghi

Go is awesome 😁

Collapse
 
m3rashid profile image
MD Rashid Hussain

You take multiple metrics over and over again and then gather data points on the basis of aggregated results from all the points.
Also, the warmup time is crucial. You should not test the server all of a sudden after starting up. It should be warmed up first to get going with the threads
Whatever be the case, interesting results 😅

Collapse
 
webjose profile image
José Pablo Ramírez Vargas

Compare Go with .Net. I bet for the latter, being in the top 10 for last year's benchmarks.

Collapse
 
ocodista profile image
Caio Borghi • Edited

I've been a .NET Developer for ~4 years, love the framework, maybe in the future, it's way laborious to write such a post 😅

Collapse
 
gabrielsclimaco profile image
Coffee

very nice insights!

Collapse
 
ocodista profile image
Caio Borghi

Thank you!

Collapse
 
shivampatel17 profile image
Shivam Patel

nice post!

Collapse
 
yourrewardcard profile image
Your Reward Card

wow

Collapse
 
nemopeti2 profile image
Péter Kovács

Nice, I suggest you to test .Net core too. 😉

Collapse
 
adaptive-shield-matrix profile image
Adaptive Shield Matrix

Biggest reason to use a js runtime like nodejs/bun/deno is not the performance.
Its that you can develop in a language you know, JS or TS.
Its impossible to do complex websites web apps with serverside only languages like go, rust or scala.

Collapse
 
abrahamn profile image
Abraham

Unless you call in WebAssembly, HTMX and really any server side generated templates

Collapse
 
chiroro_jr profile image
Dennis

Impossible?

Collapse
 
adaptive-shield-matrix profile image
Adaptive Shield Matrix

Not physically impossible but painful/cumbersome enough that no sane/experienced developer will do that. Why? because you lose any client state with live reload that does not have HMR (Hot module reload).
Having to click 10 times to recreate client state is not a fun endeavor -> do that 100 times a day while editing the ui and will quickly start to understand why. So practically speaking the same thing -> impossible as of now (January 2024).

Collapse
 
adaptive-shield-matrix profile image
Adaptive Shield Matrix • Edited

Only is small toy examples like this can node beat go.

If you write more complex projects -> go will nearly always beat nodejs, beause go has much better primitives, native data types, more efficient error handling, etc. Node and JS/TS has to use inefficient number types, has use/copy/map inefficient json/object data types, etc -> these things are fine and provide good developer experience but are horrible if you want to optimize for performance.

Persistent performance is key. If your server is only performant some times then thats only asking for trouble / headaches / suffering: how do you debug perf issues? How do you know because of that your CPU usage spiked? Why does you server started choking responses left and right? Did the garbage collector kicked in ? Etc

Collapse
 
ocodista profile image
Caio Borghi

Go read the Disclaimer again.

Collapse
 
daniel15 profile image
Daniel Lo Nigro

Node.js (JavaScript) is interpreted

Node (and the V8 engine in general) is actually a hybrid model. Hot code (code that runs a lot) gets JIT compiled to machine code.

It waits until the code runs a few times before compiling it. This is because for code that only runs once (like the startup code for the app), it's quicker to run it in interpreted mode than to compile it.

Collapse
 
nasyliis81 profile image
Pietro • Edited

This may have been a one-time anomaly, but as I won't redo the test and the metrics are all correct, I'll call this one a lucky shot for Node.

Appreciate the effort sir but, you should definitely re-do the test, multiple times, on multiple machines with different operating configurations, architecture and operating systems to provide any form of credible data to base any sensible decision.

Collapse
 
ocodista profile image
Caio Borghi

Hey Pietro, the code is Open Source, feel free to replicate this study and run the test how many times you find reasonable.

Collapse
 
swape profile image
Alireza Balouch

In real life you are connected to a database. And none of those speeds matter when db is usually the bottleneck. But nice work.

Collapse
 
ocodista profile image
Caio Borghi • Edited

Please check the Flow Diagram and Tech Stack again, the APIs are connected to a database.

Collapse
 
wuya666 profile image
wuya666

well, to be fair, one simple insert is far from real life applications. I think this test is nice to test the "extreme" performance of these two programs (but then I doubt anyone would expect a JS runtime to run faster than compiled native binaries, barring maybe some bugs and/or optimization issues), however for "non-extreme" real world applications with more complex database logic and transactions, I do agree with Alireza that most of the time database is the bottleneck.

For example for the mid-sized company I'm currently working with, operating a mobile app with about a couple millions active users, running multiple backend services in different languages and frameworks including Elixir, PHP, Node, Java, Go and Rust, it's always the database that's under the highest stress during most promotion events (but then they never use a single-core server for any of the backend services... the servers are also quite cheap compared to database and data bandwidth costs anyway)

Collapse
 
theyousssef profile image
Youssef Mohamed

What if you compared them but using worker threads for node this time ?

Collapse
 
ocodista profile image
Caio Borghi • Edited

I don't think that would help.

Worker Threads are useful when you have some CPU-Bound operation that can block the Event Loop.

It's not the case for this API, this challenge is IO-Bound, meaning that the single-threaded Event Loop is the best choice (it was created for this).

What could maybe improve Node results is using the cluster module.

But there's a catch in that.

The Cluster module helps you spin up new Node processes, usually, it's a good practice to have 1 node for each CPU Core, maximizing parallelism and avoiding concurrency.

The problem is that, in this case, the machine that runs the APIs has a single-core processor, so spinning up new node processes (or workers) using the cluster module wouldn't help, in theory, it would only increase concurrency (which is already high due to the 11/15 OS Threads that the Node process is already starting)

Collapse
 
megaproaktiv profile image
Gernot Glawe

As t2 EC2 instances have burst CPU capacity, they will mess with performance measurement. Fixed capacity like m* instances have stable cpu performance.

Collapse
 
ivan_starodubtsev_da7ed7d profile image
Ivan Starodubtsev

If you use uwebsocket.js with node, it will outperform golang in every way.