javascript

Monitor the Performance of Your Node.js Fastify App with AppSignal

Damilola Olatunji

Damilola Olatunji on

Monitor the Performance of Your Node.js Fastify App with AppSignal

Fastify stands out among Node.js web frameworks for its obsessive focus on performance and boasts impressive benchmarks, with throughput often 2-3x higher than Express and other popular alternatives.

But here's the paradox: without proper visibility, even applications with a good foundation will degrade over time as you add features and complexity.

In this guide, part one of a two-part series, you'll learn how to identify critical Fastify performance indicators, explore different instrumentation approaches for collecting data, and use AppSignal's integration with Fastify to gain visibility into your application's performance.

Let's get started!

Key Performance Indicators for Node.js Applications

Fastify is a high-performance framework designed for efficiency, but even well-optimized applications can experience bottlenecks.

It is essential that you understand the right metrics to monitor for maintaining optimal performance, such as:

  • Response time: This remains the most critical metric, directly impacting user experience. Beyond simple averages, track percentiles (95th, 99th) to identify outliers affecting your most important users. A 95th percentile of 200ms means 95% of requests complete in under 200ms, while 5% take longer.
  • Throughput measures your application's request handling capacity (requests per second). For Fastify, baseline expectations should be significantly higher than other frameworks — often 2-3x what you'd expect from Express.
  • Error rate tracks the percentage of requests resulting in errors (HTTP 500s). Even the fastest application is useless if it's returning errors, making this a critical companion to performance metrics.
  • Resource utilization (CPU, memory, network I/O) provides context for other metrics. Node.js applications are particularly sensitive to CPU usage, as the event loop can become blocked under heavy computational load.

Collecting Performance Metrics in Node.js

Once you've identified which metrics to track, the next step is to collect them by instrumenting your code.

Instrumentation is the process of adding measurement code to your application to capture performance data (and other telemetry) during execution.

There are two broad categories of instrumentation: manual and automatic. Let's look at manual first:

  1. Manual instrumentation requires explicitly adding measurement code to specific parts of your application. This gives you complete control over what gets measured and how, but it takes more effort to implement and maintain.

    For example, Node provides native performance measurement capabilities through the perf_hooks module:

    JavaScript
    import { performance, PerformanceObserver } from "node:perf_hooks"; // Create observer to process measurements const observer = new PerformanceObserver((items) => { const measurements = items.getEntries(); measurements.forEach((measurement) => { // Each measurement will be logged here. In production, you'd send this // to a monitoring service to keep track of performance over time console.log(`${measurement.name}: ${measurement.duration}ms`); }); }); observer.observe({ entryTypes: ["measure"] }); fastify.get("/users", async (request, reply) => { // You need to manually collect the measurements you're interested in performance.mark("db-query-start"); const { rows } = await request.db("SELECT * FROM users"); // End the measurement and create named measure performance.mark("db-query-end"); performance.measure("Database Query", "db-query-start", "db-query-end"); return rows; });

    This snippet demonstrates manual instrumentation with Node.js's built-in perf_hooks module, where you explicitly mark the start and end points of operations you want to measure (in this case, a database query).

    The PerformanceObserver captures these measurements when they complete, allowing you to see precisely how long each labeled operation takes. You can then send these to a monitoring service rather than just logging them to the console.

  2. Automatic instrumentation uses libraries or agents that inject measurement code into your application, often by patching common libraries or using language features like proxies. This approach is lower effort but may not capture everything you need.

    For example, the OpenTelemetry auto instrumentations package automatically instruments a host of popular Node.js libraries and frameworks to collect high-level trace data. This allows you to see exactly which high-level functions are called during request processing, and how long they take to complete.

    All you need to get started is the following:

    JavaScript
    import { getNodeAutoInstrumentations } from "@opentelemetry/auto-instrumentations-node"; import { NodeSDK } from "@opentelemetry/sdk-node"; import { ConsoleSpanExporter } from "@opentelemetry/sdk-trace-node"; const sdk = new NodeSDK({ traceExporter: new ConsoleSpanExporter(), instrumentations: [getNodeAutoInstrumentations()], }); sdk.start();

In practice, a combination of both approaches is best: automatic instrumentation for broad coverage, and manual instrumentation for tracking application-specific operations.

Tracking Performance Measurements

When it comes to ingesting and turning the raw measurements collected from your application into useful insights, you have many options, from open-source solutions to cloud-based hosted services.

In most cases, using a dedicated Application Performance Monitoring (APM) solution is the most comprehensive approach, helping you instrument your code while offering dashboards, alerts, and long-term metric storage.

In the next section, we'll explore how to set up AppSignal specifically for Fastify applications (though the general concepts apply regardless of which framework you're using).

Setting Up a Sample Fastify Application

To follow along with this guide, you'll need to set up our Fastify demo application that includes a few endpoints to demonstrate performance monitoring scenarios.

First, clone the demo repository and install the necessary dependencies:

