Next.js Supabase V3 (#463)

Version 3 of the kit:
- Radix UI replaced with Base UI (using the Shadcn UI patterns)
- next-intl replaces react-i18next
- enhanceAction deprecated; usage moved to next-safe-action
- main layout now wrapped with [locale] path segment
- Teams only mode
- Layout updates
- Zod v4
- Next.js 16.2
- Typescript 6
- All other dependencies updated
- Removed deprecated Edge CSRF
- Dynamic Github Action runner
This commit is contained in:
Giancarlo Buomprisco
2026-03-24 13:40:38 +08:00
committed by GitHub
parent 4912e402a3
commit 7ebff31475
840 changed files with 71395 additions and 20095 deletions

View File

@@ -0,0 +1,356 @@
---
status: "published"
title: 'Manual Error Capturing'
label: 'Capturing Errors'
description: 'Learn how to manually capture errors and exceptions in both client and server code using Makerkit monitoring hooks and services.'
order: 2
---
While Makerkit automatically captures unhandled errors, you often need to capture errors manually in try-catch blocks, form submissions, or API calls. This guide shows you how to capture errors programmatically.
{% sequence title="Error Capturing Guide" description="Manually capture errors in your application" %}
[Client-Side Error Capturing](#client-side-error-capturing)
[Server-Side Error Capturing](#server-side-error-capturing)
[Adding Context to Errors](#adding-context-to-errors)
[Identifying Users](#identifying-users)
{% /sequence %}
## Client-Side Error Capturing
### Using the useCaptureException Hook
The simplest way to capture errors in React components is the `useCaptureException` hook:
```typescript {% title="components/error-boundary.tsx" %}
'use client';
import { useCaptureException } from '@kit/monitoring/hooks';
export default function ErrorPage({
error,
reset,
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
// Automatically captures the error when the component mounts
useCaptureException(error);
return (
<div>
<h2>Something went wrong</h2>
<button onClick={reset}>Try again</button>
</div>
);
}
```
This hook captures the error once when the component mounts. It's ideal for error boundary pages.
### Using the useMonitoring Hook
For more control, use the `useMonitoring` hook to access the monitoring service directly:
```typescript {% title="components/form.tsx" %}
'use client';
import { useMonitoring } from '@kit/monitoring/hooks';
export function ContactForm() {
const monitoring = useMonitoring();
const handleSubmit = async (formData: FormData) => {
try {
await submitForm(formData);
} catch (error) {
// Capture the error with context
monitoring.captureException(error as Error, {
formData: Object.fromEntries(formData),
action: 'contact_form_submit',
});
// Show user-friendly error
toast.error('Failed to submit form');
}
};
return (
<form action={handleSubmit}>
{/* form fields */}
</form>
);
}
```
### Capturing Events
Track custom monitoring events (not errors) using `captureEvent`:
```typescript {% title="Example: Tracking important actions" %}
'use client';
import { useMonitoring } from '@kit/monitoring/hooks';
export function DangerZone() {
const monitoring = useMonitoring();
const handleDeleteAccount = async () => {
// Track the action before attempting
monitoring.captureEvent('account_deletion_attempted', {
userId: user.id,
timestamp: new Date().toISOString(),
});
try {
await deleteAccount();
} catch (error) {
monitoring.captureException(error as Error);
}
};
return (
<button onClick={handleDeleteAccount}>
Delete Account
</button>
);
}
```
## Server-Side Error Capturing
### In Server Actions
```typescript {% title="lib/actions/create-project.ts" %}
'use server';
import { getServerMonitoringService } from '@kit/monitoring/server';
export async function createProject(formData: FormData) {
const monitoring = await getServerMonitoringService();
await monitoring.ready();
try {
const project = await db.project.create({
data: {
name: formData.get('name') as string,
},
});
return { success: true, project };
} catch (error) {
await monitoring.captureException(error as Error, {
action: 'createProject',
formData: Object.fromEntries(formData),
});
return { success: false, error: 'Failed to create project' };
}
}
```
### In API Routes
```typescript {% title="app/api/webhook/route.ts" %}
import { getServerMonitoringService } from '@kit/monitoring/server';
export async function POST(request: Request) {
const monitoring = await getServerMonitoringService();
await monitoring.ready();
try {
const data = await request.json();
await processWebhook(data);
return Response.json({ received: true });
} catch (error) {
await monitoring.captureException(
error as Error,
{ webhook: 'stripe' },
{
path: request.url,
method: 'POST',
}
);
return Response.json(
{ error: 'Webhook processing failed' },
{ status: 500 }
);
}
}
```
## Adding Context to Errors
The `captureException` method accepts two optional parameters for adding context:
```typescript
captureException(
error: Error,
extra?: Record<string, unknown>, // Additional data
config?: Record<string, unknown> // Provider-specific config
)
```
### Extra Data
Add any relevant information that helps debug the error:
```typescript {% title="Example: Rich error context" %}
monitoring.captureException(error, {
// User context
userId: user.id,
userEmail: user.email,
userPlan: user.subscription.plan,
// Action context
action: 'checkout',
productId: product.id,
quantity: cart.items.length,
// Environment context
feature_flags: getEnabledFlags(),
app_version: process.env.APP_VERSION,
});
```
### Configuration
Pass provider-specific configuration:
```typescript {% title="Example: Sentry-specific config" %}
monitoring.captureException(
error,
{ userId: user.id },
{
// Sentry-specific options
level: 'error',
tags: {
component: 'checkout',
flow: 'payment',
},
}
);
```
## Identifying Users
Associate errors with users to track issues per user:
```typescript {% title="Example: Identify user after login" %}
'use client';
import { useMonitoring } from '@kit/monitoring/hooks';
export function useIdentifyUser(user: { id: string; email: string }) {
const monitoring = useMonitoring();
useEffect(() => {
if (user) {
monitoring.identifyUser({
id: user.id,
email: user.email,
});
}
}, [user, monitoring]);
}
```
Once identified, all subsequent errors from that session are associated with the user in your monitoring dashboard.
## Best Practices
### Capture at Boundaries
Capture errors at the boundaries of your application:
- Form submissions
- API calls
- Third-party integrations
- File uploads
- Payment processing
```typescript {% title="Pattern: Error boundary function" %}
async function withErrorCapture<T>(
fn: () => Promise<T>,
context: Record<string, unknown>
): Promise<T | null> {
const monitoring = await getServerMonitoringService();
await monitoring.ready();
try {
return await fn();
} catch (error) {
await monitoring.captureException(error as Error, context);
return null;
}
}
// Usage
const result = await withErrorCapture(
() => processPayment(paymentData),
{ action: 'processPayment', amount: paymentData.amount }
);
```
### Don't Over-Capture
Not every error needs to be captured:
```typescript {% title="Example: Selective capturing" %}
try {
await fetchUserData();
} catch (error) {
if (error instanceof NetworkError) {
// Expected error, handle gracefully
return fallbackData;
}
if (error instanceof AuthError) {
// Expected error, redirect to login
redirect('/login');
}
// Unexpected error, capture it
monitoring.captureException(error as Error);
throw error;
}
```
### Include Actionable Context
Add context that helps you fix the issue:
```typescript {% title="Good context" %}
monitoring.captureException(error, {
userId: user.id,
action: 'export_csv',
rowCount: data.length,
filters: appliedFilters,
exportFormat: 'csv',
});
```
```typescript {% title="Less useful context" %}
monitoring.captureException(error, {
error: 'something went wrong',
time: Date.now(),
});
```
{% faq
title="Frequently Asked Questions"
items=[
{"question": "Should I capture validation errors?", "answer": "Generally no. Validation errors are expected and user-facing. Capture unexpected errors like database failures, third-party API errors, or logic errors that shouldn't happen."},
{"question": "How do I avoid capturing the same error multiple times?", "answer": "Capture at the highest level where you handle the error. If you rethrow an error, don't capture it at the lower level. Let it bubble up to where it's finally handled."},
{"question": "What's the difference between captureException and captureEvent?", "answer": "captureException is for errors and exceptions. captureEvent is for tracking important actions or milestones that aren't errors, like 'user_deleted_account' or 'large_export_started'."},
{"question": "Does capturing errors affect performance?", "answer": "Minimally. Error capturing is asynchronous and non-blocking. However, avoid capturing in hot paths or loops. Capture at boundaries, not in every function."}
]
/%}
**Next:** [Creating a Custom Monitoring Provider →](custom-provider)

View File

@@ -0,0 +1,334 @@
---
status: "published"
title: 'Creating a Custom Monitoring Provider'
label: 'Custom Provider'
description: 'Integrate LogRocket, Bugsnag, Datadog, or any monitoring service by implementing the MonitoringService interface.'
order: 1
---
{% sequence title="How to create a custom monitoring provider" description="Add your preferred monitoring service to the kit." %}
[Implement the MonitoringService interface](#implement-the-monitoringservice-interface)
[Register the provider](#register-the-provider)
[Configure for server-side](#server-side-configuration)
{% /sequence %}
The monitoring system uses a registry pattern that loads providers dynamically based on the `NEXT_PUBLIC_MONITORING_PROVIDER` environment variable. You can add support for LogRocket, Bugsnag, Datadog, or any other service.
## Implement the MonitoringService Interface
Create a new package or add to the existing monitoring packages:
```typescript {% title="packages/monitoring/logrocket/src/logrocket-monitoring.service.ts" %}
import LogRocket from 'logrocket';
import { MonitoringService } from '@kit/monitoring-core';
export class LogRocketMonitoringService implements MonitoringService {
private readonly readyPromise: Promise<unknown>;
private readyResolver?: (value?: unknown) => void;
constructor() {
this.readyPromise = new Promise(
(resolve) => (this.readyResolver = resolve),
);
void this.initialize();
}
async ready() {
return this.readyPromise;
}
captureException(error: Error, extra?: Record<string, unknown>) {
LogRocket.captureException(error, {
extra,
});
}
captureEvent(event: string, extra?: Record<string, unknown>) {
LogRocket.track(event, extra);
}
identifyUser(user: { id: string; email?: string; name?: string }) {
LogRocket.identify(user.id, {
email: user.email,
name: user.name,
});
}
private async initialize() {
const appId = process.env.NEXT_PUBLIC_LOGROCKET_APP_ID;
if (!appId) {
console.warn('LogRocket app ID not configured');
this.readyResolver?.();
return;
}
if (typeof window !== 'undefined') {
LogRocket.init(appId);
}
this.readyResolver?.();
}
}
```
### Package Configuration
Create the package structure:
```json {% title="packages/monitoring/logrocket/package.json" %}
{
"name": "@kit/logrocket",
"version": "0.0.1",
"private": true,
"exports": {
".": "./src/index.ts"
},
"dependencies": {
"@kit/monitoring-core": "workspace:*",
"logrocket": "^3.0.0"
}
}
```
```typescript {% title="packages/monitoring/logrocket/src/index.ts" %}
export { LogRocketMonitoringService } from './logrocket-monitoring.service';
```
## Register the Provider
### Client-Side Registration
Update the monitoring provider registry:
```typescript {% title="packages/monitoring/api/src/components/provider.tsx" %}
import { lazy } from 'react';
import { createRegistry } from '@kit/shared/registry';
import {
MonitoringProvider as MonitoringProviderType,
getMonitoringProvider,
} from '../get-monitoring-provider';
type ProviderComponent = {
default: React.ComponentType<React.PropsWithChildren>;
};
const provider = getMonitoringProvider();
const Provider = provider
? lazy(() => monitoringProviderRegistry.get(provider))
: null;
const monitoringProviderRegistry = createRegistry<
ProviderComponent,
NonNullable<MonitoringProviderType>
>();
// Existing Sentry registration
monitoringProviderRegistry.register('sentry', async () => {
const { SentryProvider } = await import('@kit/sentry/provider');
return {
default: function SentryProviderWrapper({ children }) {
return <SentryProvider>{children}</SentryProvider>;
},
};
});
// Add LogRocket registration
monitoringProviderRegistry.register('logrocket', async () => {
const { LogRocketProvider } = await import('@kit/logrocket/provider');
return {
default: function LogRocketProviderWrapper({ children }) {
return <LogRocketProvider>{children}</LogRocketProvider>;
},
};
});
```
### Add Provider Type
Update the provider enum:
```typescript {% title="packages/monitoring/api/src/get-monitoring-provider.ts" %}
import * as z from 'zod';
const MONITORING_PROVIDERS = [
'sentry',
'logrocket', // Add your provider
'',
] as const;
export const MONITORING_PROVIDER = z
.enum(MONITORING_PROVIDERS)
.optional()
.transform((value) => value || undefined);
export type MonitoringProvider = z.output<typeof MONITORING_PROVIDER>;
export function getMonitoringProvider() {
const result = MONITORING_PROVIDER.safeParse(process.env.NEXT_PUBLIC_MONITORING_PROVIDER);
if (result.success) {
return result.data;
}
return undefined;
}
```
### Create the Provider Component
```typescript {% title="packages/monitoring/logrocket/src/provider.tsx" %}
import { MonitoringContext } from '@kit/monitoring-core';
import { LogRocketMonitoringService } from './logrocket-monitoring.service';
const logrocket = new LogRocketMonitoringService();
export function LogRocketProvider({ children }: React.PropsWithChildren) {
return (
<MonitoringContext.Provider value={logrocket}>
{children}
</MonitoringContext.Provider>
);
}
```
## Server-Side Configuration
Register the provider for server-side error capture:
```typescript {% title="packages/monitoring/api/src/services/get-server-monitoring-service.ts" %}
import {
ConsoleMonitoringService,
MonitoringService,
} from '@kit/monitoring-core';
import { createRegistry } from '@kit/shared/registry';
import {
MonitoringProvider,
getMonitoringProvider,
} from '../get-monitoring-provider';
const serverMonitoringRegistry = createRegistry<
MonitoringService,
NonNullable<MonitoringProvider>
>();
// Existing Sentry registration
serverMonitoringRegistry.register('sentry', async () => {
const { SentryMonitoringService } = await import('@kit/sentry');
return new SentryMonitoringService();
});
// Add LogRocket registration
serverMonitoringRegistry.register('logrocket', async () => {
const { LogRocketMonitoringService } = await import('@kit/logrocket');
return new LogRocketMonitoringService();
});
export async function getServerMonitoringService() {
const provider = getMonitoringProvider();
if (!provider) {
return new ConsoleMonitoringService();
}
return serverMonitoringRegistry.get(provider);
}
```
## Environment Variables
Add your provider's configuration:
```bash {% title="apps/web/.env.local" %}
# Enable LogRocket as the monitoring provider
NEXT_PUBLIC_MONITORING_PROVIDER=logrocket
# LogRocket configuration
NEXT_PUBLIC_LOGROCKET_APP_ID=your-org/your-app
```
## Example: Datadog Integration
Here's a complete example for Datadog RUM:
```typescript {% title="packages/monitoring/datadog/src/datadog-monitoring.service.ts" %}
import { datadogRum } from '@datadog/browser-rum';
import { MonitoringService } from '@kit/monitoring-core';
export class DatadogMonitoringService implements MonitoringService {
private readonly readyPromise: Promise<unknown>;
private readyResolver?: (value?: unknown) => void;
constructor() {
this.readyPromise = new Promise(
(resolve) => (this.readyResolver = resolve),
);
void this.initialize();
}
async ready() {
return this.readyPromise;
}
captureException(error: Error, extra?: Record<string, unknown>) {
datadogRum.addError(error, {
...extra,
});
}
captureEvent(event: string, extra?: Record<string, unknown>) {
datadogRum.addAction(event, extra);
}
identifyUser(user: { id: string; email?: string; name?: string }) {
datadogRum.setUser({
id: user.id,
email: user.email,
name: user.name,
});
}
private async initialize() {
if (typeof window === 'undefined') {
this.readyResolver?.();
return;
}
datadogRum.init({
applicationId: process.env.NEXT_PUBLIC_DATADOG_APP_ID!,
clientToken: process.env.NEXT_PUBLIC_DATADOG_CLIENT_TOKEN!,
site: process.env.NEXT_PUBLIC_DATADOG_SITE ?? 'datadoghq.com',
service: process.env.NEXT_PUBLIC_DATADOG_SERVICE ?? 'my-saas',
env: process.env.NEXT_PUBLIC_DATADOG_ENV ?? 'production',
sessionSampleRate: 100,
sessionReplaySampleRate: 20,
trackUserInteractions: true,
trackResources: true,
trackLongTasks: true,
});
this.readyResolver?.();
}
}
```
## Common Gotchas
1. **Browser-only initialization** - Check `typeof window !== 'undefined'` before accessing browser APIs.
2. **Ready state** - The `ready()` method must resolve after initialization completes. Server contexts call `await service.ready()` before capturing.
3. **Provider enum** - Remember to add your provider to the `MONITORING_PROVIDERS` array in `get-monitoring-provider.ts`.
4. **Lazy loading** - Providers are loaded lazily through the registry. Don't import the monitoring service directly in your main bundle.
5. **Server vs client** - Some providers (like LogRocket) are browser-only. Return a no-op or console fallback for server contexts.
This monitoring system is part of the [Next.js Supabase SaaS Kit](/next-supabase-turbo).
---
**Previous:** [Sentry Configuration ←](./sentry)

View File

@@ -0,0 +1,39 @@
---
status: "published"
title: "Configuring Honeybadger Monitoring in Your Next.js Supabase SaaS Kit"
label: "Honeybadger"
order: 5
description: "Set up Honeybadger as your error monitoring provider in Makerkit, combining analytics and error tracking in one platform."
---
[Honeybadger](https://honeybadger.io/) is a platform for error monitoring and uptime tracking with zero-config alerts.
## Installing the Honeybadger Plugin
Honeybadger is distributed as a Makerkit plugin. Install it using the CLI:
```bash
npx @makerkit/cli@latest plugins add honeybadger
```
The Makerkit CLI will automatically wire up the plugin in your project, so you don't have to do anything manually.
Please review the changes with `git diff`.
## Environment Variables
Set the monitoring provider and Honeybadger configuration:
```bash title="apps/web/.env.local"
# Enable Honeybadger as the monitoring provider
NEXT_PUBLIC_MONITORING_PROVIDER=honeybadger
# Honeybadger configuration
NEXT_PUBLIC_HONEYBADGER_API_KEY=your_api_key_here
```
Please add these environment variables to your hosting provider when you deploy your application to production.
## Scope of this plugin
This plugin is only responsible for capturing errors and exceptions in the browser and server. Uploading sourcemaps is not supported yet. Please use the [Honeybadger guide](https://docs.honeybadger.io/lib/javascript/integration/nextjs/) for more information on all the available options.

View File

@@ -0,0 +1,347 @@
---
title: "Monitoring and Error Tracking in Makerkit"
status: "published"
label: "How Monitoring Works"
order: 0
description: "Set up error tracking and performance monitoring in your Next.js Supabase SaaS app with Sentry, PostHog, or SigNoz."
---
{% sequence title="Steps to configure monitoring" description="Learn how to configure monitoring in the Next.js Supabase Starter Kit." %}
[Understanding the monitoring architecture](#understanding-the-monitoring-architecture)
[Supported monitoring providers](#supported-monitoring-providers)
[Configuring your monitoring provider](#configuring-your-monitoring-provider)
[What gets monitored automatically](#what-gets-monitored-automatically)
[Manually capturing exceptions](#manually-capturing-exceptions)
[Identifying users in error reports](#identifying-users-in-error-reports)
{% /sequence %}
## Understanding the Monitoring Architecture
Makerkit's monitoring system uses a **provider-based architecture** that lets you swap monitoring services without changing your application code. The system lives in the `@kit/monitoring` package and handles:
- **Error tracking**: Capture client-side and server-side exceptions
- **Performance monitoring**: Track server response times via OpenTelemetry instrumentation
- **User identification**: Associate errors with specific users for debugging
The architecture follows a registry pattern. When you set `NEXT_PUBLIC_MONITORING_PROVIDER`, Makerkit loads the appropriate service implementation at runtime:
```
MonitoringProvider (React context)
Registry lookup
┌───────┴───────┐
│ sentry │
│ posthog │
│ signoz │
└───────────────┘
```
This means your components interact with a consistent `MonitoringService` interface regardless of which provider you choose.
## Supported Monitoring Providers
Makerkit provides first-class support for these monitoring providers:
| Provider | Error Tracking | Performance | Self-Hostable | Notes |
|----------|---------------|-------------|---------------|-------|
| [Sentry](/docs/next-supabase-turbo/monitoring/sentry) | Yes | Yes | Yes | Built-in, recommended for most apps |
| [PostHog](/docs/next-supabase-turbo/monitoring/posthog) | Yes | No | Yes | Plugin, doubles as analytics |
| [SigNoz](/docs/next-supabase-turbo/monitoring/signoz) | Yes | Yes | Yes | Plugin, OpenTelemetry-native |
**Sentry** is included out of the box. PostHog and SigNoz require installing plugins via the Makerkit CLI.
{% alert type="default" title="Custom providers" %}
You can add support for any monitoring service by implementing the `MonitoringService` interface and registering it in the provider registry. See [Adding a custom monitoring provider](#adding-a-custom-monitoring-provider) below.
{% /alert %}
## Configuring Your Monitoring Provider
Set these environment variables to enable monitoring:
```bash title=".env.local"
# Required: Choose your provider (sentry, posthog, or signoz)
NEXT_PUBLIC_MONITORING_PROVIDER=sentry
# Provider-specific configuration
# See the individual provider docs for required variables
```
The `NEXT_PUBLIC_MONITORING_PROVIDER` variable determines which service handles your errors. Leave it empty to disable monitoring entirely (errors still log to console in development).
## What Gets Monitored Automatically
Once configured, Makerkit captures errors without additional code:
### Client-side exceptions
The `MonitoringProvider` component wraps your app and captures uncaught exceptions in React components. This includes:
- Runtime errors in components
- Unhandled promise rejections
- Errors thrown during rendering
### Server-side exceptions
Next.js 15+ includes an instrumentation hook that captures server errors automatically. Makerkit hooks into this via `instrumentation.ts`:
```typescript title="apps/web/instrumentation.ts"
import { type Instrumentation } from 'next';
export const onRequestError: Instrumentation.onRequestError = async (
err,
request,
context,
) => {
const { getServerMonitoringService } = await import('@kit/monitoring/server');
const service = await getServerMonitoringService();
await service.ready();
await service.captureException(
err as Error,
{},
{
path: request.path,
headers: request.headers,
method: request.method,
routePath: context.routePath,
},
);
};
```
This captures errors from Server Components, Server Actions, Route Handlers, and Middleware.
## Manually Capturing Exceptions
For expected errors (like validation failures or API errors), capture them explicitly:
### In Server Actions or Route Handlers
```typescript
import { getServerMonitoringService } from '@kit/monitoring/server';
export async function createProject(data: FormData) {
try {
// ... your logic
} catch (error) {
const monitoring = await getServerMonitoringService();
await monitoring.ready();
monitoring.captureException(error, {
action: 'createProject',
userId: user.id,
});
throw error; // Re-throw or handle as needed
}
}
```
### In React Components
Use the `useMonitoring` hook for client-side error capture:
```tsx
'use client';
import { useMonitoring } from '@kit/monitoring/hooks';
export function DataLoader() {
const monitoring = useMonitoring();
async function loadData() {
try {
const response = await fetch('/api/data');
if (!response.ok) {
throw new Error(`Failed to load data: ${response.status}`);
}
return response.json();
} catch (error) {
monitoring.captureException(error, {
component: 'DataLoader',
});
throw error;
}
}
// ...
}
```
### The `useCaptureException` Hook
For error boundaries or components that receive errors as props:
```tsx
'use client';
import { useCaptureException } from '@kit/monitoring/hooks';
export function ErrorDisplay({ error }: { error: Error }) {
// Automatically captures the error when the component mounts
useCaptureException(error);
return (
<div>
<h2>Something went wrong</h2>
<p>{error.message}</p>
</div>
);
}
```
## Identifying Users in Error Reports
Associate errors with users to debug issues faster. Makerkit's monitoring providers support user identification:
```typescript
const monitoring = useMonitoring();
// After user signs in
monitoring.identifyUser({
id: user.id,
email: user.email,
// Additional fields depend on your provider
});
```
Makerkit automatically identifies users when they sign in if you've configured the analytics/events system. The `user.signedIn` event triggers user identification in both analytics and monitoring.
## Adding a Custom Monitoring Provider
To add a provider not included in Makerkit:
### 1. Implement the MonitoringService interface
```typescript title="packages/monitoring/my-provider/src/my-provider.service.ts"
import { MonitoringService } from '@kit/monitoring-core';
export class MyProviderMonitoringService implements MonitoringService {
private readyPromise: Promise<void>;
private readyResolver?: () => void;
constructor() {
this.readyPromise = new Promise((resolve) => {
this.readyResolver = resolve;
});
this.initialize();
}
async ready() {
return this.readyPromise;
}
captureException(error: Error, extra?: Record<string, unknown>) {
// Send to your monitoring service
myProviderSDK.captureException(error, { extra });
}
captureEvent(event: string, extra?: Record<string, unknown>) {
myProviderSDK.captureEvent(event, extra);
}
identifyUser(user: { id: string }) {
myProviderSDK.setUser(user);
}
private initialize() {
// Initialize your SDK
myProviderSDK.init({ dsn: process.env.MY_PROVIDER_DSN });
this.readyResolver?.();
}
}
```
### 2. Register the provider
Add your provider to the monitoring registries:
```typescript title="packages/monitoring/api/src/get-monitoring-provider.ts"
const MONITORING_PROVIDERS = [
'sentry',
'my-provider', // Add your provider
'',
] as const;
```
```typescript title="packages/monitoring/api/src/services/get-server-monitoring-service.ts"
serverMonitoringRegistry.register('my-provider', async () => {
const { MyProviderMonitoringService } = await import('@kit/my-provider');
return new MyProviderMonitoringService();
});
```
```typescript title="packages/monitoring/api/src/components/provider.tsx"
monitoringProviderRegistry.register('my-provider', async () => {
const { MyProviderProvider } = await import('@kit/my-provider/provider');
return {
default: function MyProviderWrapper({ children }: React.PropsWithChildren) {
return <MyProviderProvider>{children}</MyProviderProvider>;
},
};
});
```
{% alert type="default" title="Telegram notifications" %}
We wrote a tutorial showing how to add Telegram notifications for error monitoring: [Send SaaS errors to Telegram](/blog/tutorials/telegram-saas-error-monitoring).
{% /alert %}
## Best Practices
### Do capture context with errors
```typescript
// Good: Includes debugging context
monitoring.captureException(error, {
userId: user.id,
accountId: account.id,
action: 'updateBillingPlan',
planId: newPlanId,
});
// Less useful: No context
monitoring.captureException(error);
```
### Don't capture expected validation errors
```typescript
// Avoid: This clutters your error dashboard
if (!isValidEmail(email)) {
monitoring.captureException(new Error('Invalid email'));
return { error: 'Invalid email' };
}
// Better: Only capture unexpected failures
try {
await sendEmail(email);
} catch (error) {
monitoring.captureException(error, {
extra: { email: maskEmail(email) },
});
}
```
## Next Steps
Choose a monitoring provider and follow its setup guide:
- [Configure Sentry](/docs/next-supabase-turbo/monitoring/sentry) (recommended for most apps)
- [Configure PostHog](/docs/next-supabase-turbo/monitoring/posthog) (if you already use PostHog for analytics)
- [Configure SigNoz](/docs/next-supabase-turbo/monitoring/signoz) (self-hosted, OpenTelemetry-native)

View File

@@ -0,0 +1,146 @@
---
status: "published"
title: "Configuring PostHog Monitoring in Your Next.js Supabase SaaS Kit"
label: "PostHog"
order: 3
description: "Set up PostHog as your error monitoring provider in Makerkit, combining analytics and error tracking in one platform."
---
{% sequence title="Steps to configure PostHog monitoring" description="Learn how to configure PostHog for error monitoring in your Next.js Supabase SaaS kit." %}
[Installing the PostHog plugin](#installing-the-posthog-plugin)
[Registering the monitoring service](#registering-the-monitoring-service)
[Environment variables](#environment-variables)
[How PostHog monitoring works](#how-posthog-monitoring-works)
{% /sequence %}
[PostHog](https://posthog.com) combines product analytics, session replay, feature flags, and error tracking in one platform. If you're already using PostHog for analytics, adding it as your monitoring provider lets you correlate errors with user behavior without switching between tools.
{% alert type="default" title="Already using PostHog for analytics?" %}
If you've set up PostHog using the [PostHog Analytics guide](../analytics/posthog-analytics-provider), you already have the plugin installed. Skip to [Registering the monitoring service](#registering-the-monitoring-service).
{% /alert %}
## Installing the PostHog Plugin
PostHog is distributed as a Makerkit plugin. Install it using the CLI:
```bash
npx @makerkit/cli@latest plugins add posthog
```
The Makerkit CLI will automatically wire up the plugin in your project, so you don't have to do anything manually. Please review the changes with `git diff`.
## Environment Variables
Set the monitoring provider and PostHog configuration:
```bash title=".env.local"
# Enable PostHog as the monitoring provider
NEXT_PUBLIC_MONITORING_PROVIDER=posthog
# PostHog configuration (same as analytics setup)
NEXT_PUBLIC_POSTHOG_KEY=phc_your_key_here
NEXT_PUBLIC_POSTHOG_HOST=https://eu.posthog.com
```
If you haven't configured PostHog yet, see the [PostHog Analytics guide](/docs/next-supabase-turbo/analytics/posthog-analytics-provider) for details on:
- Finding your API key
- Choosing your region (EU vs US)
- Setting up ingestion rewrites to bypass ad blockers
## How PostHog Monitoring Works
When PostHog is your monitoring provider, errors are captured and sent to PostHog's error tracking system:
### Exception capture
PostHog captures:
- Client-side React errors
- Unhandled promise rejections
- Server-side exceptions via the Next.js instrumentation hook
Errors appear in PostHog under **Error Tracking** in the sidebar.
### Session correlation
The main benefit of using PostHog for monitoring is that errors are automatically linked to session replays. When you view an error in PostHog, you can:
1. See the exact session where the error occurred
2. Watch the user's actions leading up to the error
3. Correlate errors with feature flag states
4. See the user's full journey through your app
This is particularly useful for debugging errors that only happen in specific user flows or under certain conditions.
### User identification
When a user signs in, Makerkit identifies them in PostHog. This links errors to specific users, so you can:
- See all errors for a specific user
- Contact users affected by critical bugs
- Filter errors by user properties (plan, account type, etc.)
## Limitations
PostHog's error tracking is newer than dedicated tools like Sentry. Consider these limitations:
| Feature | PostHog | Sentry |
|---------|---------|--------|
| Error tracking | Yes | Yes |
| Stack trace deobfuscation | Limited | Full source map support |
| Performance monitoring | Via analytics | Full APM |
| Release tracking | No | Yes |
| Issue assignment | No | Yes |
| Slack/PagerDuty integration | Limited | Full |
**When to choose PostHog for monitoring:**
- You're already using PostHog for analytics
- You want errors correlated with session replays
- Your error volume is moderate
- You prefer fewer tools to manage
**When to choose Sentry instead:**
- You need detailed stack traces with source maps
- You have high error volume
- You need advanced alerting and issue management
- You want dedicated performance monitoring
## Using Both PostHog and Sentry
You can use PostHog for analytics and Sentry for monitoring. Set `NEXT_PUBLIC_MONITORING_PROVIDER=sentry` while keeping PostHog configured for analytics. This gives you:
- PostHog: Analytics, session replay, feature flags
- Sentry: Error tracking, performance monitoring, source maps
## Verification
After setup:
1. Trigger a test error in your application
2. Open PostHog and navigate to **Error Tracking**
3. Verify the error appears with:
- Error message and stack trace
- User information (if logged in)
- Link to session replay
## Troubleshooting
### Errors not appearing in PostHog
1. **Check the provider setting**: Verify `NEXT_PUBLIC_MONITORING_PROVIDER=posthog`
2. **Check PostHog initialization**: Open browser DevTools and look for PostHog network requests
3. **Verify the registrations**: Ensure all three files are updated with PostHog registrations
4. **Check ad blockers**: If using PostHog directly (no ingestion rewrites), ad blockers may block requests
## Next Steps
- [Learn about PostHog's error tracking features](https://posthog.com/docs/error-tracking)
- [Configure session replay settings](https://posthog.com/docs/session-replay)
- [Return to monitoring overview](/docs/next-supabase-turbo/monitoring/overview)

302
docs/monitoring/sentry.mdoc Normal file
View File

@@ -0,0 +1,302 @@
---
status: "published"
title: "Configuring Sentry in Your Next.js Supabase SaaS Kit"
label: "Sentry"
order: 3
description: "Set up Sentry for error tracking, performance monitoring, and session replay in your Makerkit application."
---
{% sequence title="Steps to configure Sentry" description="Learn how to configure Sentry in your Next.js Supabase SaaS kit." %}
[Installing the Sentry SDK](#installing-the-sentry-sdk)
[Environment variables](#environment-variables)
[Configuring source maps](#configuring-source-maps)
[Customizing the Sentry configuration](#customizing-the-sentry-configuration)
[Sentry features in Makerkit](#sentry-features-in-makerkit)
{% /sequence %}
[Sentry](https://sentry.io) is the recommended monitoring provider for Makerkit applications. It provides error tracking, performance monitoring, and session replay out of the box. Sentry is included in Makerkit's core packages, so no plugin installation is required.
## Installing the Sentry SDK
Install the `@sentry/nextjs` package in your web application:
```bash
pnpm add @sentry/nextjs --filter web
```
This package provides the Next.js-specific integrations for Sentry, including automatic instrumentation for Server Components, Server Actions, and Route Handlers.
## Environment Variables
Add these variables to your `.env.local` file:
```bash title=".env.local"
# Required: Enable Sentry as your monitoring provider
NEXT_PUBLIC_MONITORING_PROVIDER=sentry
# Required: Your Sentry DSN (found in Sentry project settings)
NEXT_PUBLIC_SENTRY_DSN=https://abc123@o123456.ingest.sentry.io/123456
# Optional: Set the environment (defaults to VERCEL_ENV if not set)
NEXT_PUBLIC_SENTRY_ENVIRONMENT=production
```
You can find your DSN in the Sentry dashboard under **Project Settings > Client Keys (DSN)**.
## Configuring Source Maps
Source maps let Sentry show you the original source code in error stack traces instead of minified production code. This is essential for debugging production errors.
### 1. Update your Next.js configuration
Wrap your Next.js configuration with Sentry's build plugin:
```typescript title="next.config.mjs"
import { withSentryConfig } from '@sentry/nextjs';
const nextConfig = {
// Your existing Next.js config
};
export default withSentryConfig(nextConfig, {
// Sentry organization slug
org: 'your-sentry-org',
// Sentry project name
project: 'your-sentry-project',
// Auth token for uploading source maps (set in CI)
authToken: process.env.SENTRY_AUTH_TOKEN,
// Suppress logs in non-production builds
silent: process.env.NODE_ENV !== 'production',
// Upload source maps from all packages in the monorepo
widenClientFileUpload: true,
// Disable automatic server function instrumentation
// (Makerkit handles this via the monitoring package)
autoInstrumentServerFunctions: false,
});
```
### 2. Create a Sentry auth token
Generate an auth token in your Sentry account:
1. Go to **Settings > Auth Tokens** in Sentry
2. Click **Create New Token**
3. Select the `project:releases` and `org:read` scopes
4. Copy the token
### 3. Add the token to your CI environment
Add the `SENTRY_AUTH_TOKEN` to your deployment platform's environment variables:
```bash
# Vercel, Railway, Render, etc.
SENTRY_AUTH_TOKEN=sntrys_eyJ...
```
{% alert type="warning" title="Don't commit this token" %}
The `SENTRY_AUTH_TOKEN` should only exist in your CI/CD environment, not in your `.env.local` or committed to git. It has write access to your Sentry project.
{% /alert %}
## Customizing the Sentry Configuration
Makerkit initializes Sentry with sensible defaults. You can customize these by modifying the configuration in the Sentry package.
### Client-side configuration
Edit `packages/monitoring/sentry/src/sentry.client.config.ts`:
```typescript title="packages/monitoring/sentry/src/sentry.client.config.ts"
import { init } from '@sentry/nextjs';
export function initializeSentryBrowserClient(
props: Parameters<typeof init>[0] = {},
) {
return init({
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
// Sample 100% of transactions for performance monitoring
// Reduce this in high-traffic applications
tracesSampleRate: props?.tracesSampleRate ?? 1.0,
// Capture 10% of sessions for replay
replaysSessionSampleRate: 0.1,
// Capture 100% of sessions with errors for replay
replaysOnErrorSampleRate: 1.0,
// Add custom integrations
integrations: [
// Example: Add breadcrumbs for console logs
// Sentry.breadcrumbsIntegration({ console: true }),
],
...props,
});
}
```
### Server-side configuration
Edit `packages/monitoring/sentry/src/sentry.server.config.ts`:
```typescript title="packages/monitoring/sentry/src/sentry.server.config.ts"
import { init } from '@sentry/nextjs';
export function initializeSentryServerClient(
props: Parameters<typeof init>[0] = {},
) {
return init({
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
// Sample rate for server-side transactions
tracesSampleRate: props?.tracesSampleRate ?? 1.0,
...props,
});
}
```
### Adjusting sample rates for production
For high-traffic applications, sampling 100% of transactions can be expensive. Adjust the sample rates based on your traffic:
| Monthly requests | Recommended `tracesSampleRate` |
|-----------------|-------------------------------|
| < 100k | 1.0 (100%) |
| 100k - 1M | 0.1 - 0.5 (10-50%) |
| > 1M | 0.01 - 0.1 (1-10%) |
Error capture is not affected by sampling. All errors are captured regardless of the `tracesSampleRate` setting.
## Sentry Features in Makerkit
### Error tracking
All uncaught exceptions are automatically captured:
- **Client-side**: React errors, unhandled promise rejections
- **Server-side**: Server Component errors, Server Action errors, Route Handler errors, Middleware errors
Each error includes:
- Full stack trace (with source maps)
- Request context (URL, method, headers)
- User information (if identified)
- Environment and release information
### Performance monitoring
When `tracesSampleRate` is greater than 0, Sentry tracks:
- Page load times
- API route response times
- Server Component render times
- Database query durations (if using Sentry's database integrations)
### Session replay
Sentry can record user sessions and replay them when errors occur. This helps you see exactly what the user did before encountering an error.
Session replay is enabled by default with these settings:
- 10% of normal sessions are recorded
- 100% of sessions with errors are recorded
To disable replay, set both sample rates to 0 in your client config.
### User identification
Makerkit automatically identifies users when they sign in through the events system. You can also manually identify users:
```typescript
import { useMonitoring } from '@kit/monitoring/hooks';
function UserProfile({ user }) {
const monitoring = useMonitoring();
useEffect(() => {
monitoring.identifyUser({
id: user.id,
email: user.email,
username: user.name,
});
}, [user]);
// ...
}
```
## Testing Your Setup
After configuration, verify Sentry is working:
### 1. Trigger a test error
Add a temporary button to trigger an error:
```tsx
'use client';
export function TestSentry() {
return (
<button
onClick={() => {
throw new Error('Test Sentry error');
}}
>
Test Sentry
</button>
);
}
```
### 2. Check the Sentry dashboard
The error should appear in your Sentry project within a few seconds. Verify:
- The stack trace shows your original source code (not minified)
- The environment is correct
- User information is attached (if logged in)
### 3. Remove the test code
Delete the test button after verifying the setup.
## Troubleshooting
### Errors not appearing in Sentry
1. **Check the DSN**: Verify `NEXT_PUBLIC_SENTRY_DSN` is set correctly
2. **Check the provider**: Verify `NEXT_PUBLIC_MONITORING_PROVIDER=sentry`
3. **Check the console**: Look for Sentry initialization errors
4. **Check ad blockers**: Some ad blockers block Sentry's ingestion endpoint
### Source maps not working
1. **Verify the auth token**: Check `SENTRY_AUTH_TOKEN` is set in your CI environment
2. **Check the build logs**: Look for "Uploading source maps" in your build output
3. **Verify the release**: Make sure the release version matches between your build and Sentry
### High Sentry costs
1. **Reduce `tracesSampleRate`**: Lower the performance monitoring sample rate
2. **Reduce `replaysSessionSampleRate`**: Only capture error sessions
3. **Filter events**: Use Sentry's `beforeSend` hook to drop low-value errors
## Next Steps
- [View the Sentry Next.js documentation](https://docs.sentry.io/platforms/javascript/guides/nextjs/)
- [Set up Sentry alerts](https://docs.sentry.io/product/alerts/)
- [Configure Slack notifications](https://docs.sentry.io/product/integrations/notification-incidents/slack/)
- [Return to monitoring overview](/docs/next-supabase-turbo/monitoring/overview)

270
docs/monitoring/signoz.mdoc Normal file
View File

@@ -0,0 +1,270 @@
---
status: "published"
title: "Configuring SigNoz in Your Next.js Supabase SaaS Kit"
label: "SigNoz"
order: 6
description: "Set up SigNoz for OpenTelemetry-native observability with self-hosted error tracking, traces, logs, and metrics."
---
{% sequence title="Steps to configure SigNoz" description="Learn how to configure SigNoz in your Next.js Supabase SaaS kit." %}
[Installing the SigNoz plugin](#installing-the-signoz-plugin)
[Registering the monitoring services](#registering-the-monitoring-services)
[Environment variables](#environment-variables)
[Running SigNoz locally](#running-signoz-locally)
[Configuring logging with Winston](#configuring-logging-with-winston)
{% /sequence %}
[SigNoz](https://signoz.io) is an open-source, self-hostable observability platform built on OpenTelemetry. It provides traces, metrics, logs, and error tracking in one interface. Choose SigNoz if you want full control over your observability data and prefer OpenTelemetry standards.
## Installing the SigNoz Plugin
SigNoz is distributed as a Makerkit plugin. Install it using the CLI:
```bash
npx @makerkit/cli@latest plugins add signoz
```
This creates the plugin at `packages/plugins/signoz`.
Our codemod will wire up the plugin in your project, so you don't have to do anything manually. Please review the changes with `git diff`.
## Environment Variables
SigNoz requires several OpenTelemetry environment variables:
```bash title=".env.local"
# Enable SigNoz as the monitoring provider
NEXT_PUBLIC_MONITORING_PROVIDER=signoz
# Service identification
NEXT_PUBLIC_OTEL_SERVICE_NAME=makerkit
OTEL_RESOURCE_ATTRIBUTES="service.name=makerkit,service.version=1.0.0"
# Client-side SigNoz configuration
NEXT_PUBLIC_SIGNOZ_INGESTION_KEY=your_ingestion_key
NEXT_PUBLIC_SIGNOZ_INGESTION_URL=http://localhost:4318/v1/logs
# Server-side OpenTelemetry configuration
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://localhost:4318/v1/traces
OTEL_EXPORTER_OTLP_TRACES_PROTOCOL=http/protobuf
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=http://localhost:4318/v1/logs
OTEL_EXPORTER_OTLP_HEADERS="signoz-ingestion-key=your_ingestion_key"
```
### Variable reference
| Variable | Description |
|----------|-------------|
| `NEXT_PUBLIC_OTEL_SERVICE_NAME` | Name shown in SigNoz dashboards |
| `OTEL_RESOURCE_ATTRIBUTES` | Service metadata (name, version) |
| `NEXT_PUBLIC_SIGNOZ_INGESTION_KEY` | Your SigNoz ingestion API key |
| `NEXT_PUBLIC_SIGNOZ_INGESTION_URL` | Logs ingestion endpoint |
| `OTEL_EXPORTER_OTLP_ENDPOINT` | Base OTLP endpoint |
| `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` | Traces ingestion endpoint |
| `OTEL_EXPORTER_OTLP_LOGS_ENDPOINT` | Logs ingestion endpoint |
## Running SigNoz Locally
For development, run SigNoz in Docker:
```bash
# Clone SigNoz
git clone -b main https://github.com/SigNoz/signoz.git && cd signoz/deploy/
# Start SigNoz
docker compose -f docker/clickhouse-setup/docker-compose.yaml up -d
```
SigNoz will be available at `http://localhost:3301`.
For detailed installation options, see the [SigNoz Docker installation guide](https://signoz.io/docs/install/docker/).
### Local environment variables
When running SigNoz locally, use these endpoints:
```bash title=".env.local"
NEXT_PUBLIC_SIGNOZ_INGESTION_URL=http://localhost:4318/v1/logs
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://localhost:4318/v1/traces
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=http://localhost:4318/v1/logs
```
For local development, you can leave `OTEL_EXPORTER_OTLP_HEADERS` empty since no authentication is required.
## Production Configuration
When deploying SigNoz to production (self-hosted or SigNoz Cloud):
### Self-hosted
Update the endpoints to point to your SigNoz instance:
```bash title=".env.production"
NEXT_PUBLIC_SIGNOZ_INGESTION_URL=https://signoz.yourdomain.com/v1/logs
OTEL_EXPORTER_OTLP_ENDPOINT=https://signoz.yourdomain.com
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=https://signoz.yourdomain.com/v1/traces
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=https://signoz.yourdomain.com/v1/logs
```
### SigNoz Cloud
If using SigNoz Cloud, update the endpoints and add your ingestion key:
```bash title=".env.production"
NEXT_PUBLIC_SIGNOZ_INGESTION_KEY=your_cloud_ingestion_key
NEXT_PUBLIC_SIGNOZ_INGESTION_URL=https://ingest.{region}.signoz.cloud/v1/logs
OTEL_EXPORTER_OTLP_ENDPOINT=https://ingest.{region}.signoz.cloud
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=https://ingest.{region}.signoz.cloud/v1/traces
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=https://ingest.{region}.signoz.cloud/v1/logs
OTEL_EXPORTER_OTLP_HEADERS="signoz-ingestion-key=your_cloud_ingestion_key"
```
Replace `{region}` with your SigNoz Cloud region (e.g., `us`, `eu`).
## Configuring Logging with Winston
{% alert type="warning" title="Pino logging limitation" %}
Due to compatibility issues between Next.js and the OpenTelemetry transport for Pino (Makerkit's default logger), logs cannot be sent to SigNoz using Pino. Switch to Winston for log ingestion.
{% /alert %}
### 1. Switch to Winston
Set the logger environment variable:
```bash title=".env.local"
LOGGER=winston
```
### 2. Register the Winston logger
```typescript title="packages/shared/src/logger/index.ts"
// Register the Winston logger implementation
loggerRegistry.register('winston', async () => {
const { Logger: WinstonLogger } = await import('./impl/winston');
return WinstonLogger;
});
```
### 3. Configure Winston with OpenTelemetry
Follow the [SigNoz Winston integration guide](https://signoz.io/docs/logs-management/send-logs/nodejs-winston-logs/) to configure the OpenTelemetry transport for Winston.
Example Winston configuration:
```typescript title="packages/shared/src/logger/impl/winston.ts"
import winston from 'winston';
const { combine, timestamp, json } = winston.format;
export const Logger = winston.createLogger({
level: process.env.LOG_LEVEL || 'info',
format: combine(
timestamp(),
json()
),
transports: [
new winston.transports.Console(),
// Add OpenTelemetry transport for SigNoz
],
});
```
## What SigNoz Captures
### Traces
SigNoz captures distributed traces across your application:
- HTTP request traces
- Database query traces
- External API call traces
- Server Component render times
View traces in SigNoz under **Traces** to see the full request lifecycle.
### Metrics
OpenTelemetry metrics are automatically collected:
- Request duration histograms
- Error rates
- Request counts by endpoint
### Logs
When configured with Winston, logs flow to SigNoz:
- Application logs at all levels (debug, info, warn, error)
- Correlated with traces via trace IDs
- Searchable and filterable
### Exceptions
Errors captured via the monitoring service appear in SigNoz with:
- Stack traces
- Request context
- Correlation with traces and logs
## SigNoz vs Sentry
| Feature | SigNoz | Sentry |
|---------|--------|--------|
| Self-hostable | Yes (primary use case) | Yes (limited) |
| OpenTelemetry native | Yes | No |
| Traces | Yes | Yes (via APM) |
| Logs | Yes | No |
| Metrics | Yes | No |
| Error tracking | Yes | Yes (primary focus) |
| Session replay | No | Yes |
| Source maps | Limited | Full support |
| Pricing | Free (self-hosted) | Usage-based |
**Choose SigNoz when:**
- You want to self-host your observability stack
- You prefer OpenTelemetry standards
- You need traces, logs, and metrics in one place
- You want predictable costs (self-hosted = infrastructure cost only)
**Choose Sentry when:**
- You want managed service with minimal setup
- You need advanced error tracking features
- You want session replay
- You prefer detailed source map integration
## Troubleshooting
### Traces not appearing
1. **Check OTLP endpoints**: Verify all `OTEL_EXPORTER_OTLP_*` variables are set
2. **Check connectivity**: Ensure your app can reach the SigNoz endpoints
3. **Check the SigNoz logs**: Look for ingestion errors in SigNoz container logs
### Logs not appearing
1. **Verify Winston is configured**: Check `LOGGER=winston` is set
2. **Check the OpenTelemetry transport**: Ensure the Winston transport is correctly configured
3. **Check log levels**: Verify your log level includes the logs you expect
### Authentication errors
1. **Check ingestion key**: Verify `signoz-ingestion-key` header is set correctly
2. **Check key format**: The header value should be just the key, not `Bearer key`
## Next Steps
- [SigNoz documentation](https://signoz.io/docs/)
- [OpenTelemetry SDK documentation](https://opentelemetry.io/docs/languages/js/)
- [SigNoz GitHub repository](https://github.com/SigNoz/signoz)
- [Return to monitoring overview](/docs/next-supabase-turbo/monitoring/overview)