<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Rodrigo Rosenfeld Rosas — javascript</title><description>Articles tagged with javascript</description><link>https://rosenfeld.page/</link><language>en-us</language><item><title>Why proxying Bugsnag (or similar service) might be a good idea?</title><link>https://rosenfeld.page/articles/ruby-rails/2018_03_01_why_proxying_bugsnag_or_similar_service_might_be_a_good_idea/</link><guid isPermaLink="true">https://rosenfeld.page/articles/ruby-rails/2018_03_01_why_proxying_bugsnag_or_similar_service_might_be_a_good_idea/</guid><pubDate>Thu, 01 Mar 2018 19:45:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.bugsnag.com/&quot;&gt;Bugsnag&lt;/a&gt; is a great error monitoring service that takes care of
reporting and filtering/notifying exceptions in several kind of applications. I used to use
my own error reporting tool in the app I currently maintain but as I&amp;#39;m currently evaluating
creating a new application, I started to evaluate Bugsnag to save me some time. But I stumbled
upon an issue I didn&amp;#39;t have to deal with my custom error reporting tool.&lt;/p&gt;
&lt;p&gt;When reporting errors, it&amp;#39;s a good idea to attach as much meaningful data as they could be
quite helpful when trying to understand some errors, specially when they aren&amp;#39;t easily
reproducible. Such data include user information which I&amp;#39;d prefer not to expose to the front-end,
including the user id.&lt;/p&gt;
&lt;p&gt;I was initially worried about exposing the API key to the front-end, which someone could use to
report errors to my account, but then I figured out I was being too paranoid and that proxying
the request wouldn&amp;#39;t prevent users from reporting errors to my account, unless I&amp;#39;d implement
some sort of rate limit protection or disabling errors reporting for non authenticated users
(after all, I&amp;#39;d be able to track authenticated users acting that way and take some action
against them).&lt;/p&gt;
&lt;p&gt;However, hiding from the front-end user data meant to be used only internally is important to me.
That&amp;#39;s why I decided to take a few hours to proxy browsers errors through the back-end. Here&amp;#39;s
how it was implemented using the official &lt;em&gt;bugsnag-js&lt;/em&gt; npm package and the &lt;em&gt;bugsnag&lt;/em&gt; Ruby gem.&lt;/p&gt;
&lt;p&gt;In the JavaScript code, there&amp;#39;s something like showed below. I used XMLHttpRequest rather than
&lt;em&gt;fetch&lt;/em&gt; in order to support IE11 since the polyfills are lazy loaded as required in our application
and fetch may not be available when Bugsnag is initialized in the client:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;import bugsnag from &amp;#39;bugsnag-js&amp;#39;;
const bugsnagClient = bugsnag({
  apiKey: &amp;#39;000000000000000000000000&amp;#39;, // the actual api key will be inserted in the back-end
  beforeSend: report =&amp;gt; {
    const original = report.toJSON(), event = {};
    let v;
    for (let k in original) if ((v = original[k]) !== undefined) event[k] = v;
    report.ignore();

    const csrf = (document.querySelector(&amp;#39;meta[name=_csrf]&amp;#39;) || {}).content;
    const xhr = new XMLHttpRequest();
    xhr.open(&amp;#39;POST&amp;#39;, &amp;#39;/errors/bugsnag-js/notify?_csrf=&amp;#39; + csrf);
    xhr.setRequestHeader(&amp;#39;Content-type&amp;#39;, &amp;#39;application/json&amp;#39;);
    xhr.send(JSON.stringify(event));
  }
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The back-end is a Ruby application built on top of the &lt;a href=&quot;http://roda.jeremyevans.net/&quot;&gt;Roda toolkit&lt;/a&gt;.
It uses the &lt;a href=&quot;http://roda.jeremyevans.net/rdoc/classes/Roda/RodaPlugins/MultiRun.html&quot;&gt;multi_run&lt;/a&gt;
plugin, splitting the main applications into multiple apps (which can be seen as powerful
controllers if it helps understanding how it works). These are the relevant parts of the back-end:&lt;/p&gt;
&lt;p&gt;lib/setup_bugsnag.rb:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# frozen-string-literal: true

require &amp;#39;app_settings&amp;#39;
require_relative &amp;#39;../app_root&amp;#39;

if api_key = AppSettings.bugsnag_api_key
  require &amp;#39;bugsnag&amp;#39;

  Bugsnag.configure do |config|
    config.api_key = AppSettings.bugsnag_api_key
    config.project_root = APP_ROOT
    config.delivery_method = :synchronous
    config.logger = AppSettings.loggers
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;app/apps/errors_app.rb:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# frozen-string-literal: true

require &amp;#39;json&amp;#39;
require_relative &amp;#39;base_app&amp;#39;
require &amp;#39;bugsnag_setup&amp;#39;

module Apps
  class ErrorsApp &amp;lt; BaseApp
    private

    def process(r)
      super
      r.post(&amp;#39;bugsnag-js/notify&amp;#39;){ notify_bugsnag }
    end

    def notify_bugsnag
      api_key = settings.bugsnag_api_key
      head :ok unless api_key &amp;amp;&amp;amp; settings.store_front_end_errors
      event = JSON.parse request.body.read
      user_data = auth_session.to_h
      user_data[&amp;#39;id&amp;#39;] = user_data[&amp;#39;profile_id&amp;#39;]
      event[&amp;#39;user&amp;#39;] = user_data
      event[&amp;#39;apiKey&amp;#39;] = api_key
      event[&amp;#39;appVersion&amp;#39;] = settings.app_version
      payload = { apiKey: api_key, notifier: {
        name: &amp;#39;Bugsnag JavaScript&amp;#39;, version: &amp;#39;4.3.0&amp;#39;, url: &amp;#39;https://github.com/bugsnag/bugsnag-js&amp;#39;
      }, events: [event] }
      configuration = Bugsnag.configuration
      options = {
        headers: {
          &amp;#39;Bugsnag-Api-Key&amp;#39; =&amp;gt; api_key,
          &amp;#39;Bugsnag-Payload-Version&amp;#39; =&amp;gt; event[&amp;#39;payloadVersion&amp;#39;],
        }
      }
      Bugsnag::Delivery[configuration.delivery_method].
        deliver(configuration.endpoint, JSON.unparse(payload), configuration, options)

      &amp;#39;OK&amp;#39; # optional response body, could be empty as well, we don&amp;#39;t check the response
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That&amp;#39;s it, some extra code, but it allows me to send useful information to Bugsnag while not
requiring us to expose them to the front-end application. Hopefully next time I need something
like that it will help to have it written down here ;)&lt;/p&gt;
</content:encoded></item></channel></rss>