Skip to content

Next.js (App Router) — full setup

Instrument Route Handlers and server-only API routes. Do not use the SDK in Client Components or with NEXT_PUBLIC_* keys.

Install

bash
npm install @amlexiahq/node openai

Environment

Server-only (.env.local — never NEXT_PUBLIC_):

env
AMLEXIA_SDK_KEY=am_...
AMLEXIA_INGEST_URL=https://ingest.amlexia.com
AMLEXIA_ENVIRONMENT=production
AMLEXIA_RELEASE=1.0.0
OPENAI_API_KEY=sk-...

Shared client (lib/amlexia.ts)

typescript
import { AmlexiaClient, wrapFetch } from '@amlexiahq/node';

const globalForAmlexia = globalThis as unknown as {
  amlexia?: AmlexiaClient;
};

export function getAmlexiaClient(): AmlexiaClient {
  if (!globalForAmlexia.amlexia) {
    globalForAmlexia.amlexia = new AmlexiaClient({
      sdkKey: process.env.AMLEXIA_SDK_KEY!,
      ingestUrl: process.env.AMLEXIA_INGEST_URL,
      environment: process.env.AMLEXIA_ENVIRONMENT,
      releaseVersion: process.env.AMLEXIA_RELEASE,
    });
    if (typeof fetch !== 'undefined') {
      globalThis.fetch = wrapFetch(globalForAmlexia.amlexia, fetch);
    }
  }
  return globalForAmlexia.amlexia;
}

Route Handler — health

typescript
// app/api/health/route.ts
import { getAmlexiaClient } from '@/lib/amlexia';
import { withAmlexia } from '@amlexiahq/node/next';

const client = getAmlexiaClient();

export const GET = withAmlexia(
  client,
  async () => Response.json({ ok: true }),
  { route: '/api/health', serviceName: 'nextjs' },
);

Route Handler — chat (OpenAI)

typescript
// app/api/chat/route.ts
import OpenAI from 'openai';
import { trackOpenAiCompletion } from '@amlexiahq/node';
import { withAmlexia } from '@amlexiahq/node/next';
import { getAmlexiaClient } from '@/lib/amlexia';

const client = getAmlexiaClient();
const openai = new OpenAI();

export const POST = withAmlexia(
  client,
  async (request) => {
    const body = await request.json();
    const start = Date.now();
    try {
      const completion = await openai.chat.completions.create({
        model: 'gpt-4o-mini',
        messages: body.messages,
      });
      trackOpenAiCompletion(client, {
        model: completion.model,
        statusCode: 200,
        latencyMs: Date.now() - start,
        usage: completion.usage,
      });
      return Response.json(completion);
    } catch (e) {
      const msg = e instanceof Error ? e.message : 'error';
      await client.track({
        endpoint: 'POST /api/chat',
        method: 'POST',
        statusCode: 500,
        latencyMs: Date.now() - start,
        provider: 'openai',
        errorMessage: msg,
      });
      return Response.json({ error: msg }, { status: 500 });
    }
  },
  { route: '/api/chat', serviceName: 'nextjs' },
);

Stripe webhook

typescript
// app/api/webhooks/stripe/route.ts
import { getAmlexiaClient } from '@/lib/amlexia';
import { withAmlexia } from '@amlexiahq/node/next';

const client = getAmlexiaClient();

export const POST = withAmlexia(
  client,
  async (request) => {
    const payload = await request.text();
    // verify Stripe signature here …
    return Response.json({ received: true });
  },
  { route: '/api/webhooks/stripe', serviceName: 'nextjs' },
);

// Optional explicit webhook flag on a manual track:
// client.track({ ..., isWebhook: true, provider: 'stripe' });

Instrumentation rules

DoDon't
Route Handlers under app/api/**Client Components
Static route in withAmlexia optionsDynamic paths with raw IDs in route string
AMLEXIA_SDK_KEY server env onlyNEXT_PUBLIC_AMLEXIA_SDK_KEY

Use explicit route: '/api/users/[id]' patterns so cardinality stays low.

Server Actions & cron

For Server Actions or scripts calling external APIs, use manual client.track() — middleware does not wrap Actions automatically.

Edge runtime

On Edge, use a small batch + flush() after each request, or see Bun, Deno & Edge.

Verify

bash
npx amlexia health
npm run dev
# Hit http://localhost:3000/api/health

Check Live on app.amlexia.com.