Fastify (Node.js) vs Webflux (Spring): Hello world performance comparison

Mayank Choubey
Tech Tonic
Published in
4 min readMar 20, 2023

--

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:

I started addressing point number 2 with Express vs webflux comparison (the article is here). As expected, Express got washed away in front of a mighty competitor like Springboot. The primary reason being that Express, though extremely popular, is known to be very slow.

With this article, I’m continuing to address the point number 2. This time, I’m going to compare Spring Webflux with Node.js’s fastify framework. Why Fastify this time? Because Fastify is pretty fast when compared to Express. The results are tough to predict. Let’s find out who wins this time.

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 Fastify from "fastify";
const fastify = Fastify({ logger: false });

fastify.get("/", () => {
return "Hello world!";
});

fastify.listen({ port: 3000 });

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 5M 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.

Unlike Express, Fastify is able to give a good competition to Springboot with Webflux, but looses out in RPS and other latency metrics. Fastify is able to score on CPU and memory usage.

WINNER: Spring Webflux

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

--

--