From dd9cf869760e15a76c95fc94c39363a44390d166 Mon Sep 17 00:00:00 2001 From: AI Agent Date: Sun, 25 Oct 2026 17:18:58 +0000 Subject: [PATCH] [SDK-801] Document reporting from Next.js proxy/middleware Issue #125: calling the server instance from Edge middleware fails with "post_client_item scope is required" because on the Edge runtime the bundler resolves `rollbar` to its browser build, which sends browser items that a post_server_item token cannot post. - Add src/proxy.ts to the nextjs-approuter example. Next.js 16 runs proxy.ts on the Node.js runtime, so it gets the server build and the server token works. Request data is passed per call instead of via rollbar.configure(), which would mutate the shared instance. - Document the proxy pattern and the Edge middleware options (proxy.ts, runtime: 'nodejs', or a client-token instance). - Fix the example README's env var names to match src/rollbar.ts. Closes #125 Co-Authored-By: Claude Opus 5.5 --- README.md | 2 + examples/nextjs-approuter/README.md | 45 +++++++++++++++++-- .../src/__tests__/proxy.test.tsx | 41 +++++++++++++++++ examples/nextjs-approuter/src/proxy.ts | 30 +++++++++++++ 4 files changed, 115 insertions(+), 3 deletions(-) create mode 100644 examples/nextjs-approuter/src/__tests__/proxy.test.tsx create mode 100644 examples/nextjs-approuter/src/proxy.ts diff --git a/README.md b/README.md index 1fd571d..1756a93 100644 --- a/README.md +++ b/README.md @@ -167,6 +167,8 @@ We suggest you create a single instance for use server side, to be certain there There's a fully functional [example](./examples/nextjs-approuter) that showcases the many ways in which you can integrate Rollbar into your modern web-app leveraging React Server Components and the Next.js App Router. +To report errors from `proxy.ts` or `middleware.ts`, use the server instance from a Node.js runtime. In Edge middleware, `rollbar` resolves to its browser build, and the API rejects those reports when they use a `post_server_item` token. See [Reporting errors from `proxy.ts`](./examples/nextjs-approuter/README.md#reporting-errors-from-proxyts-formerly-middlewarets) in the example. + ## Components The following components are available as named imports from `@rollbar/react`. diff --git a/examples/nextjs-approuter/README.md b/examples/nextjs-approuter/README.md index 63bec7d..2c657b3 100644 --- a/examples/nextjs-approuter/README.md +++ b/examples/nextjs-approuter/README.md @@ -20,7 +20,7 @@ Open [http://localhost:3000](http://localhost:3000) with your browser to see the The first step to using Rollbar with the Next.js App Router is to configure your Rollbar instance. -``` +```ts // ./src/rollbar.ts import Rollbar from 'rollbar'; @@ -31,18 +31,20 @@ const baseConfig = { }; export const clientConfig = { - accessToken: process.env.NEXT_PUBLIC_POST_CLIENT_ITEM_TOKEN, + accessToken: process.env.NEXT_PUBLIC_ROLLBAR_CLIENT_TOKEN, ...baseConfig, }; export const serverInstance = new Rollbar({ - accessToken: process.env.POST_SERVER_ITEM_TOKEN, + accessToken: process.env.ROLLBAR_SERVER_TOKEN, ...baseConfig, }); ``` We suggest you create a single instance for use server side, to be certain there are not more than one, and a config to use in your client side components. React Server Components limit you to passing simple objects as props from Server Components to Client Components. The config object can be used by the Rollbar Provider to construct your Rollbar instance. +Set `NEXT_PUBLIC_ROLLBAR_CLIENT_TOKEN` to a project access token with the `post_client_item` scope and `ROLLBAR_SERVER_TOKEN` to one with the `post_server_item` scope. + ### Configuring the Rollbar Provider To be able to use the hooks consistently through your application. It is easiest if you configure your Rollbar Provider within your root layout. @@ -64,3 +66,40 @@ The `` component provided by this library [may still be used](./s #### Special note on Error boundaries The ErrorBoundary class is not perfect at catching and stopping the propagation of all errors. Be aware, that if you turn on `captureUncaught` or `captureUnhandledRejections` in your Rollbar config you may receive doubled occurrences. + +### Reporting errors from `proxy.ts` (formerly `middleware.ts`) + +Use your server instance, and its `post_server_item` token, from [`proxy.ts`](./src/proxy.ts). Next.js 16 runs `proxy.ts` on the Node.js runtime, so `import Rollbar from 'rollbar'` resolves to Rollbar's server build there. Visit [/proxy_error](http://localhost:3000/proxy_error) to send an error from the proxy to Rollbar. + +```ts +// ./src/proxy.ts +import { NextResponse } from 'next/server'; +import type { NextRequest } from 'next/server'; +import { serverInstance as rollbar } from '@/rollbar'; + +export function proxy(request: NextRequest) { + try { + // ... + return NextResponse.next(); + } catch (error) { + rollbar.error(error as Error, { pathname: request.nextUrl.pathname }); + return new NextResponse('Internal Server Error', { status: 500 }); + } +} +``` + +Pass request details as an argument to `rollbar.error()`, as above, rather than calling `rollbar.configure()` in the proxy. The server instance is shared by every request, so configuring it per request leaks one request's data into another's reports. + +#### Edge runtime middleware + +`middleware.ts` runs on the Edge runtime by default. On Edge, the bundler resolves `rollbar` to its browser build, which sends browser items. The API rejects a browser item sent with a server token, and the report fails with: + +``` +insufficient privileges: post_client_item scope is required but the access token only has post_server_item. +``` + +To report from middleware, do one of these: + +- Upgrade to Next.js 16 and rename `middleware.ts` to `proxy.ts` (`npx @next/codemod@canary middleware-to-proxy .`). +- On Next.js 15.5 or later, run the middleware on the Node.js runtime with `export const config = { runtime: 'nodejs' };`. The server instance then works as shown above. +- If the middleware must stay on Edge, create a separate instance there with `new Rollbar(clientConfig)`, which uses the `post_client_item` token. That token is already public, so using it on Edge exposes nothing new. diff --git a/examples/nextjs-approuter/src/__tests__/proxy.test.tsx b/examples/nextjs-approuter/src/__tests__/proxy.test.tsx new file mode 100644 index 0000000..d80145e --- /dev/null +++ b/examples/nextjs-approuter/src/__tests__/proxy.test.tsx @@ -0,0 +1,41 @@ +/** + * @jest-environment node + */ +import { NextRequest } from 'next/server'; + +import { proxy } from '../proxy'; +import { serverInstance } from '../rollbar'; + +jest.mock('../rollbar', () => ({ + serverInstance: { error: jest.fn(), configure: jest.fn() }, +})); + +describe('app router proxy', () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + it('reports errors with the server instance and per-call request data', async () => { + const response = proxy( + new NextRequest('http://localhost:3000/proxy_error'), + ); + + expect(serverInstance.error).toHaveBeenCalledTimes(1); + const [error, custom] = (serverInstance.error as jest.Mock).mock.calls[0]; + expect(error).toEqual(new Error('Something thrown in proxy.ts')); + expect(custom).toEqual({ pathname: '/proxy_error' }); + expect(serverInstance.configure).not.toHaveBeenCalled(); + + expect(response.status).toBe(500); + expect(await response.text()).toBe( + 'Error reported to Rollbar from proxy.ts', + ); + }); + + it('passes other requests through without reporting', () => { + const response = proxy(new NextRequest('http://localhost:3000/')); + + expect(serverInstance.error).not.toHaveBeenCalled(); + expect(response.headers.get('x-middleware-next')).toBe('1'); + }); +}); diff --git a/examples/nextjs-approuter/src/proxy.ts b/examples/nextjs-approuter/src/proxy.ts new file mode 100644 index 0000000..6e91da8 --- /dev/null +++ b/examples/nextjs-approuter/src/proxy.ts @@ -0,0 +1,30 @@ +import { NextResponse } from 'next/server'; +import type { NextRequest } from 'next/server'; +import { serverInstance as rollbar } from '@/rollbar'; + +// Next.js 16 runs `proxy.ts` on the Node.js runtime, so `rollbar` resolves to +// its server build here and the server access token (`post_server_item`) is +// the right one to use. An Edge `middleware.ts` resolves the browser build +// instead, which the API rejects unless it is given a `post_client_item` +// token. See the README for details. +export function proxy(request: NextRequest) { + try { + if (request.nextUrl.pathname === '/proxy_error') { + throw new Error('Something thrown in proxy.ts'); + } + + return NextResponse.next(); + } catch (error) { + // Pass request details per call rather than calling `rollbar.configure()`, + // which would change the shared instance for every concurrent request. + rollbar.error(error as Error, { pathname: request.nextUrl.pathname }); + + return new NextResponse('Error reported to Rollbar from proxy.ts', { + status: 500, + }); + } +} + +export const config = { + matcher: '/proxy_error', +};