Follow the Journey
3 min read

The JavaScript SDK

Full-stack observability for Rails apps

Backend observability is solved. You've got logs, errors, traces.

But what about the frontend?

That JavaScript error your users see? You don't know about it. That slow page load? Invisible. That broken API call from the browser? Silent failure.

The JavaScript SDK closes the gap.

The frontend blind spot

Rails developers focus on the backend. Makes sense—that's where the business logic lives.

But users experience the frontend. And the frontend breaks in ways the backend never sees:

  • JavaScript errors that crash features
  • Network requests that timeout
  • Core Web Vitals that tank SEO
  • Console errors that indicate problems

You need visibility into both sides.

One script

<!-- In your layout -->
<script src="https://cdn.brainzlab.dev/sdk.js"></script>
<script>
  BrainzLab.init({ apiKey: 'sk_live_xxx' });
</script>

Or with importmaps (Rails 7+):

# config/importmap.rb
pin "brainzlab", to: "https://cdn.brainzlab.dev/sdk.js"
// application.js
import BrainzLab from "brainzlab"
BrainzLab.init({ apiKey: window.BRAINZLAB_API_KEY })

That's it. Observability starts flowing.

What gets captured

JavaScript errors:
- Unhandled exceptions
- Promise rejections
- Error boundaries (React)
- Full stack traces with source maps

Network requests:
- Fetch and XHR calls
- Request/response timing
- Status codes
- Failed requests

Core Web Vitals:
- LCP (Largest Contentful Paint)
- FID (First Input Delay)
- CLS (Cumulative Layout Shift)
- INP (Interaction to Next Paint)

Console output:
- console.error captures
- console.warn captures
- Optional: all console levels

Correlated with backend

This is the killer feature.

Frontend errors link to backend traces.

Click a JavaScript error in Reflex. See the API call it made. Jump to the backend trace in Pulse. See the database query that failed.

Full request lifecycle. Both sides of the wire.

Stimulus integration

Using Hotwire? The SDK integrates with Stimulus:

// controllers/application_controller.js
import { Controller } from "@hotwired/stimulus"
import BrainzLab from "brainzlab"

export default class extends Controller {
  connect() {
    BrainzLab.trackEvent("controller_connected", {
      controller: this.identifier
    })
  }
}

Track Stimulus controller lifecycle. Debug Turbo issues. Understand user interactions.

Custom tracking

// Track custom events
BrainzLab.track("button_clicked", {
  button: "checkout",
  cart_value: 99.00
})

// Track custom metrics
BrainzLab.gauge("active_websockets", connections.length)

// Set user context
BrainzLab.setUser({
  id: user.id,
  email: user.email,
  plan: user.plan
})

// Custom spans for timing
BrainzLab.span("render_chart", () => {
  chart.render(data)
})

Same API as the Ruby SDK. Consistent across languages.

Performance

The SDK is tiny and fast:

  • < 10KB gzipped — Won't slow your page load
  • Async loading — Non-blocking
  • Batched sending — Efficient network usage
  • Sampling — Control volume on high-traffic sites

Users don't notice it. You just get the data.

Privacy-conscious

The SDK respects user privacy:

  • No PII captured by default
  • Configurable data scrubbing
  • Respects Do Not Track
  • GDPR-friendly options

Observability without creepiness.

The full picture

Backend SDK captures server-side.
Frontend SDK captures client-side.
Everything correlates in Recall, Reflex, Pulse.

Ask Claude: "Why are users seeing errors on the checkout page?"

Claude queries both sides. Finds the JavaScript error. Traces it to the failed API call. Identifies the backend bug.

Full-stack debugging with AI assistance.

Try it

Add the script. Make some requests. Break something on purpose.

Watch it all appear in your dashboard.

This is full-stack observability.

— Andres

All posts Follow along

Want to follow the journey?

Get Updates