<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Daniel Choudhury</title>
    <description>The latest articles on DEV Community by Daniel Choudhury (@dac09).</description>
    <link>https://dev.to/dac09</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F465773%2F2227e8e1-0d04-4f60-956a-8be392fdab4e.jpg</url>
      <title>DEV Community: Daniel Choudhury</title>
      <link>https://dev.to/dac09</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dac09"/>
    <language>en</language>
    <item>
      <title>Exception tracking 🚫 with Bugsnag and Redwood</title>
      <dc:creator>Daniel Choudhury</dc:creator>
      <pubDate>Fri, 11 Sep 2020 14:35:40 +0000</pubDate>
      <link>https://dev.to/dac09/exception-tracking-with-bugsnag-and-redwood-4kk0</link>
      <guid>https://dev.to/dac09/exception-tracking-with-bugsnag-and-redwood-4kk0</guid>
      <description>&lt;p&gt;So you’ve built your amazing redwood app, tested it thoroughly and are ready to go live. You, with your hipster beard and oat milk flat whites ☕️, you’re an awesome dev, so there’s no bugs in your code...... or are there?&lt;/p&gt;

&lt;p&gt;Of course there are hotshot 🛑! It might be an edge case you hadn’t considered, maybe it’s something in your infrastructure, or that third party service that isn’t super reliable or maybe  just that users will do what you least expect and they hit an unhandled error!&lt;/p&gt;

&lt;p&gt;For a production app, and for your app to delight users, you need to be able to diagnose issues quickly, and push up a fix as soon as you can. You need good logs to be able to see what the issue is, and you want your tooling to minimise this friction. In enterprise circles this is measured using  mean time to recovery - MTTR - in simple terms, you want to fix and ship as quickly as possible.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;Tldr;&lt;/li&gt;
&lt;li&gt;
Part 1: Frontend

&lt;ul&gt;
&lt;li&gt;Setting up exception handler&lt;/li&gt;
&lt;li&gt;Webpack setup &amp;amp; uploading source maps&lt;/li&gt;
&lt;li&gt;Validate your setup&lt;/li&gt;
&lt;li&gt;Dev &amp;amp; Prod configuration&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
Part 2: API and Graphql

&lt;ul&gt;
&lt;li&gt;Create custom plugin for graphql&lt;/li&gt;
&lt;li&gt;Custom functions&lt;/li&gt;
&lt;li&gt;Prod configuration&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;This tutorial will walk through setting up Bugsnag, which is an exception tracking tool (primarily used by mobile devs) but also supports web and Node.js, with Redwood - both on the frontend and the api side. &lt;/p&gt;

&lt;p&gt;Since we first launched &lt;a href="https://tape.sh?utm_source=bugsnag-redwood-article"&gt;&lt;strong&gt;Tape.sh&lt;/strong&gt;&lt;/a&gt;, a screen recording tool for mobile devs, we needed this visibility to fix very hard to reproduce issues. It even helped us contribute back to Redwood!&lt;/p&gt;

&lt;p&gt;We really like Bugsnag, but you can follow the exact same process, and use a tool of your own choice - I don’t imagine it being too different.&lt;/p&gt;

&lt;h1&gt;
  
  
  Tldr;
&lt;/h1&gt;

&lt;p&gt;For the frontend, wrap the Redwood app with the bugsnag exception handler component and make sure you upload sourcemaps (with the webpack plugin or otherwise).&lt;/p&gt;

&lt;p&gt;For the backend, create a custom apollo server plugin, and pass through exceptions to bugsnag. Make sure you have a method available to report errors to use in your custom functions too.&lt;/p&gt;

&lt;h1&gt;
  
  
  Part 1: Frontend
&lt;/h1&gt;

&lt;p&gt;First sign in to your bugsnag dashboard, and create your project.&lt;/p&gt;

&lt;p&gt;New Project &amp;gt; Browser &amp;gt; React &lt;/p&gt;

&lt;p&gt;Grab your API key, and we'll use it in a little bit.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting up exception handler
&lt;/h3&gt;

