← Back to blog

Mastering Production Nuxt: Real-time APM Integration with New Relic

nuxtvuejavascriptwebdev

Building a blazing-fast Nuxt application is exhilarating. With Nuxt 3’s powerful features like server-side rendering (SSR), static site generation (SSG), and a robust developer experience, we can deliver incredible user experiences. But what happens when that app hits production? The real challenge begins: ensuring it stays fast, responsive, and error-free under real-world load.

This is where Application Performance Monitoring (APM) becomes indispensable. In the current landscape of modern web development, relying solely on user bug reports or anecdotal evidence for performance issues is a recipe for disaster. We need real-time, actionable insights.

Enter New Relic.

New Relic is a powerful full-stack observability platform that can give you deep visibility into your Nuxt application’s performance, from the browser all the way to your backend services. In this deep dive, we’ll walk through how to integrate New Relic APM with your Nuxt 3 production application, covering both server-side (Node.js agent) and client-side (Browser agent) monitoring.

By the end of this post, you’ll have a robust monitoring setup that allows you to:

  • Pinpoint performance bottlenecks in your server-side rendering and API routes.
  • Track real user experience (RUM) directly from your users’ browsers.
  • Identify and diagnose errors before they escalate.
  • Optimize resource utilization and make data-driven decisions.

Let’s get started!

Why APM is Crucial for Nuxt in Production

Nuxt applications, especially those leveraging SSR, have a unique architecture. Performance issues can manifest in several places:

  1. Server-Side: Slow data fetching during SSR, inefficient server-side logic, database queries, or external API calls can significantly impact your Time To First Byte (TTFB) and overall server response time.
  2. Client-Side: Large JavaScript bundles, slow network requests, inefficient client-side rendering, or third-party script blocking can degrade Core Web Vitals (LCP, FID, CLS) and user interactivity.
  3. Network: Latency between your users, your CDN, and your server.
  4. Backend Services: Even if your Nuxt app is performant, slow microservices or databases it depends on will cause slowdowns.

Without a comprehensive APM solution, debugging these issues in a live environment is like searching for a needle in a haystack, often reactively after users have already complained. New Relic provides the tools to proactively identify and resolve these issues.

Understanding New Relic’s Core Components for Nuxt

New Relic offers a suite of monitoring tools. For a Nuxt application, we’ll primarily focus on two:

  1. New Relic Node.js APM Agent: This agent runs on your server, monitoring your Nuxt server-side processes (e.g., SSR, API routes powered by Nitro). It provides insights into transaction throughput, response times, error rates, database queries, and external service calls.
  2. New Relic Browser Agent: This JavaScript agent runs in your users’ browsers, collecting data on page load times, JavaScript errors, AJAX requests, Core Web Vitals, and user interaction. It’s crucial for understanding actual user experience.

Step 1: Server-Side Monitoring with New Relic Node.js APM Agent

For Nuxt 3, your server-side operations are handled by Nitro, which compiles your server code into a performant Node.js (or other environment) output. To monitor this, we’ll integrate the New Relic Node.js agent.

Prerequisites:

  • A New Relic account.
  • Your New Relic License Key and Application Name (you’ll find these in your New Relic account settings).

1. Install the New Relic Node.js Agent

First, add the agent to your Nuxt project’s dependencies:

npm install newrelic --save
# OR
yarn add newrelic
# OR
pnpm add newrelic

2. Configure the New Relic Agent (newrelic.js)

In the root of your Nuxt project (the same directory as package.json), create a file named newrelic.js. This file will configure the agent.

// newrelic.js
'use strict'

/**
 * New Relic agent configuration.
 *
 * See lib/config.defaults.js in the agent distribution for a more complete
 * description of configuration variables and their potential values.
 */
