Next.js Integration

Set up Apperio with Next.js App Router and Pages Router.

App Router Setup

With Next.js App Router, the SDK should be initialized client-side since it relies on browser APIs. Create a provider component marked with "use client":

Bash
npm install apperio
app/providers/ApperioProvider.tsx
class="syntax-string">"use client";

import { useEffect, useRef } from class="syntax-string">"react";
import Apperio from class="syntax-string">"apperio";

export function ApperioProvider({ children }: { children: React.ReactNode }) {
  const initialized = useRef(false);

  useEffect(() => {
    if (!initialized.current) {
      Apperio.init({
        projectId: process.env.NEXT_PUBLIC_APPERIO_PROJECT_ID!,
        apiKey: process.env.NEXT_PUBLIC_APPERIO_API_KEY!,
        environment: process.env.NODE_ENV,
        autoCapture: {
          errors: true,
          performance: true,
          network: true,
          pageviews: true,
        },
      });
      initialized.current = true;
    }

    return () => {
      Apperio.flush();
    };
  }, []);

  return <>{children}</>;
}

Add the provider to your root layout:

app/layout.tsx
import { ApperioProvider } from class="syntax-string">"./providers/ApperioProvider";

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang=class="syntax-string">"en">
      <body>
        <ApperioProvider>
          {children}
        </ApperioProvider>
      </body>
    </html>
  );
}

Info

The SDK uses browser APIs like window, PerformanceObserver, and XMLHttpRequest. It must be initialized in a client component, not a server component.

Client-Side Initialization

For pages that need logging before the provider mounts, you can also initialize in a client component directly:

app/dashboard/ApperioInit.tsx
class="syntax-string">"use client";

import { useEffect, useRef } from class="syntax-string">"react";
import Apperio from class="syntax-string">"apperio";

export function ApperioInit() {
  const initialized = useRef(false);

  useEffect(() => {
    if (!initialized.current) {
      Apperio.init({
        projectId: process.env.NEXT_PUBLIC_APPERIO_PROJECT_ID!,
        apiKey: process.env.NEXT_PUBLIC_APPERIO_API_KEY!,
      });
      initialized.current = true;
    }
  }, []);

  return null; class="syntax-comment">// Renders nothing
}

class="syntax-comment">// Usage in a server component layout:
class="syntax-comment">// <ApperioInit />
class="syntax-comment">// <DashboardContent />

Server-Side Logging

For server-side logging in Server Components, Server Actions, or Route Handlers, you can send logs directly to the Apperio API:

lib/server-logger.ts
const API_BASE = process.env.APPERIO_API_URL
  || class="syntax-string">"https:class="syntax-comment">//apperioserver.onrender.com/api/v1";
const PROJECT_ID = process.env.APPERIO_PROJECT_ID;
const API_KEY = process.env.APPERIO_API_KEY;

interface ServerLogOptions {
  level: class="syntax-string">"trace" | class="syntax-string">"debug" | class="syntax-string">"info" | class="syntax-string">"warn" | class="syntax-string">"error" | class="syntax-string">"fatal";
  message: string;
  data?: Record<string, any>;
  service?: string;
}

export async function serverLog(options: ServerLogOptions) {
  try {
    await fetch(API_BASE + class="syntax-string">"/" + PROJECT_ID + class="syntax-string">"/logs", {
      method: class="syntax-string">"POST",
      headers: {
        class="syntax-string">"Content-Type": class="syntax-string">"application/json",
        class="syntax-string">"X-API-Key": API_KEY!,
      },
      body: JSON.stringify({
        timestamp: new Date().toISOString(),
        level: options.level,
        message: options.message,
        data: options.data,
        service: options.service || class="syntax-string">"nextjs-server",
        environment: process.env.NODE_ENV,
      }),
    });
  } catch (err) {
    console.error(class="syntax-string">"Failed to send server log:", err);
  }
}

class="syntax-comment">// Usage in a Server Component or Server Action:
class="syntax-comment">// await serverLog({
class="syntax-comment">//   level: class="syntax-string">"info",
class="syntax-comment">//   message: class="syntax-string">"Page rendered",
class="syntax-comment">//   data: { page: class="syntax-string">"/dashboard" },
class="syntax-comment">// });

Warning

Server-side environment variables (without NEXT_PUBLIC_ prefix) are only available in server-side code. Use separate env vars for server and client.

Middleware Integration

Log requests passing through Next.js middleware for observability into routing decisions:

middleware.ts
import { NextResponse } from class="syntax-string">"next/server";
import type { NextRequest } from class="syntax-string">"next/server";

