TurboJet
Monitoring

Sentry

Enable Sentry with @repo/monitoring and wire it into a Next.js app.

Sentry monitors your application from backend to frontend. TurboJet ships the SDK inside @repo/monitoring — you configure env vars and wire each app that should report.

Not wired by default

Marketing and docs do not depend on @repo/monitoring yet. Follow the wiring steps below to start sending events.

Create a Sentry project

  1. Sign up at Sentry and create an organization if needed.
  2. Create a project with the Next.js platform and copy the DSN.

Enable via environment

Add these to the root .env (see Environment):

.env
NEXT_PUBLIC_ENABLE_SENTRY=true
NEXT_PUBLIC_SENTRY_DSN=https://…@….ingest.sentry.io/…
# Optional — source map uploads during build
SENTRY_AUTH_TOKEN=

Sentry is active only when features.sentry is on (NEXT_PUBLIC_ENABLE_SENTRY !== "false") and NEXT_PUBLIC_SENTRY_DSN is non-empty (IS_SENTRY_ACTIVE in @repo/monitoring).

Create an auth token in the Sentry dashboard under Settings → Auth Tokens if you want source maps uploaded on build.

Customize build options

Edit org/project (and other options) in packages/monitoring/src/next-config.ts. Defaults today:

  • org: hilmirfs-org
  • project: javascript-nextjs
  • tunnelRoute: /monitoring

withMonitoring wraps withSentryConfig and returns the unmodified Next config when Sentry is inactive.

Wire an app

Repeat for apps/marketing and/or apps/docs.

1. Add the dependency

Add @repo/monitoring as a workspace dependency of the app. Include it in transpilePackages in that app’s next.config if other workspace packages are listed there.

2. Wrap Next.js config

Marketing example (compose with existing plugins; order matters):

apps/marketing/next.config.ts
import { withMonitoring } from "@repo/monitoring/next-config";

export default withMonitoring(withNextIntl(nextConfig));

For docs, wrap whatever you already export (for example withMDX(...)).

3. Server / edge instrumentation

Create src/instrumentation.ts:

apps/marketing/src/instrumentation.ts
import { initSentry } from "@repo/monitoring/config";
import { captureRequestError } from "@repo/monitoring/instrumentation";

export async function register() {
	if (process.env.NEXT_RUNTIME === "nodejs") {
		initSentry("server");
	}

	if (process.env.NEXT_RUNTIME === "edge") {
		initSentry("edge");
	}
}

export const onRequestError = captureRequestError;

4. Client instrumentation

In the existing instrumentation-client.ts (PostHog already lives here), also init Sentry:

apps/marketing/src/instrumentation-client.ts
import { initPostHogClient } from "@repo/analytics/client";
import { initSentryClient } from "@repo/monitoring/client";

initPostHogClient();
initSentryClient();

Do not import @repo/monitoring/client from edge/server instrumentation.

5. Global error handler

In src/app/global-error.tsx, report the error to Sentry:

"use client";

import * as Sentry from "@sentry/nextjs";
import { useEffect } from "react";

export default function GlobalError({
	error,
}: {
	error: Error & { digest?: string };
}) {
	useEffect(() => {
		Sentry.captureException(error);
	}, [error]);

	// ... existing UI
}

6. Exclude the tunnel in proxy.ts

The Sentry tunnel is /monitoring. Extend the proxy matcher so i18n middleware does not rewrite it:

export const config = {
	matcher: [
		"/((?!api|monitoring|_next|_vercel|.*\\..*).*)",
	],
};

Capture from app code

Use helpers from @repo/monitoring when you need richer context:

import { addException, setUser } from "@repo/monitoring";

setUser({ user: { id: user.id, email: user.email } });

addException({
	category: "api",
	message: "Checkout failed",
	error,
	context: { orderId },
});

Further reading

On this page