exports.config = {
  /**
   * Application name acts as a container for all your monitoring data.
   * You can add up to three to group your data in different ways.
   * For example, 'My Nuxt App', 'Production Nuxt App'.
   */
  app_name: [process.env.NEW_RELIC_APP_NAME || 'My Nuxt 3 App (Development)'],
  
  /**
   * Your New Relic license key.
   */
  license_key: process.env.NEW_RELIC_LICENSE_KEY,
  
  /**
   * Set this to true to log agent startup and operation details.
   */
  logging: {
    level: 'info' // 'trace', 'debug', 'info', 'warn', 'error', 'fatal'
  },

  /**
   * When true, the agent will send real time metrics for the HTTP module.
   */
  allow_all_headers: true,
  
  /**
   * Code level metrics improve the ability to troubleshoot.
   * This setting controls the maximum number of methods to instrument.
   */
  transaction_tracer: {
    enabled: true,
    attributes: {
      exclude: [
        'request.headers.cookie',
        'request.headers.authorization',
        'request.headers.proxyAuthorization',
        'request.headers.xForwardedFor',
        'request.headers.xRealIp'
      ]
    }
  },
  
  /**
   * Distributed tracing allows you to see the path of a request across services.
   */
  distributed_tracing: {
    enabled: true
  },

  // Add more configuration as needed.
  // For example, if deploying to a serverless environment like Vercel,
  // consider setting `serverless_mode.enabled = true` and `serverless_mode.unbounded = true`.
}

Important Notes for newrelic.js:

  • Environment Variables: Always use environment variables (process.env.NEW_RELIC_LICENSE_KEY, process.env.NEW_RELIC_APP_NAME) for sensitive information like your license key and to dynamically set the app name based on environment (e.g., ‘My Nuxt App (Production)’).
  • App Name: You can define multiple app names in an array to categorize your data in the New Relic UI.
  • logging.level: Set to 'info' or 'debug' during initial setup to verify it’s working, then reduce to 'warn' or 'error' for production.

3. Integrate with Nuxt 3’s Production Start Script

The New Relic Node.js agent works by instrumenting Node.js core modules. For this to happen effectively, the agent must be loaded as the very first module in your application’s entry point.

For a Nuxt 3 application built with nuxt build, the production server entry point is typically .output/server/index.mjs. We can use Node.js’s -r (require) flag to load the New Relic agent before Nuxt.

Update your package.json start script:

{
  "name": "my-nuxt-app",
  "private": true,
  "scripts": {
    "build": "nuxt build",
    "dev": "nuxt dev",
    "generate": "nuxt generate",
    "preview": "nuxt preview",
    "postinstall": "nuxt prepare",
    "start": "node -r newrelic .output/server/index.mjs"
  },
  "dependencies": {
    "nuxt": "^3.9.0", // Ensure you are on a recent Nuxt 3 version
    "vue": "^3.3.13",
    "newrelic": "^11.x" // Check for the latest stable version
  }
}

Now, when you run npm run start (or yarn start / pnpm start) in your production environment, the New Relic agent will be loaded and will begin monitoring your Nuxt server.

Deployment Gotchas:

  • Docker: If using Docker, ensure newrelic.js is copied into your Docker image and that your CMD or ENTRYPOINT command correctly uses the node -r newrelic pattern.
  • PM2/Process Managers: Configure your process manager to start the Nuxt app with node -r newrelic .output/server/index.mjs.
  • Serverless (e.g., Vercel, Netlify Functions): Integrating the Node.js agent directly for serverless functions can be more complex, as each function invocation is a separate process. New Relic has specific serverless integrations (e.g., for AWS Lambda) which might be a better fit than the traditional Node.js agent. For Nuxt on Vercel, the Nitro server is often wrapped, so custom integration might be limited to what Vercel exposes, or you might rely more heavily on browser-side monitoring and logs. For this blog, we assume a traditional Node.js server deployment.

4. Custom Instrumentation (Optional but Recommended)

While the New Relic agent automatically instruments many common Node.js operations, you might want to add custom instrumentation for specific Nuxt server routes, API endpoints, or complex logic.

You can use the newrelic API directly within your server/api routes or server plugins:

// server/api/my-custom-api.ts
import newrelic from 'newrelic';

