Express (Node.js) vs webflux (Spring): Hello world performance comparison

Mayank Choubey
Tech Tonic
Published in
4 min readMar 20

--

One of my popular article series has been the performance comparison between Node.js and Springboot. I started the series by comparing Node’s native HTTP server with Springboot (article is here). As the article got a record number of views, I got comments that:

  1. A native server shouldn’t be compared with a framework like Spring
  2. For apples to apples comparison, Spring’s reactive framework webflux should be used instead of the Spring’s threadpool

To address point 1, I already did comparison of Node’s popular frameworks with Springboot:

With this article, I’m starting to address the point number 2. I’m going to compare Spring Webflux with Node.js’s express framework. Why Express? Because Express still enjoys more than 25M weekly downloads on NPM. The results are going to be obvious. Express is a popular, but very slow framework. It is expected that express will not be able to stand in front of Spring webflux. Let’s see if the expectations turn out right or not.

A note about Spring Webflux

Reactive Programming is a programming style which deals with asynchronous data streams! A development model which focuses on observing data streams and reacting on changes and propagating them. You learn Reactive Programming to build Reactive Systems — a highly resilient distributed systems or Microservices! Spring Webflux is an example of reactive programming.

The test setup is the same:

  • MacBook M1 with 16G RAM
  • Node.js v19.8.1
  • Spring 3.0.4 over Java 17

The code is as follows:

Node.js

import express from "express";
const app = express();
const port = 3000;

app.get("/", (req, res) => {
res.send("Hello World!");
});

app.listen(port, () => console.log("Listening on", port));

Spring Webflux

The Spring Webflux ‘hello world’ code has been taken from their official documentation:

HelloWorldHandler.java

package hello;

import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;

import reactor.core.publisher.Mono;

@Component
public class HelloWorldHandler {

public Mono<ServerResponse> hello(ServerRequest request) {
return ServerResponse.ok().contentType(MediaType.TEXT_PLAIN)
.body(BodyInserters.fromValue("Hello World!"));
}
}

HelloWorldRouter.java

package hello;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerResponse;

import static org.springframework.web.reactive.function.server.RequestPredicates.GET;
import static org.springframework.web.reactive.function.server.RequestPredicates.accept;

@Configuration(proxyBeanMethods = false)
public class HelloWorldRouter {

@Bean
public RouterFunction<ServerResponse> route(HelloWorldHandler helloWorldHandler) {

return RouterFunctions
.route(GET("/"), helloWorldHandler::hello);
}
}

HelloWorldClient.java

package hello;

import reactor.core.publisher.Mono;

import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.client.ClientResponse;
import org.springframework.web.reactive.function.client.WebClient;

@Component
public class HelloWorldClient {

private final WebClient client;

public HelloWorldClient(WebClient.Builder builder) {
this.client = builder.baseUrl("http://localhost:3000").build();
}

public Mono<ClientResponse> getMessage() {
return this.client.get()
.uri("/")
.exchange();
}

}

Application.java

package hello;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class Application {

public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
HelloWorldClient helloWorldClient = context.getBean(HelloWorldClient.class);
}
}

The test runs a total of 1M requests for 25, 100, 200, and 300 concurrent connections. The testing tool is bombardier which is known to be very fast.

Let’s jump straight to the results.

Express gets washed away by the stellar performance of Spring Webflux. There is simply no comparison between 70k RPS of Spring & 18K RPS of Express. Barring less CPU and memory usage, Express is nowhere in the competition.

WINNER: Spring Webflux

A comparison of Fastify with Spring Webflux is available here.

Thanks for reading! Let me know any comments, suggestions, etc.

--

--

Mayank Choubey
Tech Tonic

I write about Deno, Bun, and Node.js. My new book: https://choubey.gitbook.io/learn-react-in-a-day/