Events
Node.js SDK
@metricneed/node sends product events and insights from your backend — fire and forget, with retries and batching built in. Zero dependencies, Node 18+.
Install
npm install @metricneed/node
Create a key in Settings → Events API and keep it in your server's environment (METRICNEED_API_KEY). Keys are write-only, but they are still secrets: never ship one in browser code.
Track an event
import { MetricNeed } from '@metricneed/node'
const mn = new MetricNeed({ apiKey: process.env.METRICNEED_API_KEY })
mn.track({
channel: 'signups',
event: 'New signup',
icon: 'user-plus',
userId: 'usr_42',
tags: { plan: 'pro', source: 'producthunt' }
})track() never throws and never blocks your request: the event is sent in the background and failures go to onError. Fields mirror the REST API in camelCase (userId, idempotencyKey); icon is typed against the accepted icon list.
Await the result
const res = await mn.trackAsync({ channel: 'billing', event: 'Upgrade', value: 24 })
// res.accepted, res.duplicates, res.usage → { used, limit, resetsAt }
await mn.trackMany([
{ channel: 'deploys', event: 'Production deploy', pin: true },
{ channel: 'support', event: 'Ticket resolved', value: 42 }
])trackAsync() and trackMany() resolve with the API's answer (including this month's usage) and throw a MetricNeedError on failure.
Batching
const mn = new MetricNeed({ apiKey, batch: true, flushInterval: 2000 })
mn.track({ channel: 'api', event: 'Request' }) // queued, sent every 2s
await mn.shutdown() // flush before a serverless function returns In batch mode events are queued and sent every flushInterval, up to 100 per request. A batch that still fails after the retries is reported to onError and dropped, so a long outage can't grow memory without bound.
Insights
mn.insight.set('Waitlist', 1204, { icon: 'users' })
await mn.insight.setAsync('Open tickets', 3)
mn.insight.increment('Signups today') // +1, atomic
mn.insight.increment('Open tickets', -1) // any delta Setting an insight again replaces its value; increment() adds to it atomically on the server (a new gauge starts at 0), so concurrent workers never lose an update. Numeric gauges keep a day-by-day history you can chart on a dashboard. Insights don't count against the monthly event quota.
Errors and retries
import { MetricNeed, MetricNeedError } from '@metricneed/node'
const mn = new MetricNeed({
apiKey,
onError: (err: MetricNeedError) => logger.warn('metricneed', err.code, err.message)
})
try {
await mn.trackAsync({ channel: 'signups', event: 'New signup' })
} catch (err) {
if (err instanceof MetricNeedError && err.code === 'event_quota') {
// monthly quota reached — err.body.data has used / limit / resetsAt
}
} Network errors, timeouts, 408, 429 and 5xx are retried — honouring Retry-After, then with exponential backoff. 400 (invalid payload), 401 (bad key) and 402 (quota reached) are not: retrying wouldn't help. The API allows 600 requests per minute per key.
Options
| Option | Default | Meaning |
|---|---|---|
apiKey | — | Required. The mne_… key from Settings → Events API. |
baseUrl | https://metricneed.com | For self-hosted or local instances. |
batch | false | Queue track() calls and send them in batches. |
flushInterval | 2000 | Batch interval in milliseconds. |
maxBatchSize | 100 | Events per request (max 100). A full queue is sent right away. |
maxRetries | 3 | Retries on network errors, timeouts, 408, 429 and 5xx. |
timeout | 10000 | Per-attempt timeout in milliseconds. |
onError | warn once per error kind | Receives every error from fire-and-forget calls. |
fetch | globalThis.fetch | Custom fetch implementation. |
flushOnExit | true | Batch mode: send what is queued when the process is about to exit. |
Frameworks
Next.js route handler:
// app/api/signup/route.ts
import { MetricNeed } from '@metricneed/node'
const mn = new MetricNeed({ apiKey: process.env.METRICNEED_API_KEY! })
export async function POST(req: Request) {
const user = await createUser(await req.json())
await mn.trackAsync({ channel: 'signups', event: 'New signup', userId: user.id })
return Response.json(user)
}Nuxt server route:
// server/api/signup.post.ts
import { MetricNeed } from '@metricneed/node'
const mn = new MetricNeed({ apiKey: useRuntimeConfig().metricneedApiKey })
export default defineEventHandler(async (event) => {
const user = await createUser(await readBody(event))
mn.track({ channel: 'signups', event: 'New signup', userId: user.id })
return user
}) On serverless platforms, await trackAsync() — or call shutdown() in batch mode — before returning, so the function isn't frozen with the event still queued.
Without the SDK
curl -X POST https://metricneed.com/api/v1/events \
-H "Authorization: Bearer mne_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{ "channel": "signups", "event": "New signup", "icon": "user-plus", "user_id": "usr_42", "tags": { "plan": "pro" } }'Any language that can send an HTTP request works — see the REST reference.