export default defineEventHandler(async (event) => {
  // Start a custom web transaction if not already in one
  // This is good for tracking specific API logic.
  newrelic.startWebTransaction('myCustomApiHandler', async () => {
    try {
      // Simulate some work
      await new Promise(resolve => setTimeout(resolve, 100));

      // Record a custom metric
      newrelic.recordMetric('Custom/MyApi/SuccessCount', 1);

      // Add custom attributes to the transaction
      newrelic.addCustomAttribute('userId', '123');
      newrelic.addCustomAttribute('requestType', 'GET');

      return { status: 'success', data: 'Hello from custom API!' };
    } catch (error) {
      // Notice an error
      newrelic.noticeError(error);
      newrelic.recordMetric('Custom/MyApi/ErrorCount', 1);
      throw createError({ statusCode: 500, statusMessage: 'Internal Server Error' });
    } finally {
      // End the transaction (important!)
      newrelic.endTransaction();
    }
  });
});

Note: For Nuxt 3, it’s often better to check if newrelic is defined before calling it, as it might not be available in all environments (e.g., client-side or during build steps).

// Example for a server plugin if you want global instrumentation
// server/plugins/newrelic-instrumentation.server.ts
import newrelic from 'newrelic';

export default defineNitroPlugin((nitroApp) => {
  if (process.env.NODE_ENV === 'production' && newrelic) {
    nitroApp.hooks.hook('request', async (event) => {
      newrelic.setTransactionName(event.req.url || '/unknown');
      // Or you can start a new transaction here if the agent didn't catch it
      // newrelic.startWebTransaction(event.req.url, () => {});
    });

    nitroApp.hooks.hook('error', (error, { event }) => {
      newrelic.noticeError(error);
      newrelic.addCustomAttribute('errorUrl', event.req.url);
    });
  }
});

Step 2: Client-Side Monitoring with New Relic Browser Agent

Monitoring the client-side performance is crucial for understanding the actual user experience. The New Relic Browser agent captures metrics directly from your users’ browsers.

1. Generate Your Browser Monitoring Snippet

  1. Log in to your New Relic account.
  2. Navigate to Browser and click Add more data.
  3. Follow the instructions to add a new browser application.
  4. Choose the “Copy/Paste JavaScript” method. New Relic will generate a unique JavaScript snippet for your application. This snippet typically has two parts: an NREUM.info configuration object and the actual script tag loading the agent.

2. Integrate the Browser Agent into nuxt.config.ts

To inject the New Relic Browser agent into every page of your Nuxt application, you’ll use the app.head.script option in your nuxt.config.ts. This ensures the script is placed in the <head> section of your HTML, allowing it to start collecting data as early as possible.

// nuxt.config.ts
export default defineNuxtConfig({
  // ... other Nuxt configurations
  app: {
    head: {
      script: [
        // New Relic Browser Monitoring Script
        // IMPORTANT: Replace the innerHTML content and src with YOUR ACTUAL SNIPPET from New Relic UI.
        // This is a placeholder example.
        {
          innerHTML: `
            window.NREUM || (NREUM = {});
            NREUM.info = {
              "init": {
                "distributedTracing": {
                  "enabled": true,
                  "exclude_nr_lib": false
                },
                "privacy": {
                  "cookies_enabled": true
                },
                "ajax": {
                  "deny_list": ["bam.nr-data.net"]
                }
              },
              "loader_config": {
                "accountID": "YOUR_ACCOUNT_ID",
                "agentID": "YOUR_AGENT_ID",
                "trustKey": "YOUR_TRUST_KEY",
                "licenseKey": "YOUR_BROWSER_LICENSE_KEY", // This is usually different from the Node.js agent license key
                "applicationID": "YOUR_BROWSER_APPLICATION_ID"
              },
              "agent": "js-agent.newrelic.com/nr-spa-1.240.0.min.js", // Ensure this path matches your snippet's agent version
              "spa": {
                "enabled": true // Critical for Nuxt SPAs and client-side navigation
              }
            };
          `,
          type: 'text/javascript',
          charset: 'utf-8',
          // Optionally, add a `hid` property if you have multiple scripts and need to prevent duplicates or control order.
          // hid: 'new-relic-browser-config',
        },
        // The actual New Relic agent script loader
        {
          src: 'https://js-agent.newrelic.com/nr-spa-1.240.0.min.js', // Replace with the actual URL from your snippet
          defer: true, // Use defer to avoid blocking render
          crossorigin: 'anonymous',
          // hid: 'new-relic-browser-agent',
        }
      ]
    }
  },
  // ... rest of your nuxt.config.ts
});

