Mastering Production Nuxt: Real-time APM Integration with New Relic
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:
- 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.
- 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.
- Network: Latency between your users, your CDN, and your server.
- 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:
- 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.
- 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.jsis copied into your Docker image and that yourCMDorENTRYPOINTcommand correctly uses thenode -r newrelicpattern. - 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
- Log in to your New Relic account.
- Navigate to Browser and click Add more data.
- Follow the instructions to add a new browser application.
- 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.infoconfiguration 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-spaversion, 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. Ensurespa.enabled: truein your configuration. deferAttribute: Usingdeferon the script tag is a good practice to prevent the script from blocking the initial page render, improving perceived performance.innerHTMLvs.src: The configuration object (NREUM.info) is best embedded directly usinginnerHTMLso it’s available immediately. The main agent script can then be loaded viasrc.- CSP (Content Security Policy): If you have a Content Security Policy, ensure you allow
script-srcforjs-agent.newrelic.comandbam.nr-data.net(for data ingestion) and potentiallyconnect-srcforbam.nr-data.net.
Nuxt-Specific Best Practices & Considerations
- Distributed Tracing: Ensure
distributed_tracing.enabled: truein both yournewrelic.jsand browser agent config. This allows New Relic to link client-side requests (e.g., auseFetchcall) 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. Thenewrelic.jsconfig’sapp_nameandlicense_keyenvironment checks help manage this.
Verifying Your Integration
After deploying your Nuxt application with New Relic integrated:
- Generate Traffic: Interact with your application, navigate through pages, trigger API calls.
- 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.
- 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:
- What are some of the most challenging Nuxt performance issues you’ve encountered in production, and how did you diagnose them?
- 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?