Plugin vs No Plugin Implementation in Fastify

Written by: Bagus Facsi Aginsa
Published at: 15 Jan 2022


Fastify is known for its performance and the plugin ecosystem to extend its functionality. You can see the list of fastify plugin ecosystems in Fastify Ecosystem. But why should we need a plugin anyway? Is it any different from just using a standard library from npm? How about the performance when using a plugin? You will find out the answer in this article.

If you are confused about fastify, I suggest you to see my previous article first here: Fastify vs Express Performance. This will give you an idea what is fastify and why we explore more about fastify here.

Test Scenario

To demonstrate and compare the implementation of the plugin and non-plugin, we have to do a test just like before. All the application is installed on my notebook with this spec:

  1. CPU: Intel Core i3-6006U, 2 cores, 4 threads.
  2. RAM: 16 GB
  3. OS: Ubuntu 18.04 WSL
  4. Node Version: 14.17.2
  5. Express Version: 4.17.2
  6. Fastify Version: 3.25.3
  7. ApacheBench Version: 2.3

The application we used for the load testing is ab (ApacheBench). The ab will hit the fastify server as much as 10k times with 100 concurrency with this command:

ab -n 10000 -c 100 -H "Authorization: Bearer {token}" http://localhost:3000/getproducts

Notice that we use the Authorization header because one of the plugins that we will test is related to jwt.

The webserver will first verify the bearer token from the request. If the token is verified it will return the list of products (20 rows) that it gets from the database query. We use the MySQL database in this test. You can see the test topology below:


 ab ------> Fastify ------> MySQL

In this test, We will compare:

  1. NodeJS using fastify framework with mysql2 and fast-jwt library.
  2. Nodejs using fastify framework with fastify-mysql and fastify-jwt plugin (the plugin use mysql2 and fast-jwt under the hood).

Fastify with No Plugin Code

This is the app.js using fastify with no plugin implementation:

const fastify = require("fastify");
const mysql = require("mysql2/promise");
const { createVerifier } = require("fast-jwt");
const verify = createVerifier({
 key: async () => process.env.JWT_SECRET,
});
const app = fastify();

const pool = mysql.createPool({
 connectionLimit: 100,
 host: process.env.DB_HOST,
 user: process.env.DB_USER,
 password: process.env.DB_PASS,
 database: process.env.DB_NAME,
});

app.get("/getproduct", async (req, res) => {
 const token = req.headers.authorization?.split(" ")[1];
 if (!token) {
 res.status(403).send({ message: "please provide token" });
 return;
 }
 const decoded = await verify(token);
 if (!decoded) {
 res.status(403).send({ message: "token not valid" });
 return;
 }
 const [results] = await pool.query("SELECT * FROM product");
 res.send({ results });
});

app.listen(3000);

Notice that we use mysql2 and fast-jwt in a normal way, not integrated with the fastify framework.

Fastify with Plugin Code

This is the app.js using fastify with plugin implementation:

const fastify = require("fastify");
const app = fastify();

app.register(require("fastify-mysql"), {
 connectionLimit: 100,
 host: process.env.DB_HOST,
 user: process.env.DB_USER,
 password: process.env.DB_PASS,
 database: process.env.DB_NAME,
 promise: true,
});

app.register(require("fastify-jwt"), {
 secret: process.env.JWT_SECRET,
});

app.get("/getproduct", async (req, res) => {
 const decoded = await req.jwtVerify();
 if (!decoded) {
 res.status(403).send({ message: "token not valid" });
 return;
 }
 const [results] = await app.mysql.query("SELECT * FROM product");
 res.send({ results });
});

app.listen(3000);

Compared to no-plugin source code, this is way cleaner. The mysql2 and fast-jwt is still used, but it’s already integrated with the fastify framework using fastify-mysql and fastify-mysql.

The Test Result

In this segment, we will show you the result of the test (10k requests, 100 concurrent). The test scenario is done 10 times, this is the test results:

Time Taken For Tests

Fastify Plugin vs Non-Plugin Implementation Performance: Time Needed

On average, fastify with plugin implementation finish the test in 6.403 second while fastify with non-plugin implementation finishes the test in 6.387 second. As we can see that the plugin will make the process slower by 16 ms, but it is very little that we can almost ignore the difference.

Requests Per Second

Fastify Plugin vs Non-Plugin Implementation Performance: Time Needed

We also can see that using the plugin is handling the requests slightly lower here. On average, the fastify with the plugin handles 1566.56 requests per second, while the fastify with non-plugin handles 1571.33 requests per second.

Conclusion

There are some key points when I was doing this experiment:

  1. We can see from this test that although the average performance is slightly lower, sometimes the plugin implementation can still beat the non-plugin implementation on a single test. So, I conclude that the performance difference can be ignored.
  2. Some plugins have seamless integration with fastify, for example, the fastify-jwt. The syntax is so beautiful that you don’t have to extract the bearer token from the request header like we used to do before. You can directly verify the bearer token from the req object with fastify. Look at this code difference between no plugin vs plugin implementation:
     ...
     const token = req.headers.authorization?.split(" ")[1];
     if (!token) {
     res.status(403).send({ message: "please provide token" });
     return;
     }
     const decoded = await verify(token);
     if (!decoded) {
     res.status(403).send({ message: "token not valid" });
     return;
     }
     ...
    

    vs

     ...
     const decoded = await req.jwtVerify();
     if (!decoded) {
     res.status(403).send({ message: "token not valid" });
     return;
     }
     ...
    
  3. Before using fastify, I’m using jsonwebtoken for handling jwt in my express framework, because it is the popular choice. On the other hand, fastify choose fast-jwt as their plugin instead. Why? Performance! Fastify has already chosen the best performance library for you.

That’s it! Now you know the difference between a plugin and no plugin implementation in fastify. For me, I prefer to use the plugin for the sake of beautiful code because the performance difference can be ignored. How about you?