Shell
git clone https://github.com/damilolaolatunji/fastify-perf-demo && cd fastify-perf-demo npm install

The demo uses PostgreSQL with the Northwind sample database. You can quickly set up a PostgreSQL instance with Docker by running:

Shell
docker run \ --rm \ --name postgres \ --env POSTGRES_PASSWORD=admin \ --env POSTGRES_DB=northwind \ --volume pg-data:/var/lib/postgresql/data \ --publish 5432:5432 \ postgres:alpine

This command launches a lightweight PostgreSQL container accessible at http://localhost:5432, using a default postgres user (whose password is set to admin) and creates a northwind database.

Once the database is up and running, you can download and load the sample data by executing the following commands:

Shell
curl -O https://raw.githubusercontent.com/pthom/northwind_psql/refs/heads/master/northwind.sql
Shell
docker exec -i postgres psql -U postgres -d northwind < northwind.sql

Then update your database configuration in the server.js file if necessary:

JavaScript
// server.js . . . // Initialize Knex connection const db = knex({ client: 'pg', connection: { host: 'localhost', user: 'postgres', password: 'admin', database: 'northwind', }, }); . . .

Once you're all set up, start the development server by running:

Shell
npm start

Test it by querying the /customers-with-orders route:

Shell
curl http://localhost:3000/customers-with-orders

You should see the following response:

JSON
{ "customerCount": 91, "customers": [ { "customer_id": "ALFKI", "company_name": "Alfreds Futterkiste", "contact_name": "Maria Anders", "orders": [. . .] } . . . ] }

With the application up and running, let's go ahead and integrate the AppSignal SDK for performance monitoring.

Integrating AppSignal in Your Fastify App

Before you can start monitoring with AppSignal, you'll need to sign up for a free account and create a new Node.js application.

AppSignal add app

You'll be guided through a setup process. Select Node.js and Fastify as your technology stack, install the required packages, name your application, and get the minimal setup code.

When you get to the Add the API Key page, copy your APPSIGNAL_PUSH_API_KEY and store it in a .env file at your project root:

AppSignal Push API Key
plaintext
# .env APPSIGNAL_PUSH_API_KEY=<your_push_api_key>

Next, create an appsignal.js file with the following content:

JavaScript
// appsignal.js import { Appsignal } from "@appsignal/nodejs"; new Appsignal({ active: true, name: "<YOUR_APP_NAME>", // replace this with the name of the AppSignal app });

That's all you need for basic performance monitoring, as Fastify is automatically instrumented by the @appsignal/nodejs package. The instrumentation tracks each component's execution time during request processing and captures any errors.

Import this file into your application's entry script after loading environment variables, but before any other application logic:

JavaScript
// server.js import 'dotenv/config'; import './appsignal.js'; // Import it here import Fastify from 'fastify'; import Knex from 'knex'; . . .

To generate traffic for testing, use a tool like autocannon:

Shell
npx autocannon -c 3 -d 300s http://localhost:3000/ http://localhost:3000/user-balances http://localhost:3000/customers-with-orders

Within a few minutes, your AppSignal dashboard's Overview section will display key metrics, including error rate, response time, and throughput, along with recent errors:

AppSignal overview

For route-specific performance data, navigate to the Performance > Issue list to see average response times, throughput, and impact scores for each endpoint. Click any route to view sample requests:

AppSignal Route Samples

Selecting a sample provides a detailed breakdown of the request processing timeline:

AppSignal sample breakdown

In this example, the /user-balances route has two fetch requests dominating the response time, with the second taking significantly longer.

If you hover your cursor over the individual events in the timeline, you'll see additional event details (if available). For example, database queries in the /customers-with-orders route include the specific query that was executed:

AppSignal Event Timeline details

Finally, you can check out the Graphs tab to see response times and a throughput graph for the specific route:

AppSignal Graph tab

And that's it for now!

Wrapping Up

In this guide, we explored the key performance indicators for Node.js applications and learned how to collect these metrics through AppSignal's automatic instrumentation for Fastify.

In part two, we'll dive deeper into AppSignal's features to not just monitor, but actively detect and resolve performance issues.

We'll cover how to identify and fix performance regressions, find opportunities for optimization, and implement custom instrumentation to track business-specific metrics.

Stay tuned!

Wondering what you can do next?

Finished this article? Here are a few more things you can do:

  • Share this article on social media
Damilola Olatunji

Damilola Olatunji

Damilola is a freelance technical writer and software developer based in Lagos, Nigeria. He specializes in JavaScript and Node.js, and aims to deliver concise and practical articles for developers. When not writing or coding, he enjoys reading, playing games, and traveling.

All articles by Damilola Olatunji

Become our next author!

Find out more

AppSignal monitors your apps

AppSignal provides insights for Ruby, Rails, Elixir, Phoenix, Node.js, Express and many other frameworks and libraries. We are located in beautiful Amsterdam. We love stroopwafels. If you do too, let us know. We might send you some!

Discover AppSignal
AppSignal monitors your apps