DEV Community

Ryan Carter
Ryan Carter

Posted on

Increasing RPS in Nuxt.js

JavaScript · Vue.js

We all know that Nuxt.js 2 (and in general any Node.js application using SSR) cannot handle high load without caching. In an average project, when SSR mode is enabled, the application usually handles only 20–30 RPS, which is extremely low.

The standard solution is to connect one or more caching packages and cache pages or requests. In general, this helps a lot.

However, on real projects I encountered several problems and wanted to stabilize the situation — to give the application at least a chance to run without SSR when needed.


Problems Encountered in Production

1. Slow API responses

Sometimes the backend does not anticipate certain scenarios, and in production API requests become slow. When SSR depends on such APIs, the entire page render slows down.

2. JavaScript errors in SSR mode

Errors may occur only during SSR, while in the browser everything works fine. These errors can appear due to carelessness or other reasons and can completely break server-side rendering.

3. High load or DDoS attacks

Under heavy load or during DDoS attacks, the server can hit memory or CPU limits. In such cases, SSR must be disabled and the client should receive a minimal HTML shell so the application can start in the browser.


SPA Rules Agreed with DevOps

After discussions with DevOps engineers, we defined several rules that an SPA application should follow:

  • Disable SSR when page rendering becomes slow and delegate rendering to the browser
  • Downgrade to SPA not globally, but only for problematic routes
  • Under heavy load, when rendering becomes slower and slower, track this state and temporarily disable SSR for the entire application, then automatically enable it again later
  • Force-disable SSR on pages where it is not needed to reduce server load
  • Add caching and store data in Redis

Automatic Solution

To implement all these rules, I wrote a module that performs all these actions automatically.

To achieve the maximum effect, it is recommended to additionally use a caching module (for example, nuxt-ssr-cache).


Installation

npm install @drozd/nuxt-performance
Enter fullscreen mode Exit fullscreen mode

Configuration

Next, add the module configuration to nuxt.config.js:

module.exports = {
  performance: {
    // Log route render time
    renderRouteTimeCallback: (route, ms) => {
      console.log(`time render route: ${route} ${ms} ms`);
    },

    // Disable SSR for specific routes
    isOnlySPA: (route, _context) => {
      return route === '/personal';
    },

    // Maximum allowed render time for SSR (ms)
    maxRenderTime: 1000,

    // Number of SSR render attempts before disabling SSR
    maxAttemptSsr: 5,

    // Routes excluded from the module entirely
    excludeRoutes: /healthcheck/,

    // How long SSR is disabled for a specific route (ms)
    timeDisabledSsrWithRoute: 1000 * 60,

    // Interval for clearing the global slow-render counter
    clearSlowCounterIntervalTime: 1000 * 60 * 5,

    // Total number of slow requests before disabling SSR globally
    maxSlowCount: 50
  }

  // ...
};
Enter fullscreen mode Exit fullscreen mode

Results

After implementing this module and enabling caching for all pages for 10 seconds, we achieved an ~15x increase in RPS — from 30 to ~500.

The exact result may vary depending on the resources allocated by system administrators.


Repository

Source code is available here:

https://github.com/chain-utils/nuxt-performance

Final Notes

If you have experience with performance optimizations or alternative approaches, feel free to share — it will be useful for everyone.

Top comments (0)