&lt;p&gt;Let's add the Bugsnag library and react plugin&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# -W because we'll use it in both web and api&lt;/span&gt;
yarn add &lt;span class="nt"&gt;-W&lt;/span&gt; @bugsnag/js 
yarn workspace web add @bugsnag/plugin-react
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now we have to wrap our entire frontend react app, with the exception handler.  In &lt;em&gt;web/src/index.js&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gi"&gt;+ import Bugsnag from '@bugsnag/js'
+ import BugsnagPluginReact from '@bugsnag/plugin-react'
&lt;/span&gt;
+ Bugsnag.start({
&lt;span class="gi"&gt;+  apiKey: process.env.BUGSNAG_NOTIFIER_API_KEY,
+  plugins: [new BugsnagPluginReact()],
+  releaseStage: process.env.CONTEXT || process.env.NODE_ENV,
+  appVersion: process.env.DEPLOY_ID,
+ })
&lt;/span&gt;
+ const BugsnagBoundary = Bugsnag.getPlugin('react').createErrorBoundary(React)

ReactDOM.render(
&lt;span class="gd"&gt;- &amp;lt;FatalErrorBoundary page={FatalErrorPage}&amp;gt;
&lt;/span&gt;&lt;span class="gi"&gt;+ &amp;lt;BugsnagBoundary
+    FallbackComponent={&amp;lt;FatalErrorBoundary page={FatalErrorPage} /&amp;gt;}
+  &amp;gt;
&lt;/span&gt;      &amp;lt;RedwoodProvider&amp;gt;
        &amp;lt;Routes /&amp;gt;
      &amp;lt;/RedwoodProvider&amp;gt;
&lt;span class="gi"&gt;+ &amp;lt;/BugsnagBoundar&amp;gt;
&lt;/span&gt;&lt;span class="gd"&gt;- &amp;lt;/FatalErrorBoundary&amp;gt;,
&lt;/span&gt;  document.getElementById('redwood-app')
&lt;span class="err"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The fallback component I'm using is the default Redwood FatalErrorBoundary, but you can use your own here.&lt;/p&gt;

&lt;p&gt;Notice how we're using &lt;code&gt;process.env&lt;/code&gt; variables. By default Redwood (rightly!) doesn't expose env variables to the frontend. So lets modify &lt;strong&gt;redwood.toml&lt;/strong&gt; to include these variables&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;[web]&lt;/span&gt;
  port = 8910
  apiProxyPath = "/.netlify/functions"
&lt;span class="gi"&gt;+ includeEnvironmentVariables = ['BUGSNAG_NOTIFIER_API_KEY', 'CONTEXT', 'NODE_ENV', 'DEPLOY_ID']
&lt;/span&gt;&lt;span class="err"&gt;[api]&lt;/span&gt;
  port = 8911
&lt;span class="err"&gt;[browser]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;REMEMBER!&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;And finally, remember to add &lt;code&gt;BUGSNAG_NOTIFIER_API_KEY&lt;/code&gt; to your .env file&lt;/p&gt;

&lt;p&gt;Done ✨! Now you’ll get notified when your user’s hit an exception. But the logs you’ll see won’t be that helpful yet, because your javascript is minified. So far we know &lt;em&gt;what&lt;/em&gt; is happening, now let’s setup the &lt;em&gt;why&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Webpack setup &amp;amp; uploading source maps
&lt;/h3&gt;

&lt;p&gt;We're going to use the Bugsnag's webpack plugins to set this up. Let's install them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn workspace web add webpack-bugsnag-plugins
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;To customise your webpack config for Redwood, you need to create a file at &lt;code&gt;web/config/webpack.config.js&lt;/code&gt;. If you already have it, great just add to it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* ==== web/config/webpack.config.js ==== */&lt;/span&gt;

&lt;span class="c1"&gt;// Important, so webpack can use the environment variables&lt;/span&gt;
&lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dotenv-defaults&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;BugsnagSourceMapUploaderPlugin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;BugsnagBuildReporterPlugin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;webpack-bugsnag-plugins&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Check if its building in netlify&lt;/span&gt;
    &lt;span class="c1"&gt;// No need to upload source maps when building locally&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;netlifyBuild&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;!!&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NETLIFY&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bugsnagPlugins&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;netlifyBuild&lt;/span&gt;
    &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;BugsnagBuildReporterPlugin&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;BUGSNAG_NOTIFIER_API_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;appVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DEPLOY_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;releaseStage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CONTEXT&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NODE_ENV&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;sourceControl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;github&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;REPOSITORY_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;revision&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;COMMIT_REF&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;}),&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;BugsnagSourceMapUploaderPlugin&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;BUGSNAG_NOTIFIER_API_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;appVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DEPLOY_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}),&lt;/span&gt;
      &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

  &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;plugins&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;bugsnagPlugins&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Notice that we're using the &lt;code&gt;process.env.NETLIFY&lt;/code&gt; environment variable. This is so that we don't upload source maps for local builds. The environment variables &lt;code&gt;REPOSITORY_URL&lt;/code&gt;, &lt;code&gt;COMMIT_REF&lt;/code&gt;, &lt;code&gt;DEPLOY_ID&lt;/code&gt; and &lt;code&gt;CONTEXT&lt;/code&gt; come from Netlify, so modify according to where you're going to deploy your code. &lt;/p&gt;