Key Considerations for Browser Agent Integration:

  • SPA Monitoring: The New Relic Browser agent, especially the nr-spa version, is specifically designed for Single Page Applications (like Nuxt in SPA mode or client-side navigation). It automatically tracks route changes and re-measures performance metrics for each new “page view” within the SPA. Ensure spa.enabled: true in your configuration.
  • defer Attribute: Using defer on the script tag is a good practice to prevent the script from blocking the initial page render, improving perceived performance.
  • innerHTML vs. src: The configuration object (NREUM.info) is best embedded directly using innerHTML so it’s available immediately. The main agent script can then be loaded via src.
  • CSP (Content Security Policy): If you have a Content Security Policy, ensure you allow script-src for js-agent.newrelic.com and bam.nr-data.net (for data ingestion) and potentially connect-src for bam.nr-data.net.

Nuxt-Specific Best Practices & Considerations

  • Distributed Tracing: Ensure distributed_tracing.enabled: true in both your newrelic.js and browser agent config. This allows New Relic to link client-side requests (e.g., a useFetch call) to their corresponding server-side processing, giving you an end-to-end view of a request’s journey.
  • Error Reporting: Both agents automatically capture unhandled errors. For client-side, this includes JavaScript errors. For server-side, it captures exceptions. You can manually report errors using newrelic.noticeError(error).
  • Custom Attributes: Add custom attributes to transactions to provide more context. For example, newrelic.addCustomAttribute('userId', user.id) on the server can help correlate performance issues with specific user types.
  • Nuxt Cache (server/cache): If you’re using Nuxt’s server-side caching, ensure your APM setup is aware of it. Cached responses might show extremely fast transaction times, which is good, but you’ll want to monitor cache hit/miss rates separately if possible.
  • Static Assets: For purely static Nuxt sites (nuxt generate), the Node.js APM agent isn’t relevant as there’s no running Node.js server to monitor. In this case, focus entirely on the Browser agent.
  • Local Development: You typically wouldn’t run the New Relic agent in development (npm run dev) as it adds overhead. The newrelic.js config’s app_name and license_key environment checks help manage this.

Verifying Your Integration

After deploying your Nuxt application with New Relic integrated:

  1. Generate Traffic: Interact with your application, navigate through pages, trigger API calls.
  2. Check New Relic UI:
    • APM: Go to your APM dashboard in New Relic. You should see your Nuxt application listed, with data for throughput, response time, and errors. Explore transaction traces to see details of server-side operations, including external calls and database queries.
    • Browser: Go to your Browser dashboard. You should see data on page load performance, AJAX requests, JavaScript errors, and Core Web Vitals.
  3. Logs: Check your server logs for New Relic agent initialization messages. If the agent fails to start or connect, it will usually log an error.

Conclusion

Integrating New Relic APM into your production Nuxt application transforms your ability to deliver high-performance, resilient user experiences. From understanding the nuances of server-side rendering performance with the Node.js agent to gaining real-time insights into user interactions and client-side bottlenecks with the Browser agent, you’re equipped with the data needed to proactively optimize and troubleshoot.

Don’t wait for your users to report slow performance or errors. Empower yourself and your team with real-time observability and ensure your Nuxt masterpiece shines in production.


Discussion Questions for Readers:

  1. What are some of the most challenging Nuxt performance issues you’ve encountered in production, and how did you diagnose them?
  2. Beyond New Relic, are there other APM or observability tools you’ve found particularly effective with Nuxt 3, and what features stood out to you?

Comments

No comments yet. Be the first!

We use cookies to enhance your browsing experience, serve personalized content, and analyze our traffic. By clicking "OK", you consent to our use of cookies and agree to our Terms of Service & Privacy Policy.