Skip to content
LogZuno

Node.js event tracking

Send events to LogZuno from Node.js, with code that is tested against the API.

This example sends one event to LogZuno's ingestion API from Node.js. It is the exact file our snippet tests send to the API before the docs are published.

Requirements

Node.js 18 or later, which has fetch built in. No packages to install.

Send an event

// Node.js 18 or later (built-in fetch), no dependencies.
const endpoint = process.env.LOGZUNO_ENDPOINT ?? "https://api.logzuno.app/v1";

const response = await fetch(`${endpoint}/log`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.LOGZUNO_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    channel: "signups",
    event: "User signed up",
    user_id: "user-123",
    icon: "🎉",
    notify: true,
    tags: { plan: "growth", source: "website" },
  }),
});

console.log(response.status, await response.json());

Save it as logzuno.mjs, export LOGZUNO_API_KEY, and run node logzuno.mjs. LogZuno answers 202 and the event appears in your project's live feed within seconds. Keep the key in an environment variable or your secrets manager, not in source control.

Notes for Node.js

Batch and retry. For more than a few events a minute, queue them and send up to 100 per request to /v1/batch, waiting for Retry-After on a 429:

// Node.js 18+: send up to 100 events per request, and retry when LogZuno
// answers 429 (rate limit) after the number of seconds in Retry-After.
const endpoint = process.env.LOGZUNO_ENDPOINT ?? "https://api.logzuno.app/v1";

async function sendBatch(events, attempt = 1) {
  const response = await fetch(`${endpoint}/batch`, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.LOGZUNO_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ events }),
  });

  if (response.status === 429 && attempt < 5) {
    const seconds = Number(response.headers.get("Retry-After") ?? 60);
    await new Promise((resolve) => setTimeout(resolve, seconds * 1000));
    return sendBatch(events, attempt + 1);
  }

  return { status: response.status, body: await response.json() };
}

const events = [
  { channel: "product", event: "Report exported", user_id: "user-123", tags: { format: "csv" } },
  { channel: "product", event: "Report shared", user_id: "user-123", tags: { with: "team" } },
];

console.log(await sendBatch(events));

Don't block the request. Send events after you respond to your user, or from a queue worker, so a slow network never slows your app. Never let an analytics failure fail the user's request.

What the request contains

The body is the same in every language: a channel and an event, and optionally user_id, group_id, description, icon, notify and tags. All fields, limits and response codes are in sending events and the ingestion API reference.