&lt;h3&gt;
  
  
  Validate your setup
&lt;/h3&gt;

&lt;p&gt;So let’s validate our setup now. Just add&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Catch me bugsnag!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;anywhere in your frontend code, and when it get’s triggered you should see it come through on your dashboard (and email). You’ll be able to see &lt;em&gt;what&lt;/em&gt; happened, &lt;em&gt;why&lt;/em&gt; it happened and also, &lt;em&gt;how&lt;/em&gt; it happened through the Breadcrumbs tab.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--e7hbCC-G--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/http://tapes.tape.sh/static/bugsnag-Screentape.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--e7hbCC-G--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/http://tapes.tape.sh/static/bugsnag-Screentape.gif" alt="Bugsnag UI preview"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A note on sourcemaps with Netlify. If you have "optimise JS bundles" enabled on Netlify, they move your bundles to the Cloudfront CDN, and Bugsnag isn't able to match up the sourcemap to the js file.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you switch off the setting in the Netlify UI, you'll be able to see the stacktrace fully. I haven't looked into making it work with Cloudfront yet, because unfortunately Netlify doesn't expose what they rename the files to and what the hostname is going to be, before uploading.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dev &amp;amp; Prod configuration
&lt;/h3&gt;

&lt;p&gt;Great, now that you're seeing errors come through, you want to disable it for dev. So let's create &lt;code&gt;&amp;lt;EnvironmentAwareErrorBoundary&amp;gt;&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;EnvironmentAwareErrorBoundary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;memo&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;otherProps&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NODE_ENV&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;development&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;FatalErrorBoundary&lt;/span&gt; &lt;span class="na"&gt;page&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;FatalErrorBoundary&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;otherProps&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;FatalErrorBoundary&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Bugsnag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;BUGSNAG_NOTIFIER_API_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;BugsnagPluginReact&lt;/span&gt;&lt;span class="p"&gt;()],&lt;/span&gt;
      &lt;span class="na"&gt;releaseStage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CONTEXT&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NODE_ENV&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;appVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DEPLOY_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;BugsnagBoundary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Bugsnag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getPlugin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;createErrorBoundary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;React&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;BugsnagBoundary&lt;/span&gt;
        &lt;span class="na"&gt;FallbackComponent&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;FatalErrorBoundary&lt;/span&gt; &lt;span class="na"&gt;page&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;FatalErrorPage&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;otherProps&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;BugsnagBoundary&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;What this does is use the default Redwood FatalErrorBoundary on development, but reports the exception to Bugsnag in production.&lt;/p&gt;

