This design resolves the "Express-only" limitation.
import { Senzor } from '@senzops/apm-node';
Senzor.init({ apiKey: "sz_apm_..." });
app.use(Senzor.requestHandler());Next.js doesn't use middleware the same way. Users can wrap handlers or use a manual track.
// pages/api/user.ts
import { Senzor } from '@senzops/apm-node';
export default async function handler(req, res) {
const start = performance.now();
// ... logic ...
// Track manually at end
Senzor.track({
method: req.method,
route: '/api/user',
path: req.url,
status: res.statusCode,
duration: performance.now() - start
});
// Critical for Vercel/Serverless: Wait for flush
await Senzor.flush();
}// server/middleware/senzor.ts
import { Senzor } from '@senzops/apm-node';
Senzor.init({ apiKey: "..." });
export default defineEventHandler(async (event) => {
const start = performance.now();
// Process request
await callHandler(event);
Senzor.track({
method: event.method,
route: event.path, // Nitro might need regex to normalize
path: event.path,
status: event.node.res.statusCode,
duration: performance.now() - start
});
});Run this inside the apm-node directory:
npm install
npm run build
This will produce a lightweight `dist/` folder ready for publishing to NPM.1. Express (Standard)
app.use(Senzor.requestHandler());2. Next.js App Router (app/api/route.ts)
import { Senzor } from '@senzops/apm-node';
export const GET = Senzor.wrapNextRoute(async (request) => {
return Response.json({ success: true });
});3. Nuxt / Nitro (server/api/test.ts)
import { Senzor } from '@senzops/apm-node';
export default Senzor.wrapH3(defineEventHandler((event) => {
return { hello: 'world' }
}));4. Fastify
import { Senzor } from '@senzops/apm-node';
fastify.register(Senzor.fastifyPlugin, { apiKey: '...' });This approach provides native robustness for each framework. It captures errors (500s), 404s (Route not found), and correct timing without the user manually writing track() calls.
Now your users can do this in their Express/Next.js apps:
app.get('/users', async (req, res) => {
// 1. Start a span
const dbSpan = Senzor.startSpan('fetch_users_db', 'db');
const users = await db.query('SELECT * FROM users');
// 2. End span (Duration calculated automatically)
dbSpan.end({ query: 'SELECT * FROM users' });
// 3. Start another span
const apiSpan = Senzor.startSpan('stripe_api', 'http');
await axios.get('...');
apiSpan.end({}, 200);
res.json(users);
});This will automatically appear in your Waterfall Chart on the dashboard, nested under the main request Trace.