export async function middleware(request: NextRequest) {
  const start = Date.now();
  const response = NextResponse.next();

  class="syntax-comment">// Log to Apperio(fire-and-forget)
  const logData = {
    timestamp: new Date().toISOString(),
    level: class="syntax-string">"info",
    message: class="syntax-string">"Middleware: " + request.method + class="syntax-string">" " + request.nextUrl.pathname,
    service: class="syntax-string">"nextjs-middleware",
    data: {
      method: request.method,
      path: request.nextUrl.pathname,
      userAgent: request.headers.get(class="syntax-string">"user-agent"),
      duration: Date.now() - start,
    },
  };

  class="syntax-comment">// Non-blocking log send
  fetch(process.env.APPERIO_API_URL + class="syntax-string">"/" + process.env.APPERIO_PROJECT_ID + class="syntax-string">"/logs", {
    method: class="syntax-string">"POST",
    headers: {
      class="syntax-string">"Content-Type": class="syntax-string">"application/json",
      class="syntax-string">"X-API-Key": process.env.APPERIO_API_KEY!,
    },
    body: JSON.stringify(logData),
  }).catch(() => {
    class="syntax-comment">// Silently fail - don't block the request
  });

  return response;
}

Error Handling

Use Next.js error files with Apperio for comprehensive error reporting:

Global Error Handler

app/error.tsx
class="syntax-string">"use client";

import { useEffect } from class="syntax-string">"react";
import Apperio from class="syntax-string">"apperio";

export default function Error({
  error,
  reset,
}: {
  error: Error & { digest?: string };
  reset: () => void;
}) {
  useEffect(() => {
    Apperio.error(class="syntax-string">"Next.js page error", {
      error: {
        name: error.name,
        message: error.message,
        stack: error.stack,
        digest: error.digest,
      },
    });
  }, [error]);

  return (
    <div>
      <h2>Something went wrong</h2>
      <button onClick={reset}>Try again</button>
    </div>
  );
}

Not Found Handler

app/not-found.tsx
import Apperio from class="syntax-string">"apperio";

export default function NotFound() {
  class="syntax-comment">// Note: This is a server component, so use serverLog
  class="syntax-comment">// or handle client-side
  return (
    <div>
      <h2>Page Not Found</h2>
      <NotFoundLogger />
    </div>
  );
}

class="syntax-comment">// Client component for logging
class="syntax-string">"use client";
function NotFoundLogger() {
  useEffect(() => {
    Apperio.warn(class="syntax-string">"class="syntax-number">404 - Page not found", {
      url: window.location.href,
      referrer: document.referrer,
    });
  }, []);
  return null;
}

Route Handler Logging

Log API route handler activity for server-side observability:

app/api/users/route.ts
import { NextResponse } from class="syntax-string">"next/server";
import { serverLog } from class="syntax-string">"@/lib/server-logger";

export async function GET(request: Request) {
  const start = Date.now();

  try {
    const users = await fetchUsers();

    await serverLog({
      level: class="syntax-string">"info",
      message: class="syntax-string">"GET /api/users",
      data: {
        status: class="syntax-number">200,
        count: users.length,
        duration: Date.now() - start,
      },
    });

    return NextResponse.json({ users });
  } catch (err) {
    await serverLog({
      level: class="syntax-string">"error",
      message: class="syntax-string">"GET /api/users failed",
      data: {
        error: err instanceof Error ? err.message : class="syntax-string">"Unknown error",
        duration: Date.now() - start,
      },
    });

    return NextResponse.json(
      { error: class="syntax-string">"Internal server error" },
      { status: class="syntax-number">500 }
    );
  }
}

Environment Variables

Configure your .env.local with both client-side and server-side variables:

.env.local
# Client-side(exposed to browser)
NEXT_PUBLIC_APPERIO_PROJECT_ID=your_project_id
NEXT_PUBLIC_APPERIO_API_KEY=your_api_key

# Server-side only(not exposed to browser)
APPERIO_PROJECT_ID=your_project_id
APPERIO_API_KEY=your_api_key
APPERIO_API_URL=https://apperioserver.onrender.com/api/v1
VariableScopeUsed By
NEXT_PUBLIC_APPERIO_PROJECT_IDClientSDK initialization in browser
NEXT_PUBLIC_APPERIO_API_KEYClientSDK API authentication
APPERIO_PROJECT_IDServerServer-side logging, middleware
APPERIO_API_KEYServerServer-side API authentication
APPERIO_API_URLServerAPI base URL for server logging

Pages Router (Legacy)

If you are using the Pages Router, initialize Apperio in _app.tsx:

pages/_app.tsx
import { useEffect, useRef } from class="syntax-string">"react";
import Apperio from class="syntax-string">"apperio";
import type { AppProps } from class="syntax-string">"next/app";

export default function App({ Component, pageProps }: AppProps) {
  const initialized = useRef(false);

  useEffect(() => {
    if (!initialized.current) {
      Apperio.init({
        projectId: process.env.NEXT_PUBLIC_APPERIO_PROJECT_ID!,
        apiKey: process.env.NEXT_PUBLIC_APPERIO_API_KEY!,
        environment: process.env.NODE_ENV,
      });
      initialized.current = true;
    }
  }, []);

  return <Component {...pageProps} />;
}

Tip

For new projects, use the App Router setup. The Pages Router integration is provided for compatibility with existing projects that have not yet migrated.