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 openaiEnvironment
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
| Do | Don't |
|---|---|
Route Handlers under app/api/** | Client Components |
Static route in withAmlexia options | Dynamic paths with raw IDs in route string |
AMLEXIA_SDK_KEY server env only | NEXT_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/healthCheck Live on app.amlexia.com.
