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