Next.js and dynamic routes

I have setup our Next.js app with Elastic amp and it works fine out of the box.
The only thing is that I have a catch all route which handles all dynamic routes and send the url back to the CMS to retrieve the correct content.

I Kibana its reportet as GET /[...pages]

Is it possible to modify this to send the current URL?

So if I hit localhost/about-us it sends this as GET /about-us

Hi @Aarhus. Thanks for the question.

Currently, no this isn't possible. However, this should work with the usePathAsTransactionName config option. It doesn't currently because the APM agent's Next.js instrumentation isn't honouring that config var. I'll open an issue to fix that.

Once that is fixed in the APM agent there is a concern with using usePathAsTransactionName: true -- high cardinality transaction names. When that config var is turned on, a request to GET /about-us will result in an APM trace transaction name of GET /about-us. This is what you want, I think. However this will happen for any request path: GET /foo, GET /whatever, ...
If there are thousands of different request paths, the result can be performance issues in the APM UI. If your situation won't result in thousands of different request paths, then it will be fine for you.

Does your Next.js app have other dynamic routes? Setting usePathAsTransactionName: true would be global to all routes in the Next.js app.

I've opened Next.js instrumentation doesn't honour `usePathAsTransactionName` config var · Issue #3072 · elastic/apm-agent-nodejs · GitHub

@Aarhus I have an initial branch (PR is here with a potential fix for this. Would you be willing to try it out? To try it, you would need to install the Node.js APM agent from the branch. I believe this should work:

npm install 'elastic-apm-node@elastic/apm-agent-nodejs#trentm/nextjs-usePathAsTransactionName'

Hi @trentm

I tried using the usePathAsTransactionName in the old NPM package but would stil get GET /[...pages] but I could se the get requests for our favicon and fonts would get logged.

I then tried a workaround by importing elastic-apm-node into [...pages].tsx and then in our getServerSideProps function calling

apm.setTransactionName('GET ' + context.resolvedUrl);

This works, but am a little unsure if it causes any side effects?

I will try out your PR and report back :slight_smile:

Hi @trentm

I installed your PR and it kind of works.
I does log the Paths but only when I do a hard browser reload.

When I navigate to another page it doesent log the URL, I think this has someting to do with state between dynamic routes.

I do however reset state between dynamic routes by using the router.asPath as key in _app.tsx,
because we had problems with the state not being reset and new calls to the API not being called.

As for now I will use the old workaround:

export const getServerSideProps: GetServerSideProps<PageProps> = async (context) => {  
  // Add the current URL path as the transaction name for Elastic.
  apm.setTransactionName('GET ' + context.resolvedUrl);

Are you able to show me how/where in your Next.js files you do the import of apm? I'm no Next.js expert and I've had difficulty avoiding Next.js attempting to bundle the full elastic-apm-node module to send and use on the client. (See the first limitation mentioned at Get started with Next.js | APM Node.js Agent Reference [master] | Elastic)

I think using apm.setTransactionName(...) is a good answer -- mainly because it is specific to one route and not a global setting -- but it was those difficulties with only importing elastic-apm-node for the server-side that kept me from suggesting it.

Do you get a transaction named something like Next.js _data/route /[...page]? That is the transaction name given to data requests that Next.js client-side makes for a page (e.g. /_next/data/Tsav2cXHuGQxDzP2J-vmr/a-page.json) -- rather than a full server-side page fetch.

My PR did not update the code that sets that transaction name to use the usePathAsTransactionName config var.

Hi @trentm

Sure, I import the elastic-apm-node in my catch-all-route [...pages]

import { GetServerSideProps } from 'next';
import ErrorPage from 'next/error';
import { ErrorBoundary } from 'react-error-boundary';
import apm from 'elastic-apm-node';
import { withTransaction } from '@elastic/apm-rum-react';
...

const Page = ({ status, data }: PageProps) => {
  if (status === 'error') {
    return <ErrorPage statusCode={500} />;
  }
  ...
};

export const getServerSideProps: GetServerSideProps<PageProps> = async (context) => {

  // Add the current URL path as the transaction name for Elastic.
  apm.setTransactionName('GET ' + context.resolvedUrl);
  ...
};

//export default Page;
export default withTransaction('Page', 'component')(Page);

I just ran a bundle analyze and can se that elastic-apm-node is not getting bundled in the clientside bundles, only the apm-rum-react is bundled clientside:

This topic was automatically closed 20 days after the last reply. New replies are no longer allowed.