npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@nexly/node

v0.15.3

Published

Nexly server-side ingest SDK for Node.js: fire-and-forget events with optional linking to browser visitors

Downloads

760

Readme

@nexly/node

Server-side ingest SDK for Nexly. Fire-and-forget fetch transport, no batching, no state — for emitting backend events (webhooks, jobs, CLI tools, Lambda handlers) straight to the collector.

Install

npm install @nexly/node

Requires Node.js ≥ 20 (for the built-in fetch with keepalive).

Usage

import { Nexly } from '@nexly/node'

const nexly = Nexly.init({
  appId: process.env.NEXLY_APP_ID!,
  key: process.env.NEXLY_INGEST_KEY!,
})

// Anonymous backend event — no visitor binding.
nexly.event({
  name: 'subscription_created',
  type: 'custom',
  data: { plan: 'pro', amount_cents: 4900 },
})

Events are fire-and-forget: the call returns immediately, the fetch fires in the background, failures are swallowed so analytics never crash the host process.

Graceful shutdown

Long-running servers don't need anything special. For short-lived processes (CLI scripts, AWS Lambda, cron jobs) call flush() before exit so in-flight events aren't dropped:

process.on('SIGTERM', async () => {
  await nexly.flush()  // defaults to 2s timeout
  process.exit(0)
})

// or inside a Lambda handler:
export async function handler(event) {
  nexly.event({ name: 'order_processed', type: 'custom', data: { order_id: event.id } })
  await nexly.flush()
}

Origin allowlist

The SDK sends Origin: nexly-node://server on every request. Add this exact string once to the app's allowed origins in the dashboard, same way as you'd add a web origin.

Linking backend events to browser visitors

Important. This is the primary way to stitch server-side events (payments, subscription changes, background jobs) onto the same user timeline as their browser activity.

Backend events have no visitor_id by default — they stand alone unless you opt in. To connect an event to a specific browser session, your browser code forwards its Nexly visitor ID to your backend once (e.g. on sign-up, login, or when first authenticating), and you store the mapping alongside your own user id.

1. Browser: grab the visitor id

import { getVisitorId, getSessionId } from '@nexly/web'

await fetch('/api/link-visitor', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    visitorId: getVisitorId(),
    sessionId: getSessionId(), // optional — only if you need session-level linking
  }),
})

getVisitorId() is exported from @nexly/web and returns the persistent anonymous ID generated on first ingest for that browser. It's a plain localStorage string, safe to read from any component.

2. Backend: persist the mapping

// your users table (or equivalent):
// ALTER TABLE users ADD COLUMN nexly_visitor_id TEXT;

app.post('/api/link-visitor', async (req, res) => {
  const user = await getSessionUser(req)
  await db.users.update({ where: { id: user.id }, data: { nexly_visitor_id: req.body.visitorId } })
  res.status(204).end()
})

3. Backend: emit linked events

nexly.event({
  name: 'subscription_created',
  type: 'custom',
  data: { plan: 'pro' },
  context: { visitor_id: user.nexly_visitor_id },  // ← the link
})

In the dashboard these events now appear on the same timeline as that user's browser pageviews / clicks, filterable by visitor_id.

Without linking

If your project has no frontend or you just don't care about stitching, omit context entirely:

nexly.event({ name: 'invoice_paid', type: 'custom', data: { amount_cents: 12000 } })

The event lands with visitor_id='' and is perfectly usable for aggregate metrics — it just isn't associated with any browser user.

Verify locally

After wiring the SDK and whitelisting nexly-node://server, fire a test event and check it landed:

nexly.event({ name: 'smoke_test', type: 'custom', data: {}, context: { visitor_id: 'v_demo' } })
await nexly.flush()

Server-side (Postgres):

SELECT client, event_name, visitor_id, received_at
FROM events
WHERE app_id = 'app_…' AND client = 'node'
ORDER BY received_at DESC LIMIT 5;

-- To confirm linking worked, join with a browser event sharing the same visitor_id:
SELECT client, event_name, visitor_id FROM events
WHERE visitor_id = 'v_demo' ORDER BY received_at;

Common failures and what they mean:

| Response | Cause | | --- | --- | | 401 unauthorized | app_id + api_token pair not in the dashboard | | 403 forbidden | Token belongs to a different account | | 403 origin not allowed | Forgot to whitelist nexly-node://server | | 400 invalid client | Using a pre-0.13 backend that doesn't accept 'node' |

Custom props

Anything specific to your domain goes in data. Use context only for Nexly-known fields (visitor_id, session_id, path):

nexly.event({
  name: 'checkout_completed',
  type: 'custom',
  data: {
    order_id: 'ord_123',
    currency: 'USD',
    amount_cents: 4200,
    items: 3,
    internal_user_id: user.id,  // your own ids are fine here
  },
  context: { visitor_id: user.nexly_visitor_id },
})