&lt;p&gt;You can then wrap your app in this component like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="p"&gt;ReactDOM.render(
&lt;/span&gt;&lt;span class="gi"&gt;+  &amp;lt;EnvironmentAwareErrorBoundary&amp;gt;
&lt;/span&gt;            {*/ your other stuff */}
      &amp;lt;RedwoodProvider&amp;gt;
        &amp;lt;Routes /&amp;gt;
      &amp;lt;/RedwoodProvider&amp;gt;
&lt;span class="gi"&gt;+  &amp;lt;/EnvironmentAwareErrorBoundary&amp;gt;,
&lt;/span&gt;  document.getElementById('redwood-app')
&lt;span class="err"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;






&lt;h1&gt;
  
  
  Part 2: API and Graphql
&lt;/h1&gt;

&lt;h3&gt;
  
  
  Create custom plugin for graphql
&lt;/h3&gt;

&lt;p&gt;For the backend, we want to capture errors from graphql. So let's start with create a util module to house the Bugsnag code. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;api/src/lib/bugsnag.js&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Bugsnag&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@bugsnag/js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;isEmpty&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lodash&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="nx"&gt;Bugsnag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;BUGSNAG_SERVER_API_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;releaseStage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CONTEXT&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NODE_ENV&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;appVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DEPLOY_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;reportErrorFromContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;requestContext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;metrics&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;requestContext&lt;/span&gt;

    &lt;span class="c1"&gt;// Call bugsnag here&lt;/span&gt;
  &lt;span class="c1"&gt;// But you could easily use something else here&lt;/span&gt;
  &lt;span class="nx"&gt;Bugsnag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;notify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;severity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addMetadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;metrics&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;metrics&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addMetadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;errors&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addMetadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;query&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;reportError&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;Bugsnag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;notify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We expose the &lt;code&gt;reportReportErrorFromContext&lt;/code&gt; to use in our custom apollo server plugin, but leave the &lt;code&gt;reportError&lt;/code&gt; for use elsewhere.&lt;/p&gt;

&lt;p&gt;Now let's create the plugin and add it to our server setup&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt; === api/src/functions/graphql.js ===

+ import { reportErrorFromContext } from 'src/lib/bugsnag'


&lt;span class="gi"&gt;+ const bugSnagExceptionPlugin = {
+   requestDidStart() {
+     return {
+       didEncounterErrors(requestContext) {
+         reportErrorFromContext(requestContext)
+       },
+     }
+   },
+ }
&lt;/span&gt;
export const handler = createGraphQLHandler({
  getCurrentUser,
&lt;span class="gi"&gt;+  plugins: [bugSnagExceptionPlugin],
&lt;/span&gt;  schema: makeMergedSchema({
    schemas,
    services: makeServices({ services }),

// ....rest of the file omitted for brevity
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Custom functions
&lt;/h3&gt;

&lt;p&gt;Remember how we created the &lt;code&gt;reportError&lt;/code&gt; method? You can now use this in your custom functions &lt;/p&gt;

&lt;h3&gt;
  
  
  Prod configuration
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Quick headsup here, Netlify’s functions annoyingly don’t set the NODE_ENV value at runtime by default. So if you wanted to check if you’re running on prod, you’ll have to make sure you set the value in the Netlify UI, but then in your netlify.toml set NODE_ENV to development for the build env (otherwise the build will fail). &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Same as the frontend, we want to disable logging exceptions during dev. So let's wrap the code in some if statements and we're done! In our case we're using the &lt;code&gt;process.env.LOG_EXCEPTIONS&lt;/code&gt; variable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;api/src/lib/bugsnag.js&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gi"&gt;+ if (!isEmpty(process.env.LOG_EXCEPTIONS)) {
&lt;/span&gt;  Bugsnag.start({
    apiKey: process.env.BUGSNAG_SERVER_API_KEY,
    releaseStage: process.env.CONTEXT || process.env.NODE_ENV,
    appVersion: process.env.DEPLOY_ID,
  })
&lt;span class="gi"&gt;+ }
&lt;/span&gt;
export const reportReportErrorFromContext = (requestContext) =&amp;gt; {
  const { errors, metrics, request, context } = requestContext

  // Note that netlify doesn't set node_env at runtime in functions
&lt;span class="gi"&gt;+  if (isEmpty(process.env.LOG_EXCEPTIONS)) {
+    return
+  }
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;






&lt;p&gt;That's all folks! 🎉 You're now ready to launch your app, with the confidence that you can find, trace and fix exceptions if they happen!&lt;/p&gt;

&lt;h4&gt;
  
  
  👋🏽 PS Here's what we're working on with Redwood:
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://tape.sh?utm_source=bottom-bugsnag-article"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0BeQvbDv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/0voxp8oo0bhtgv8iwln0.png" alt="Friction-free mobile &amp;amp; tvOS screen recording &amp;amp; sharing"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>serverless</category>
    </item>
  </channel>
</rss>
