Skip to content

Getting started — Node.js

Goal: 5 minutes from bun add @goliapkg/sentori-javascript to a Node-side error showing up in the Sentori dashboard. Covers Express, Hono, Fastify, plain scripts, and bun.

Terminal window
bun add @goliapkg/sentori-javascript
# or
pnpm add @goliapkg/sentori-javascript

The same package serves browser and Node — the Node entry point attaches process.on('uncaughtException') and process.on('unhandledRejection') automatically.

Call initSentori once, as early as possible — before any other import that might throw on load. The cleanest spot is a dedicated sentori.ts imported first from your entry:

sentori.ts
import { initSentori } from '@goliapkg/sentori-javascript'
initSentori({
token: process.env.SENTORI_TOKEN!,
release: process.env.SENTORI_RELEASE!,
environment: process.env.NODE_ENV === 'production' ? 'prod' : 'dev',
ingestUrl: process.env.SENTORI_INGEST_URL!,
})
// index.ts — your real entry
import './sentori.js' // ← must come first
import { startServer } from './server.js'
startServer()

.env:

Terminal window
SENTORI_TOKEN=st_pk_...
SENTORI_RELEASE=myapi@1.0.0
SENTORI_INGEST_URL=https://ingest.sentori.golia.jp
NODE_ENV=production

The global hooks catch anything you let escape:

// somewhere in your handler
function buggy() {
throw new TypeError('hello sentori from node')
}
setTimeout(buggy, 0) // unhandled — captured automatically

Or imperatively from inside a try/catch:

import { captureException, captureException } from '@goliapkg/sentori-javascript'
try {
await chargeCard()
} catch (err) {
captureException(err as Error, {
tags: { feature: 'billing', region: process.env.AWS_REGION ?? 'unknown' },
})
throw err // re-throw so your normal error handling still runs
}
import express from 'express'
import { captureException } from '@goliapkg/sentori-javascript'
const app = express()
app.use((err: Error, _req, _res, next) => {
captureException(err, { tags: { framework: 'express' } })
next(err)
})
import { captureException } from '@goliapkg/sentori-javascript'
import { Hono } from 'hono'
const app = new Hono()
app.onError((err, c) => {
captureException(err, {
tags: { 'hono.path': c.req.path, framework: 'hono' },
})
return c.text('Internal error', 500)
})
import { captureException } from '@goliapkg/sentori-javascript'
import Fastify from 'fastify'
const fastify = Fastify()
fastify.setErrorHandler((err, req, reply) => {
captureException(err, {
tags: { 'fastify.route': req.routerPath, framework: 'fastify' },
})
reply.status(500).send({ error: 'Internal error' })
})

The bun runtime also exposes process.on('uncaughtException') so the JS SDK’s hooks just work. No extra wiring.

Open your dashboard. The new issue appears within a few seconds.

If you don’t see it:

  • Confirm initSentori ran with the right token (the SDK logs [sentori] warnings on bad config).
  • Watch your app’s stdout for [sentori] POST ... 401 — that’s a token mismatch.
  • A 429 means rate limit — default 1000 req/min/token.

Drop a breadcrumb whenever something interesting happens — it ships with the next captured event:

import { addBreadcrumb } from '@goliapkg/sentori-javascript'
addBreadcrumb({ type: 'log', data: { msg: 'cache miss', key: 'user:42' } })
addBreadcrumb({ type: 'net', data: { url: 'https://api.x.com/v2', status: 503 } })

Buffer is bounded (drops oldest first when full).

  • Self-hosting — production deploy, SMTP, backups
  • Protocol — wire format reference if you’re writing your own SDK