Support for nextjs
I am having trouble figuring out how to get elastic apm working with a hybrid express + nextjs app in an effective way.
It seems to pick up requests fine (to specific URLs) and it also picks up error codes and they show up in the elastic UI "transactions" tab. But I cant seem to get errors to show up in the "errors" tab.
If i throw an error in a next page - eg: ~/page/test.js
Say I have:
function Test() {
throw new Error('Blah');
return (
<p>
Hello World
</p>
)
}
export default Test
It does not show up in the elastic UI.
Describe the solution you'd like I need exceptions that occur in nextjs to show up in the "error" tab in the elastic apm UI.
A hybrid express + nextjs app would look like this as an example: https://github.com/jooj123/test-nextjs
Describe alternatives you've considered
I have tired to send errors manually in the next page, via captureError:
https://www.elastic.co/guide/en/apm/agent/nodejs/3.x/agent-api.html#apm-capture-error
But then i get a compile error like:
./node_modules/elastic-apm-node/lib/instrumentation/async-hooks.js
Module not found: Can't resolve 'async_hooks' in '/Users/martin.jujou/Documents/projects/fe-server-auction-results/node_modules/elastic-apm-node/lib/instrumentation'
> Build error occurred
Error: > Build failed because of webpack errors
This I believe is because nextjs pages are built for client and server and because elastic apm is a node client it blows up with the async_hooks error, much like: https://github.com/zeit/next.js/issues/4348
Not sure if you have any suggestions ...
For any exceptions at the express side of things i'm ok, but if an exception occurs in nextjs context im blind at the moment which is far from ideal
I ended up working around this by doing these steps:
In next.config.js:
webpack: (config, { isServer, webpack }) => {
if (!isServer) {
config.node = {
dgram: 'empty',
fs: 'empty',
net: 'empty',
tls: 'empty',
child_process: 'empty',
};
// ignore apm (might use in nextjs code but dont want it in client bundles)
config.plugins.push(
new webpack.IgnorePlugin(/^(elastic-apm-node)$/),
);
}
return config;
}
Then in _error.js render func you can call captureError manually:
function Error({ statusCode, message, err }) {
const serverSide = typeof window === 'undefined';
// only run this on server side as APM only works on server
if (serverSide) {
// my apm instance (imports elastic-apm-node and returns captureError)
const { captureError } = require('../src/apm');
if (err) {
captureError(err);
} else {
captureError(`Message: ${message}, Status Code: ${statusCode}`);
}
}
}
Not very elegant - if there is any other suggestions that would be helpful - thanks.
I've spent a little time learning Next.js finally. We'd have to add the basic instrumentation of the next module and docs around this as a first step to trace the next server-side actions. I think that should be relatively straightforward.
As stretch work, or for later/separate work, it might be interesting to see if we can use our newish Lambda support to support tracing of Next.js apps deployed to Vercel.
I know this has been a while in coming. I'm getting close on my PR for this and came back here to sanity check that this case is covered. My PR has an example Next.js app setup with APM (https://github.com/elastic/apm-agent-nodejs/tree/trentm/feat-nextjs/examples/nextjs/).
- I added "pages/test.js" as above:
% cat pages/test.js
function Test() {
throw new Error('Blah');
return (
<p>
Hello World
</p>
)
}
export default Test
- I configured APM with my APM server URL (in "elastic-apm-node.js"). I ran the dev server,
npm run dev, and then visitedhttp://localhost:3000/test:
The error is captured, and here is what I see in the Kibana APM app:

And here is the error group page:

Hi all, I know it has been a long time since this issue was opened. If you are a Next.js user who uses the Elastic APM agent (or is interested in doing so), I would love to hear feedback on the new Next.js server instrumentation in [email protected]. Thanks.
@trentm Can you, please, tell me, how to connect it to nextjs in standalone mode? Without standalone mode, it worked well. With him - does not find the package, since standalone does not drag it into the bundle
Can you, please, tell me, how to connect it to nextjs in standalone mode?
@mindyourlifeguide Hi! I assume you mean https://nextjs.org/docs/pages/api-reference/next-config-js/output
I was able to get it to work by making the following changes to the small example Next.js app here: https://github.com/elastic/apm-agent-nodejs/tree/main/examples/nextjs
diff --git a/examples/nextjs/next.config.js b/examples/nextjs/next.config.js
index ae887958..7eb7101a 100644
--- a/examples/nextjs/next.config.js
+++ b/examples/nextjs/next.config.js
@@ -2,6 +2,7 @@
const nextConfig = {
reactStrictMode: true,
swcMinify: true,
+ output: 'standalone',
}
module.exports = nextConfig
diff --git a/examples/nextjs/package.json b/examples/nextjs/package.json
index 45168c85..6d94b1a4 100644
--- a/examples/nextjs/package.json
+++ b/examples/nextjs/package.json
@@ -9,8 +9,8 @@
"lint": "next lint"
},
"dependencies": {
- "elastic-apm-node": "elastic/apm-agent-nodejs#main",
- "next": "12.3.1",
+ "elastic-apm-node": "^3.45.0",
+ "next": "^12.3.1",
"react": "18.2.0",
"react-dom": "18.2.0"
},
diff --git a/examples/nextjs/pages/api/hello.js b/examples/nextjs/pages/api/hello.js
index df63de88..10526995 100644
--- a/examples/nextjs/pages/api/hello.js
+++ b/examples/nextjs/pages/api/hello.js
@@ -1,5 +1,7 @@
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
+import 'elastic-apm-node'
+
export default function handler(req, res) {
res.status(200).json({ name: 'John Doe' })
}
The run:
npm install
npm run build
cd .next/standalone
node -r elastic-apm-node/start-next.js server.js
The trick was in adding import 'elastic-apm-node' in one of my page handlers. This resulted in Next.js's dependency scanning including elastic-apm-node and all of its modules.
However, a warning: when I tried this with [email protected] the built .next/standalone was totally bogus. Is standalone mode possibly broken in next@13? I didn't try [email protected] or [email protected] because elastic-apm-node doesn't currently support those versions (see https://github.com/elastic/apm-agent-nodejs/issues/3263).
@trentm I have version 13.2.1. Without standalone mode, everything worked as it should. It won't start with it.
I managed to get it to work via API route but I am getting an unknown route.
usePathAsTransactionName: true does not add readability
/pages/api/hello.js
import apm from 'elastic-apm-node';
apm.start({
... my config from elastic-apm-node.js
});
I use: "elastic-apm-node": "3.45.0", "next": "13.2.1",
See https://github.com/elastic/apm-agent-nodejs/pull/3073 for supporting usePathAsTransactionName: true.
I'd need more detail to try to understand why you are getting an unknown route.
/pages/api/hello.js
import apm from 'elastic-apm-node'; apm.start({ ... my config from elastic-apm-node.js });
You don't want to be starting the apm agent (apm.start(...)) twice. The agent is started in the -r elastic-apm-node/start-next.js module.
(At this point, please open a separate issue, otherwise this discussion is likely to get lost on a closed issue.)