<?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: Thibaut Tiberghien</title>
    <description>The latest articles on DEV Community by Thibaut Tiberghien (@tibotiber).</description>
    <link>https://dev.to/tibotiber</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%2F245818%2F2679288e-a26c-46e2-b1e8-752c9783dca4.jpeg</url>
      <title>DEV Community: Thibaut Tiberghien</title>
      <link>https://dev.to/tibotiber</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tibotiber"/>
    <language>en</language>
    <item>
      <title>Deploy private source maps with Netlify</title>
      <dc:creator>Thibaut Tiberghien</dc:creator>
      <pubDate>Fri, 11 Oct 2019 11:00:07 +0000</pubDate>
      <link>https://dev.to/tibotiber/deploy-private-source-maps-with-netlify-3ejp</link>
      <guid>https://dev.to/tibotiber/deploy-private-source-maps-with-netlify-3ejp</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally posted on &lt;a href="https://medium.com/@tibotiber/deploy-private-source-maps-with-netlify-72865adb2f8"&gt;Medium&lt;/a&gt; on Mar 10, 2019.&lt;/em&gt;&lt;br&gt;
&lt;em&gt;Photo by &lt;a href="https://unsplash.com/photos/SH98nuc1-Xc"&gt;Markus Spiske&lt;/a&gt; on Unsplash.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Source maps are great, they let you ship minified, optimised code to your users while enabling you to map things back to your original source code in error logs and while debugging. You will typically use a build system to generate your minifed code along with the source maps and deploy them together on your host. You can also use this build step to “uglify” your code in order to protect valuable intellectual property present in your frontend code. When that happens, you can no longer afford to deploy the source maps publicly. This post details how I deployed our source maps with team-only access.&lt;/p&gt;


&lt;h2&gt;
  
  
  Private hosting of source maps
&lt;/h2&gt;

&lt;p&gt;We chose to deploy our source maps on &lt;a href="https://cloud.google.com/storage/"&gt;Google Cloud Storage&lt;/a&gt;, in a bucket configured for access to authenticated users from our domain only. This is really convenient as we are using G Suite for our emails and can therefore leverage an authentication system that’s already in place. To do this, set permissions using &lt;a href="https://cloud.google.com/storage/docs/access-control/lists"&gt;Access Control Lists (ACLs)&lt;/a&gt; on your bucket.&lt;/p&gt;

&lt;p&gt;For example, head to &lt;em&gt;Permissions&lt;/em&gt; for your bucket, then &lt;em&gt;Add members&lt;/em&gt;, enter &lt;code&gt;yourdomain.com&lt;/code&gt; as the member and add the role &lt;em&gt;Storage Object Viewer&lt;/em&gt; to let anyone in your organization access source maps when their browser is logged in.&lt;/p&gt;


&lt;h2&gt;
  
  
  Pointing the source maps to your bucket
&lt;/h2&gt;

&lt;p&gt;Source maps are automatically fetched by the browser whenever needed, to do this the browser relies on an address indicated at the end of the minified file. It usually looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//# sourceMappingURL=index.e0d98431.js.map
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;By default, most build systems will co-locate source maps with the minified files and link them using their relative path. We will need to change the URL to the one of the bucket we created.&lt;/p&gt;

&lt;p&gt;Here, your mileage may vary depending on the tools you use, but the concept remain the same. We use &lt;a href="https://reactjs.org/"&gt;React&lt;/a&gt; for our frontend code, with &lt;a href="https://facebook.github.io/create-react-app/"&gt;create-react-app&lt;/a&gt; for an easy setup. We had to modify the &lt;a href="https://webpack.js.org/"&gt;Webpack&lt;/a&gt; configuration to use the &lt;a href="https://webpack.js.org/plugins/source-map-dev-tool-plugin/"&gt;SourceMapDevToolPlugin&lt;/a&gt; to set a custom URL. We use &lt;a href="https://github.com/timarney/react-app-rewired"&gt;react-app-rewired&lt;/a&gt; to modify the Webpack configuration without ejecting.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// config-overrides.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;webpack&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&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;setSourceMaps&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;env&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;devtool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&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;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="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="nx"&gt;push&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;webpack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SourceMapDevToolPlugin&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;append&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nx"&gt;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="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s1"&gt;//# sourceMappingURL=[url]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
          &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s1"&gt;//# sourceMappingURL=https://storage.cloud.google.com/&amp;lt;bucket-name&amp;gt;/[url]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[file].map&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="nx"&gt;config&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="na"&gt;webpack&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;env&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;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;setSourceMaps&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;env&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;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;From there, your build will inject the new URL at the end of the minified file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//# sourceMappingURL=https://storage.cloud.google.com/&amp;lt;bucket-name&amp;gt;/index.e0d98431.js.map
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;






&lt;h2&gt;
  
  
  Deploying the source maps with Netlify
&lt;/h2&gt;

&lt;p&gt;We use &lt;a href="https://www.netlify.com/"&gt;Netlify&lt;/a&gt; to deploy and host our frontend code behind their &lt;a href="https://www.netlify.com/features/adn/"&gt;Application Delivery Network (ADN)&lt;/a&gt;. We previously built the code and let Netlify handle the rest. Our build script was something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;“build”: “react-app-rewired build”
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now we call a shell script that handles the build and upload the source maps:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;“build”: “./build.sh”
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# build.sh&lt;/span&gt;

&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="c"&gt;# build&lt;/span&gt;
yarn react-app-rewired build
&lt;span class="c"&gt;# install gcloud cli&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Installing gcloud sdk"&lt;/span&gt;
curl &lt;span class="nt"&gt;-O&lt;/span&gt; https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-sdk-237.0.0-linux-x86_64.tar.gz
&lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-xzf&lt;/span&gt; google-cloud-sdk-237.0.0-linux-x86_64.tar.gz
./google-cloud-sdk/bin/gcloud &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;span class="c"&gt;# authenticate to gcloud&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Authenticating to gcloud"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$GCLOUD_KEY_FILE&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /tmp/gcloud_keyfile.json
./google-cloud-sdk/bin/gcloud auth activate-service-account &lt;span class="nt"&gt;--key-file&lt;/span&gt; /tmp/gcloud_keyfile.json
&lt;span class="c"&gt;# upload sourcemaps&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Uploading sourcemaps to gcs"&lt;/span&gt;
./google-cloud-sdk/bin/gsutil &lt;span class="nb"&gt;cp &lt;/span&gt;build/&lt;span class="k"&gt;**&lt;/span&gt;/&lt;span class="k"&gt;**&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;.map gs://&amp;lt;bucket-name&amp;gt;/
&lt;span class="c"&gt;# delete sourcemaps&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Deleting sourcemaps from netlify deploy"&lt;/span&gt;
&lt;span class="nb"&gt;rm &lt;/span&gt;build/&lt;span class="k"&gt;**&lt;/span&gt;/&lt;span class="k"&gt;**&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;.map
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You may be using another Continuous Integration (CI) tool like &lt;a href="https://travis-ci.com/"&gt;TravisCI&lt;/a&gt; but the concept will be exactly the same.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Build your code as usual&lt;/li&gt;
&lt;li&gt;You must install the GCloud SDK (which includes &lt;code&gt;gsutil&lt;/code&gt;, the cloud storage CLI) in a non-interactive fashion by getting it from archived versions. This also ensures you use a fix version of the SDK and future updates don’t break your CI pipeline.&lt;/li&gt;
&lt;li&gt;You must authenticate to Google Cloud. They recommend using a &lt;a href="https://cloud.google.com/iam/docs/service-accounts"&gt;service account&lt;/a&gt; for CI jobs. See how to create one from &lt;a href="https://cloud.google.com/iam/docs/creating-managing-service-accounts#creating_a_service_account"&gt;the docs&lt;/a&gt;. This will generate a JSON file which you need to provide during your deploy job. As we do not want to put this file in the repository for security reasons, I suggest storing its content as en environment variable in Netlify (or the CI tool you use) and building the file from the environment variable. (that’s what &lt;code&gt;$GCLOUD_KEY_FILE&lt;/code&gt; is all about)&lt;/li&gt;
&lt;li&gt;From there the hardest is done. Use &lt;code&gt;gsutil&lt;/code&gt; to upload the source maps and do not forget to delete them from the build folder before letting Netlify deploy that folder.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  BONUS: Upload code and source maps to Sentry
&lt;/h2&gt;

&lt;p&gt;If you’re using &lt;a href="https://sentry.io"&gt;Sentry&lt;/a&gt; as error reporting solution, you may also want to upload both the minified code and private source maps to Sentry to get the best error stack traces you can. To do that, we’ll simply add a section to create a Sentry “release” in our &lt;code&gt;build.sh&lt;/code&gt; file. Below are the extras to add right before deleting the source maps.&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;# build.sh&lt;/span&gt;

&lt;span class="c"&gt;# ... build and push to gcloud ... #&lt;/span&gt;

&lt;span class="c"&gt;# install sentry cli&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Installing and configuring sentry cli"&lt;/span&gt;
curl &lt;span class="nt"&gt;-L&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; sentry-cli https://github.com/getsentry/sentry-cli/releases/download/1.40.0/sentry-cli-Linux-x86_64
&lt;span class="nb"&gt;chmod &lt;/span&gt;u+x sentry-cli
./sentry-cli &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;SENTRY_ORG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;sentry-org&amp;gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;SENTRY_PROJECT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;sentry-project&amp;gt;
&lt;span class="c"&gt;# create sentry release and upload source maps&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Creating release"&lt;/span&gt;
./sentry-cli releases new &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;COMMIT_REF&lt;/span&gt;::7&lt;span class="k"&gt;}&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Setting release commits"&lt;/span&gt;
./sentry-cli releases set-commits &lt;span class="nt"&gt;--commit&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;github-org&amp;gt;/&amp;lt;github-repo&amp;gt;@&lt;/span&gt;&lt;span class="nv"&gt;$COMMIT_REF&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;COMMIT_REF&lt;/span&gt;::7&lt;span class="k"&gt;}&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Link deploy to release"&lt;/span&gt;
./sentry-cli releases deploys &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;COMMIT_REF&lt;/span&gt;::7&lt;span class="k"&gt;}&lt;/span&gt; new &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;$SENTRY_ENV&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Uploading source maps"&lt;/span&gt;
./sentry-cli releases files &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;COMMIT_REF&lt;/span&gt;::7&lt;span class="k"&gt;}&lt;/span&gt; upload-sourcemaps build/static/js/ &lt;span class="nt"&gt;--rewrite&lt;/span&gt; &lt;span class="nt"&gt;--url-prefix&lt;/span&gt; &lt;span class="s1"&gt;'~/static/js'&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Finalizing release"&lt;/span&gt;
./sentry-cli releases finalize &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;COMMIT_REF&lt;/span&gt;::7&lt;span class="k"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# ... delete source maps ... #&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Notes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The name of the release is your choice, I use the short SHA of the commit &lt;code&gt;${COMMIT_REF::7}&lt;/code&gt; but you can use anything, just make sure to adapt it in every &lt;code&gt;sentry-cli&lt;/code&gt; command.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;$SENTRY_ENV&lt;/code&gt; is an environment variable I use to name my release (dev, staging, prod). It is set by branch in the &lt;code&gt;netlify.toml&lt;/code&gt; file as described &lt;a href="https://www.netlify.com/docs/netlify-toml-reference/"&gt;in the docs&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;upload-sourcemaps&lt;/code&gt; will look for &lt;code&gt;.js&lt;/code&gt; and &lt;code&gt;.js.map&lt;/code&gt; files in the provided directory, here &lt;code&gt;build/static/js/&lt;/code&gt; where our JS assets are bundled by webpack. The &lt;code&gt;rewrite&lt;/code&gt; and &lt;code&gt;url-prefix&lt;/code&gt; options ensure that sentry uses its own uploaded artifacts instead of trying to get the one from cloud storage.&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;Hope this has been a useful post. Happy devops ;).&lt;/p&gt;

</description>
      <category>devops</category>
      <category>webpack</category>
      <category>googlecloud</category>
      <category>ci</category>
    </item>
    <item>
      <title>GraphQL Interfaces (and Union Types) with Prisma and Yoga</title>
      <dc:creator>Thibaut Tiberghien</dc:creator>
      <pubDate>Fri, 11 Oct 2019 10:22:31 +0000</pubDate>
      <link>https://dev.to/tibotiber/graphql-interfaces-and-union-types-with-prisma-and-yoga-26d3</link>
      <guid>https://dev.to/tibotiber/graphql-interfaces-and-union-types-with-prisma-and-yoga-26d3</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally posted on &lt;a href="https://itnext.io/graphql-interfaces-and-union-types-with-prisma-and-yoga-7224f9e1d9ad"&gt;Medium&lt;/a&gt; on Apr 2, 2018.&lt;/em&gt;&lt;br&gt;
&lt;em&gt;Photo by &lt;a href="https://unsplash.com/photos/BW0vK-FA3eg?utm_source=unsplash"&gt;Clint Adair&lt;/a&gt; on Unsplash.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  What is GraphQL?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://graphql.org/"&gt;GraphQL&lt;/a&gt; is an API query language that came out of the Facebook team and has been taking over the internet recently. It gets its strength from being built around a strongly typed API contract which defines exhaustively the data in your API as well as its schema, how to request for it, and so on. It supports deeply nested querying with controlled hydration and lets API clients combine data from different sources or models, all into a single query. With GraphQL, you get exactly the data you want, formatted the way you want, and in a single query, solving several problems of traditional REST APIs. Moreover, the API contract concept enables a wide variety of powerful developer tools, some of which I describe below.&lt;/p&gt;


&lt;h2&gt;
  
  
  My GraphQL Stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.prisma.io/"&gt;Prisma&lt;/a&gt;&lt;/strong&gt;, by the amazing team at &lt;a href="https://twitter.com/graphcool"&gt;Graphcool&lt;/a&gt;, is sort of a GraphQL ORM, it takes your data schema defined in the &lt;a href="https://blog.graph.cool/graphql-sdl-schema-definition-language-6755bcb9ce51"&gt;SDL (Schema Definition Language)&lt;/a&gt; and generates a database and API for it. The extensiveness of the generated API for (nested) CRUD operations is just amazing. You can deploy your database service in their cloud or using docker on your infrastructure. On top of this, Prisma comes with &lt;a href="https://www.prisma.io/docs/reference/prisma-bindings/overview-oobi0eicho"&gt;bindings&lt;/a&gt; which provide a convenience layer for building GraphQL servers on top of Prisma services.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/graphcool/graphql-yoga"&gt;graphql-yoga&lt;/a&gt;&lt;/strong&gt;, also by Graphcool (these guys are on 🔥), is the simplest way to build GraphQL servers. It is based on or compatible with most of the de facto standard libraries for building GraphQL servers in Javascript, but it takes the angle of improving developer experience by making everything easier to setup, with sensible defaults and a more declarative approach to configuration. It covers more or less the whole GraphQL spec, even up to WebSockets support for Subscriptions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/graphcool/graphql-playground"&gt;GraphQL Playground&lt;/a&gt;&lt;/strong&gt;, also by Graphcool (wuuut? 😱), is a web-based GraphQL client / IDE which supercharges your development workflow by introspecting your API contract to provide an automatic and interactive documentation for it as well as a query interface with auto-completion and validation against your schema. It is packed with nifty little features and is a go-to tool for anything GraphQL.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.apollographql.com/client"&gt;Apollo Client&lt;/a&gt;&lt;/strong&gt;, by the geniuses at &lt;a href="https://twitter.com/apollographql"&gt;Apollo&lt;/a&gt;, is probably the best GraphQL client available. It is compatible with every major frontend platform, and focuses on getting your data inside UI components without taking care of all the plumbing to get it. I love its declarative data fetching approach for React, and the advanced data loading features it supports. e.g. caching, loading, optimistic UI, pagination, etc. The devtools are a great addition to your developer experience as well.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Now to Interfaces…
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Some Context
&lt;/h3&gt;

&lt;p&gt;The GraphQL schema specification supports &lt;a href="https://graphql.org/learn/schema/#interfaces"&gt;Interfaces&lt;/a&gt; and &lt;a href="https://graphql.org/learn/schema/#union-types"&gt;Union Types&lt;/a&gt;. An Interface is an abstract type that includes a certain set of fields that a type must include to implement the interface, while Union Types allow for the grouping of several types without the sharing of any structure.&lt;/p&gt;

&lt;p&gt;For any non-trivial data structure, you will most probably need to leverage these constructs to model your data. The problem is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Prisma does not support Interfaces or Union Types yet. There are open issues for each of them — see &lt;a href="https://github.com/graphcool/prisma/issues/83"&gt;Interface&lt;/a&gt; and &lt;a href="https://github.com/graphcool/prisma/issues/165"&gt;Union Type&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;graphql-yoga supports both of them but their usage is not yet documented, which makes it hard to actually implement anything. I opened &lt;a href="https://github.com/graphcool/graphql-yoga/issues/121"&gt;an issue&lt;/a&gt; to know more a while back and this post is where it led me.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  My approach
&lt;/h3&gt;

&lt;p&gt;Since Prisma only supports Types and Enums at the moment, we have to find a way to model our data without using Interfaces in Prisma. We can however use Interfaces on the GraphQL server (graphql-yoga) so that the client facing API is properly structured and users get to request data across types using &lt;a href="https://graphql.org/learn/queries/#inline-fragments"&gt;Inline Fragments&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This leaves us with 2 options:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Storing all data with optional type-specific fields under one type (the interface) in Prisma, and then splitting the data back between the primitive types in the app server.&lt;/li&gt;
&lt;li&gt;Storing the data in each primitive type on Prisma, and stitching things for queries on the app server.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The problem with option 2 is that you loose the consistency in pagination. How do you get the last 20 items for the interface? How many of each primitive type should you request? You could do 20, sort them, and take 20, but that seems inelegant to me.&lt;/p&gt;

&lt;p&gt;So I picked option 1, let’s see how to implement it. I’ll give code snippets following the schema used &lt;a href="https://graphql.org/learn/schema/#interfaces"&gt;in the docs&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Prisma Workaround
&lt;/h3&gt;

&lt;p&gt;Basically, we want to merge all primitive types as a single “interface” type. Type-specific fields must be optional since they will not be available for every entry, and they are prefixed with the name of the primitive type to make sure they are unique. In the docs, we have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="c"&gt;# datamodel.graphql&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="k"&gt;interface&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Character&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;friends&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Character&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;appearsIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Episode&lt;/span&gt;&lt;span class="p"&gt;]!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Human&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;implements&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Character&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;friends&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Character&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;appearsIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Episode&lt;/span&gt;&lt;span class="p"&gt;]!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;starships&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Starship&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;totalCredits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Int&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Droid&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;implements&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Character&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;friends&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Character&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;appearsIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Episode&lt;/span&gt;&lt;span class="p"&gt;]!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;primaryFunction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Our workaround schema is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="c"&gt;# datamodel.graphql&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;DbCharacter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="c"&gt;# interface&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;friends&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Character&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;appearsIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Episode&lt;/span&gt;&lt;span class="p"&gt;]!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="c"&gt;# custom fields: Human&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;human_starships&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Starship&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;human_totalCredits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Int&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="c"&gt;#custom fields: Droid&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;droid_primaryFunction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Mapping interfaces in graphql-yoga
&lt;/h2&gt;

&lt;p&gt;As desired, we declare in the schema for the client facing API the same interface and primitive types as in the docs. We also copy the schema of the &lt;code&gt;dbCharacters&lt;/code&gt; query generated by Prisma as the &lt;code&gt;characters&lt;/code&gt; query for our client facing API. This could probably be more refined. The return type is however changed to our interface, hence returned items should be mapped to a primitive type on which type-specific inline fragments can be used.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="c"&gt;# src/schema.graphql&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c"&gt;# import {} from "./generated/prisma.graphql"&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;characters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;DbCharacterWhereInput&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;orderBy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;DbCharacterOrderByInput&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;skip&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Int&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;after&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;before&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Int&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Int&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Character&lt;/span&gt;&lt;span class="p"&gt;!]!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="k"&gt;interface&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Character&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;friends&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Character&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;appearsIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Episode&lt;/span&gt;&lt;span class="p"&gt;]!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Human&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;implements&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Character&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="c"&gt;# interface&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;friends&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Character&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;appearsIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Episode&lt;/span&gt;&lt;span class="p"&gt;]!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="c"&gt;# custom fields&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;starships&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Starship&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;totalCredits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Int&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Droid&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;implements&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Character&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="c"&gt;# interface&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;friends&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Character&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;appearsIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Episode&lt;/span&gt;&lt;span class="p"&gt;]!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="c"&gt;# custom fields&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;primaryFunction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In order to map items returned by Prisma to a primitive type, we need to provide a type resolver for our interface at the root of our resolvers object. I have separated the declaration of interface resolvers into a separate file and import it with &lt;a href="https://developer.mozilla.org/my/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment"&gt;object destructuring&lt;/a&gt; into the resolvers object. See the &lt;code&gt;__resolveType&lt;/code&gt; example in the &lt;code&gt;interfaces.js&lt;/code&gt; file. This is a simplistic example showcasing how to resolve types. You would implement yours according to the specific business logic of your data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/resolvers/index.js&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;interfaces&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;./interfaces&lt;/span&gt;&lt;span class="dl"&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;Query&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;./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;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="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;interfaces&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Query&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/resolvers/interfaces.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;interfaces&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;Character&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;__resolveType&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// resolve the type of the incoming interface data&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;obj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;primaryFunction&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Droid&lt;/span&gt;&lt;span class="dl"&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="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Human&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="p"&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;interfaces&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The last thing to do is to implement the client API for the interface. It is backed by the corresponding API from Prisma but we need to translate I/Os between the 2 schemas. The resolver for the &lt;code&gt;characters&lt;/code&gt; query is implemented in the &lt;code&gt;Query.js&lt;/code&gt; file, which is pretty classic. The implementation details are as follow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We must make sure all fields selected for the primitive types in the query are requested from Prisma. To do this I have written a utility function called &lt;code&gt;makeSelection&lt;/code&gt; into &lt;code&gt;interfaces.js&lt;/code&gt; which takes the &lt;code&gt;info&lt;/code&gt; object from the resolver and parses the query AST (&lt;code&gt;GraphQLResolveInfo&lt;/code&gt;) to generate the string selection sent to Prisma. This modifies the selection to make sure all fields nested in Inline Fragments such as &lt;code&gt;...on Droid { primaryFunction }&lt;/code&gt; will be queried from Prisma as normal prefixed fields, e.g. &lt;code&gt;droid_primaryFunction&lt;/code&gt;. The code for this method was pretty much trial and error while inspecting the &lt;code&gt;info&lt;/code&gt; object and mapping it to the expected selection to send to Prisma. &lt;em&gt;Disclaimer:&lt;/em&gt; the code covers only the queries I have have been needing and might need additions to cover all use-cases. Note also that I’m not an expert with ASTs so there might be a better way to do this, please suggest in the comments if you know one.&lt;/li&gt;
&lt;li&gt;We must format the objects received from Prisma back to their expected form in the client API schema. I use another utility function called &lt;code&gt;formatPrimitiveFields&lt;/code&gt;, also available in &lt;code&gt;interfaces.js&lt;/code&gt; which takes a field such as &lt;code&gt;droid_primaryFunction&lt;/code&gt; and remove the primitive type prefix.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/resolvers/Query.js&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;makeSelection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;formatPrimitiveFields&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;./interfaces&lt;/span&gt;&lt;span class="dl"&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;Query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;characters&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;info&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="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dbCharacters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;makeSelection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;info&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;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;formatPrimitiveFields&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;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;Query&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/resolvers/interfaces.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;R&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;ramda&lt;/span&gt;&lt;span class="dl"&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;interfaces&lt;/span&gt; &lt;span class="o"&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;unpackSelectionFromAST&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;R&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&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;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Field&lt;/span&gt;&lt;span class="dl"&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selectionSet&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="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&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="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; { &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;unpackSelectionFromAST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selectionSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selections&lt;/span&gt;
        &lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt; }`&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;InlineFragment&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;typeCondition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;NamedType&lt;/span&gt;&lt;span class="dl"&gt;'&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;R&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;compose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nx"&gt;R&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;field&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;R&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toLower&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;typeCondition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;_&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="nx"&gt;R&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;R&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;__&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt; &lt;span class="c1"&gt;// apollo client compatibility (__typename)&lt;/span&gt;
            &lt;span class="nx"&gt;unpackSelectionFromAST&lt;/span&gt;
          &lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selectionSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selections&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
          &lt;span class="nx"&gt;console&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="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;typeCondition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; unknown in selections AST`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="k"&gt;break&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;break&lt;/span&gt;
    &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="nx"&gt;console&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="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; unknown in selections AST`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;break&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;makeSelection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;info&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="nx"&gt;R&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;compose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;`{ &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;R&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt; }`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;R&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;R&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isNil&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nx"&gt;R&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;flatten&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;unpackSelectionFromAST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;R&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;selections&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nx"&gt;R&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;selectionSet&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nx"&gt;R&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;head&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;R&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fieldNodes&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="nx"&gt;info&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;formatPrimitiveFields&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;R&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;R&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;compose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;R&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fromPairs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;R&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(([&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;v&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;R&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/^.*_/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
    &lt;span class="nx"&gt;R&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toPairs&lt;/span&gt;
  &lt;span class="p"&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;interfaces&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;makeSelection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;formatPrimitiveFields&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;






&lt;p&gt;Unions Types are not directly covered in this post but they are pretty similar to the &lt;code&gt;__resolveType&lt;/code&gt; approach for Interfaces.&lt;/p&gt;

&lt;p&gt;Code snippets are written for node 8 and above.&lt;/p&gt;

&lt;p&gt;If you’re using &lt;strong&gt;Apollo Client&lt;/strong&gt;, note that interfaces and unions in inline fragments are not resolved properly out of the box. You need to setup a custom fragment matcher based on the api schema. This is explained in details &lt;a href="https://www.apollographql.com/docs/react/advanced/fragments.html#fragment-matcher"&gt;in the docs&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>graphql</category>
      <category>prisma</category>
      <category>yoga</category>
      <category>javascript</category>
    </item>
    <item>
      <title>My Recommended Free Resources to Learn React</title>
      <dc:creator>Thibaut Tiberghien</dc:creator>
      <pubDate>Fri, 11 Oct 2019 07:55:37 +0000</pubDate>
      <link>https://dev.to/tibotiber/my-recommended-free-resources-to-learn-react-1pca</link>
      <guid>https://dev.to/tibotiber/my-recommended-free-resources-to-learn-react-1pca</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally posted on &lt;a href="https://medium.com/netscape/my-recommended-free-resources-to-learn-react-68f4d20a8dc1"&gt;Medium&lt;/a&gt; on June 18, 2017.&lt;/em&gt;&lt;br&gt;
&lt;em&gt;Photo by &lt;a href="https://unsplash.com/photos/ZMraoOybTLQ"&gt;Artem Sapegin&lt;/a&gt; on Unsplash.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Over the past few months, I’ve been learning React slowly, trying to build an in-depth understanding of its large ecosystem. This post lists the resources I found most useful to get a solid foundation in this area. I have definitely missed out on some links or people, so please feel free to reply with your own favourites and recommendations.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: I strongly recommend coding along to get the most out of these.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://tylermcginnis.com/courses/react-fundamentals/"&gt;React Fundamentals&lt;/a&gt;, by Tyler McGinnis
&lt;/h2&gt;

&lt;p&gt;This is the best place to start in my opinion, because it begins with React out of its ecosystem to make you feel the pain points solved by each tool it introduces. That way you won’t feel overwhelmed by too much tooling all at once, and you will then understand the reason for the rather large tooling lineup.&lt;/p&gt;

&lt;p&gt;The course covers the JS bits you might miss, React (with and without JSX), the different kinds of React components, ES6 classes, React Router, Babel, Webpack. It offers good explanations right from the basics up to best practices. And you are lucky as it has been updated as of React v15.5.&lt;/p&gt;

&lt;h2&gt;
  
  
  Redux docs &amp;amp; egghead.io courses by Dan Abramov
&lt;/h2&gt;

&lt;p&gt;Redux is not mandatory with React, but I am personally a huge fan of how it keeps the code really clear and self-documenting, by decoupling concerns around state management.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;[EDIT] I am primarily not using Redux anymore, and prefer MobX-State-Tree (more details further down). However, I would still highly recommend learning Redux. You’ll become a much better developer just understanding the underlying principles to Redux. Read more about this in &lt;a href="https://www.robinwieruch.de/redux-javascript"&gt;this excellent article&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;There are 2 ways to go about this list — video lectures or docs tutorials. You can pick the method you prefer, but I do find useful to do both and this is the order I’d recommend (I prefer reading first, so feel free to swap the video/docs order):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="http://redux.js.org/"&gt;Redux docs&lt;/a&gt;, read the “Introduction” and “Basics” sections and treat the Todo List example as your first tutorial.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://egghead.io/courses/getting-started-with-redux"&gt;Getting Started with Redux&lt;/a&gt; course on egghead.io builds upon the Todo List example.&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://redux.js.org/docs/advanced/"&gt;Redux docs&lt;/a&gt;, read the “Advanced” and “Recipes” sections and treat the Reddit API example as your second tutorial.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://egghead.io/courses/building-react-applications-with-idiomatic-redux"&gt;Building React Applications with Idiomatic Redux&lt;/a&gt; course on egghead.io is a continuation of the first egghead.io course.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;See the note on MobX-State-Tree below, which is currently my favorite state management library. I would still recommend learning Redux, as it is an essential part of the community’s fundamentals and is an excellent way to understand simple functional programming concepts which will make you a better developer in the long run.&lt;/p&gt;

&lt;h2&gt;
  
  
  Other tools and libs
&lt;/h2&gt;

&lt;p&gt;Having gone through the above, you should be pretty confident by now to build a typical React + Redux app. The best at this point would be to pick a side project of your own to further explore things first-hand. My chosen project was a React + D3 playground which I wrote about in this post. Below are some tools and libs that I’ve been playing with and would recommend learning.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://facebook.github.io/immutable-js/"&gt;Immutable.js&lt;/a&gt; &amp;amp; &lt;a href="https://github.com/reactjs/reselect"&gt;Reselect&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;These tools are great for performance optimizations using pure components. They help to ensure the props you pass to your components are immutable, allowing to drop out of unnecessary render cycles. If you haven’t already, read the part on &lt;a href="http://redux.js.org/docs/recipes/UsingImmutableJS.html"&gt;Immutability&lt;/a&gt; from the redux docs. When using Redux, use Immutable.js to make your whole store immutable and Reselect to make computed props optimal with memoized selectors.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://www.styled-components.com/"&gt;Styled Components&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;This is my favorite CSS-in-JS solution. It has been thought to be &lt;em&gt;the&lt;/em&gt; way to go with CSS in the component age. It uses ES6 tagged template literals to allow JS-powered CSS writing, with dynamic theming, props-based styling, etc. It has full CSS support and allows you to basically write CSS along your components with close to no learning curve. Classes are automatically generated and styles are nicely encapsulated. This means your component are really portable without risking class conflicts inherent to big apps.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://github.com/facebookincubator/create-react-app"&gt;Create React App&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;This is the best way to get down to coding without having a ton of configuration to handle. It is supported by the React team and is more or less the React CLI people dream of. It provides a great developer experience, with out-of-the-box support for most of the features you might desire, as well as options for building your app for production.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://redux-saga.js.org/"&gt;Redux-Saga&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Redux does not support side-effects in its actions / reducers. A simple way to get around this limitation is &lt;a href="https://github.com/gaearon/redux-thunk"&gt;Redux Thunk&lt;/a&gt;, a Redux middleware for dispatching functions. If you want a more complex but elegant approach, Redux-Saga is pretty darn amazing. It is another Redux middleware based on the &lt;a href="http://formidable.com/blog/2017/javascript-power-tools-redux-saga/"&gt;Saga pattern&lt;/a&gt;, which helps you implement your side-effects calls as ES6 generators. You declare them in a central place and they are executed reactively to pure Redux actions.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://github.com/acdlite/recompose"&gt;Recompose&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Recompose is a React utility belt which I use mainly to write Higher-Order Components and compose behaviours into components in neat ways. It is very well introduced in the &lt;a href="https://egghead.io/courses/higher-order-components-with-functional-patterns-using-recompose"&gt;Higher Order Components with Functional Patterns Using Recompose&lt;/a&gt; egghead.io course by Tim Kindberg.&lt;/p&gt;

&lt;p&gt;If you want to know more about HOCs, there is also a great post by Franleplant entitled &lt;a href="https://medium.com/@franleplant/react-higher-order-components-in-depth-cf9032ee6c3e"&gt;React Higher Order Components in depth&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://mobx.js.org/"&gt;MobX&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Redux can sometimes be an overkill. Yet, you might still want some easy-to-use state management solution that is more featured than &lt;code&gt;setState()&lt;/code&gt;. I like MobX for such situations. MobX turns your state into an observable, allows for computed values and automatic reactions (side-effects) based on actions, and most importantly turns React components into reactive components automatically.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://github.com/mobxjs/mobx-state-tree"&gt;MobX-State-Tree (MST)&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Despite its name, MST is very different from MobX. It was built by the same people and leverages MobX under the hood for the observable and reactivity part. However, MST is a lot more opinionated and full-fledged than the minimal MobX. With MST, you need to provide the shape (type information) of you state “tree” (i.e. the models, actions, etc.) which is automatically leveraged to generate a living tree of immutable, structurally shared snapshots of your state. This can then be used to support time travel, hot module reloading, and other developer goodness that Redux got us accustomed to, but sparing us from the boilerplate.&lt;/p&gt;

&lt;p&gt;I see MST as a mature solution for managing state which combines the goodness of immutability, the ease of use of mutable structures, the convenience of type checking, the separation of concerns of dedicated state stores, and the natural performance of observable structures. &lt;em&gt;It is to state what React is to UI (paraphrasing &lt;a href="http://danielearwicker.github.io/json_mobx_Like_React_but_for_Data_Part_2.html"&gt;Daniel Earwicker&lt;/a&gt;)&lt;/em&gt;. It is the best developer experience (DX) I’ve had managing state. 🔥&lt;/p&gt;

&lt;p&gt;I’d recommend learning in depth about MobX from &lt;a href="https://mobx.js.org/"&gt;the docs&lt;/a&gt;, before diving in on MST with the excellent &lt;a href="https://egghead.io/courses/manage-application-state-with-mobx-state-tree"&gt;egghead course&lt;/a&gt; by &lt;a href=""&gt;Michel Weststrate&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final good reads
&lt;/h2&gt;

&lt;p&gt;Having played around with the above on your own, you should start to feel pretty solid. I would then recommend the following resources to wrap things up.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://facebook.github.io/react/docs/hello-world.html"&gt;React docs&lt;/a&gt;. Yes, it probably sounds odd to finish here. While I find the above resources provide a better pace to get started with React, the docs are a great reference for everything-React and make for a good conclusion chapter.&lt;/li&gt;
&lt;li&gt;Watch the &lt;a href="https://www.youtube.com/watch?v=7ctkTFv6XdA&amp;amp;feature=youtu.be"&gt;Worst ‘Hello World’ ever&lt;/a&gt; by mpj, which, given all your newly acquired knowledge, should make you laugh. &lt;em&gt;Disclaimer: this is not the way you should be coding!&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;blockquote&gt;
&lt;p&gt;I would like to highlight that the &lt;a href="https://frontendmasters.com/courses/complete-intro-react/"&gt;Complete Intro to React&lt;/a&gt; course by Brian Holt on Frontend Masters is really great as well. It is not part of the resources listed above as it is not free, but I do recommend it.&lt;/p&gt;

&lt;p&gt;Shout-out to &lt;a href="https://clevertech.biz/"&gt;Clevertech&lt;/a&gt;, my employer who sponsored the Frontend Masters subscription. Check out what we are up to and &lt;a href="https://clevertech.biz/join"&gt;come join us&lt;/a&gt;!&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Some great React-ers to follow on Twitter
&lt;/h2&gt;

&lt;p&gt;In alphabetical order, let’s not play favourites!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://twitter.com/sharifsbeat"&gt;A. Sharif&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/holtbt"&gt;Brian Holt&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/dan_abramov"&gt;Dan Abramov&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/dceddia"&gt;Dave Ceddia&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/rauchg"&gt;Guillermo Rauch&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/linclark"&gt;Lin Clark&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/mpjme"&gt;Mattias P Johansson&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/mxstbr"&gt;Max Stoiber&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/tylermcginnis33"&gt;Tyler McGinnis&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/wesbos"&gt;Wes Bos&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>react</category>
      <category>redux</category>
      <category>mobx</category>
      <category>javascript</category>
    </item>
    <item>
      <title>React + D3.js: Balancing Performance &amp; Developer Experience</title>
      <dc:creator>Thibaut Tiberghien</dc:creator>
      <pubDate>Fri, 11 Oct 2019 07:33:01 +0000</pubDate>
      <link>https://dev.to/tibotiber/react-d3-js-balancing-performance-developer-experience-1b5g</link>
      <guid>https://dev.to/tibotiber/react-d3-js-balancing-performance-developer-experience-1b5g</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally posted on &lt;a href="https://medium.com/@tibotiber/react-d3-js-balancing-performance-developer-experience-4da35f912484"&gt;Medium&lt;/a&gt; on May 17, 2017.&lt;/em&gt; &lt;/p&gt;

&lt;p&gt;Let’s put it out there, I love dashboards. I find the way they help you gain a rapid understanding out of complex information really interesting. I have written real-time data visualisations in the past, but always thought that complete dashboards were really hard to implement. That was until I learnt React a while back, and then it clicked: I had finally found a tech that would make building dashboards easier and save the developer’s sanity. I recently started on a side project to try and get React and D3 to integrate seamlessly, with two main goals: &lt;em&gt;render performance and developer experience (DX)&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Below is a quick showcase of the mock dashboard I built. The data here is not important since the focus is really on the technical integration of both libraries. You can find a live demo hosted on &lt;a href="https://rd3.now.sh/"&gt;∆ now&lt;/a&gt; and the code on &lt;a href="https://github.com/tibotiber/rd3"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--16GZh64L--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://miro.medium.com/max/1600/1%2AiO5NER89kECm8gdfEv23bg.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--16GZh64L--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://miro.medium.com/max/1600/1%2AiO5NER89kECm8gdfEv23bg.gif" alt="mock dashboard"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Motivation
&lt;/h3&gt;

&lt;p&gt;There are many great things about integrating React and D3. You can build yourself a custom library of reusable charts backed by React, which means better render performance with React’s reconciliation, and opportunities for lifecycle performance tweaks. Additionally, you bring all the new shiny possibilities of the modern web to your D3 charts: great developer tools, server-side rendering, an array of excellent state management options, data selectors and immutability, CSS-in-JS, …&lt;/p&gt;

&lt;p&gt;Of course, you can use some of these things without React but it is so much easier when the path is all tar road with proper documentation.&lt;/p&gt;

&lt;h3&gt;
  
  
  The problem
&lt;/h3&gt;

&lt;p&gt;The problem lies with integrating React and D3 &lt;em&gt;seamlessly&lt;/em&gt;. Both libraries are built upon data-driven DOM manipulation where the DOM is taken care of for you. So without careful precautions, React would not accept well getting &lt;em&gt;its stuff&lt;/em&gt; moved around. It’s like trying to change some code convention in a project managed by that OCD colleague of yours (that might be me). &lt;em&gt;Ouch!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;So I read a bit here and there and compared the options available. Below is what I found and thought. I hope you will find this interesting or even helpful. I’m still learning all this, so do drop a response whether you want to send appreciation, highlight a misunderstanding on my end, or point me in a better direction.&lt;/p&gt;




&lt;h2&gt;
  
  
  React + D3: What is the best way?
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;TL;DR: Looking into integrating React and D3 seamlessly, I first tried to disable React in D3 land (1), then to use D3’s helpers only and to render charts as JSX with React (2), to finally settle on the &lt;code&gt;react-faux-dom&lt;/code&gt; approach (3).&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Solution 1 — To each its (DOM) land!
&lt;/h3&gt;

&lt;p&gt;The first and simplest idea I have encountered is to basically disable React in D3 land. There are a few techniques to do so, either by rendering an empty &lt;code&gt;&amp;lt;div/&amp;gt;&lt;/code&gt; with React which becomes D3’s scope, or by returning &lt;code&gt;false&lt;/code&gt; in the &lt;code&gt;shouldComponentUpdate()&lt;/code&gt; lifecycle method.&lt;/p&gt;

&lt;p&gt;My main issue with this rather effective solution is that you lose all the goodness brought by React into D3 land. In particular, you get slower rendering performance by doing heavy DOM manipulation that React’s &lt;a href="https://facebook.github.io/react/docs/reconciliation.html"&gt;reconciliation&lt;/a&gt; algorithm could have shaved milliseconds off. You also loose all the tooling and the DX provided by React that you probably started to love (see Motivation). And for the last point, I will just go ahead and quote &lt;a href="https://twitter.com/OliverCaldwell"&gt;Oliver Caldwell&lt;/a&gt;, whom I completely agree with.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Many solutions involve stepping out of the React tree for that specific component, which does work, but leaves you with a little island of mutable DOM, festering away inside your tree. It just doesn’t feel quite right to me.&lt;br&gt;
— Oliver Caldwell, &lt;a href="https://oli.me.uk/2015/09/09/d3-within-react-the-right-way/"&gt;D3 within React the right way&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Solution 2 — D3 for the maths, React for the DOM
&lt;/h3&gt;

&lt;p&gt;This is probably the most widespread technique at the time of writing. It consists in only using D3’s helpers to prepare the data, the axes, etc. and then feed all of that to React to be rendered. This means you don’t actually use D3’s data binding, but handle it yourself with React by specifying a &lt;code&gt;key&lt;/code&gt; for all your SVG elements. This is something you sometimes have to do in D3 too, when the binding is not trivial enough for D3 to figure it out alone. The big change here is that you will render your SVG elements as JSX, instead of using the familiar &lt;code&gt;d3.(...).append()&lt;/code&gt;. There is a great post by &lt;a href="https://twitter.com/danscan"&gt;Dan Scanlon&lt;/a&gt; on &lt;a href="https://hackernoon.com/how-and-why-to-use-d3-with-react-d239eb1ea274"&gt;Hackernoon&lt;/a&gt; about this approach.&lt;/p&gt;

&lt;p&gt;This method provides good performance overall, but my main issues here are with the DX. Firstly, the visualization code is extremely different from vanilla D3 code. This introduces several disavantages in my opinion.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Having to draw out the SVG and axes myself in JSX feels really awkward at first, I’m not sure how long I would take to get used to it and whether I would ever like this way of doing things.&lt;/li&gt;
&lt;li&gt;It undeniably stamps your code “React”, making it harder to extract it from its component in case it ever becomes useful. I worry here about framework lock-in, since the volatility of JS frameworks is rather high as compared to D3's.&lt;/li&gt;
&lt;li&gt;It becomes time-consuming to code from example (or port existing code), since you have to convert all your vanilla D3 code to JSX. This is important for me as it is my default process for implementing D3 visualizations, and I’m probably not alone considering the &lt;a href="http://blockbuilder.org/search"&gt;20K+ examples available&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The learning curve for D3 developers is steep and I am not sure if it is worth the cost, at least not for every team.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Another issue with this solution is that since D3’s data binding is not used, we also lose the &lt;a href="http://bost.ocks.org/mike/circles/"&gt;enter-update-exit pattern&lt;/a&gt; and therefore D3 transitions. I consider D3 transitions and animations as a great part of D3's value proposition. This is what powers many techniques for creating rich user experiences. This, added to the reduced DX, makes it hard for me to really embrace this approach.&lt;/p&gt;

&lt;h3&gt;
  
  
  Solution 2b — Enter/exit with React, update with D3
&lt;/h3&gt;

&lt;p&gt;This solution was described in an excellent &lt;a href="https://medium.com/@sxywu/on-d3-react-and-a-little-bit-of-flux-88a226f328f3"&gt;Medium post&lt;/a&gt; by &lt;a href="https://twitter.com/sxywu"&gt;Shirley Wu&lt;/a&gt;. It builds upon solution 2 but mixes in a bit of solution 1. The idea is still to use D3’s helpers and JSX to render SVG elements, except that now the elements rendered by React are rendered without attributes, and D3 is used to add their attributes. So the line of ownership between React and D3 is not at the element level like in solution 1, but at the attributes level. Although small, the difference is key to getting D3 transitions back. Attributes being handled by D3, we can add an &lt;code&gt;enter()&lt;/code&gt; method called in &lt;code&gt;componentDidMount()&lt;/code&gt; and an &lt;code&gt;update()&lt;/code&gt; method called in &lt;code&gt;componentDidUpdate()&lt;/code&gt;. Each of these methods can use typical D3 code to position, style, and transition elements.&lt;/p&gt;

&lt;p&gt;There are some caveats to this approach:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;As declared in the post introducing this idea, &lt;code&gt;exit()&lt;/code&gt; transitions are not supported without bringing in React’s &lt;a href="https://github.com/reactjs/react-transition-group"&gt;TransitionGroup&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Since React does not keep track of attributes, we have to manually implement state comparison to detect when the component should update in order to call the &lt;code&gt;update()&lt;/code&gt; method performing D3 transitions. This basically means that we implement React’s job for it because we intentionaly bypassed it.&lt;/li&gt;
&lt;li&gt;This approach still has all the DX issues inherent to solution 2.&lt;/li&gt;
&lt;li&gt;I found the implementation too complex for a simple chart. I believe this is due to the need to split the code according to the line of ownership between React and D3, instead of splitting it into logical units.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Solution 3 — Feed D3 a fake DOM that renders to state
&lt;/h3&gt;

&lt;p&gt;This is the solution I found the most elegant so far, and it is what powers the demo at the beginning of this post. It is based on &lt;a href="https://github.com/Olical/react-faux-dom"&gt;react-faux-dom&lt;/a&gt;, made by &lt;a href="https://twitter.com/OliverCaldwell"&gt;Oliver Caldwell&lt;/a&gt; who detailed the idea &lt;a href="https://oli.me.uk/2015/09/09/d3-within-react-the-right-way/"&gt;on his blog&lt;/a&gt;. The concept is that D3 is fed a fake DOM which implements all methods it would expect the DOM to have. That fake DOM is manipulated by D3 and then automatically rendered as React elements stored into the component’s state where React can pick up changes and kick-off an update, including lifecycle methods and reconciliation as you would expect.&lt;/p&gt;

&lt;p&gt;I found this approach elegant because both D3 and React are used without alienation.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Except for feeding the faux DOM node to D3 instead of using a selector as you normally would, vanilla D3 code can be used. This means no framework lock-in, easily port existing code or start from example, and no learning curve for D3 developers.&lt;/li&gt;
&lt;li&gt;The full D3 API is supported, with transitions, animations, mouse events, etc.&lt;/li&gt;
&lt;li&gt;React’s component lifecycle and render methods are being used, and changes made by the D3 are picked up and reconciled seamlessly. Hence, you get to enjoy the typical render performance of React components.&lt;/li&gt;
&lt;li&gt;SVG elements are automatically transformed into React elements and are inspectable in the devtools.&lt;/li&gt;
&lt;li&gt;The implementation is compatible with server-side rendering, so you get isomorphic charts at no cost.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Overall, this solution has restored my faith is having a great DX when using D3 visualizations in React components, while making the most out of React’s render performance.&lt;/p&gt;




&lt;h2&gt;
  
  
  Performance tricks
&lt;/h2&gt;

&lt;p&gt;In this section, I will describe some techniques I have used to improve the render performance of my playground dashboard. The basic idea is that D3 updates are more expensive than React re-renders. Indeed, without resorting to performance-motivated tricks to decompose you D3 code, each time D3 processes some update it needs to recompute all the chart helpers and check all the data to &lt;em&gt;possibly&lt;/em&gt; update the bound elements. Also D3 updates will trigger a new render cycle of the component anyway. So how can we avoid D3 updates? &lt;em&gt;TL;DR: Only update D3 on new data or on resize events.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Extract tooltips to React
&lt;/h3&gt;

&lt;p&gt;Tooltips are typically something I prefer extracting from D3 into React land. Being usually displayed on mouse hover and hidden on mouse out, their update rate is much higher than that of the underlying data. This means recomputing helpers and checking over the data is pointless and it makes tooltips prime candidates for &lt;em&gt;Reactification — if that’s even a word&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;To extract tooltips to React, I add &lt;code&gt;mouseover&lt;/code&gt; and &lt;code&gt;mouseout&lt;/code&gt; event listeners to SVG elements, in which I &lt;code&gt;setState&lt;/code&gt; the &lt;code&gt;hover&lt;/code&gt; value so that React can kick-off a render cycle on updates. I often use &lt;code&gt;setTimeout()&lt;/code&gt; in the &lt;code&gt;mouseout&lt;/code&gt; callback, and then &lt;code&gt;clearTimeout()&lt;/code&gt; in the &lt;code&gt;mouseover&lt;/code&gt; callback to avoid the flickering between hovers caused by the margin/space between the graph elements. This also lets me use CSS animations to translate tooltips. The tooltip is then rendered directly in JSX, using D3 helpers for positioning if necessary. You can simply share the helpers in the component’s scope using the &lt;code&gt;this&lt;/code&gt; keyword. Also, we must be careful to avoid updating D3 when the hover changes in state. To do so, I omit &lt;code&gt;hover&lt;/code&gt; from the state’s shallow comparison done in &lt;code&gt;componentDidUpdate&lt;/code&gt;. Now, that’s a lot to take in without code so here you go with a simplified code excerpt and feel free to dive in the full source on GitHub.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;Chart&lt;/span&gt; &lt;span class="kd"&gt;extends&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;Component&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="nx"&gt;componentDidUpdate&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prevProps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;prevState&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;stripState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;omit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hover&lt;/span&gt;&lt;span class="dl"&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;shallowEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stripState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;stripState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prevState&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;renderD3&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="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="nx"&gt;setHover&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hX&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;hover&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;hX&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="nx"&gt;computeTooltipProps&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hX&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;hoveredData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;values&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
      &lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;hX&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="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hoveredData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;y&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))),&lt;/span&gt;
        &lt;span class="na"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hX&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;hX&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hoveredData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;y&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;, &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="nx"&gt;render&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;chart&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hover&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Tooltip&lt;/span&gt; &lt;span class="p"&gt;{...&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;computeTooltipProps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hover&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&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="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="nx"&gt;renderD3&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="c1"&gt;// make x and y helpers available to JSX for tooltips positioning&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;d3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scale&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ordinal&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;xDomain&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rangeRoundBands&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mf"&gt;0.08&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;d3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;linear&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;yStackMax&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nx"&gt;range&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="c1"&gt;// add mouse event listeners&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;rect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;layer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selectAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rect&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;rect&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;enter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rect&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="nx"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;x&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;y&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;width&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rangeBand&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;height&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mouseover&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;d&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;clearTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;unsetHoverTimeout&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setHover&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&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;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mouseout&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;d&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;unsetHoverTimeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setHover&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
          &lt;span class="mi"&gt;200&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Handle styling updates in a parent component
&lt;/h3&gt;

&lt;p&gt;If you decide to go with dynamic styling for your charts — for example by reducing the opacity of non-hovered values, or by letting users change colors dynamically — you should certainly not go through a D3 update to do so. Instead, add a CSS class to your SVG elements that includes a key to the data and/or group they represent, and then handle styling outside of D3 land using your favourite CSS-in-JS tool. I personally am a huge fan of &lt;a href="https://styled-components.com/"&gt;styled-components&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Going further with this idea, if you are building a dashboard or anything that gets you maintaining multiple charts in your codebase, you might want to share the parts of the state that dictate your charts styling into a parent component — I love &lt;a href="http://redux.js.org/"&gt;Redux&lt;/a&gt; for state management, but pick anything that works for you. You can then apply styling on that parent component, and it will be shared by all chart components in its subtree. For example, in my playground dashboard, none of the chart components need rendering when the user picks a new color from the pallet, it’s all handled by rendering the dashboard component. Similarly hovering the barchart does not re-render the scatterplot although it looks like it does; the dashboard takes care of setting the opacity on filtered data. This also has the advantage that you code your styling once and it is handled for all your chart components, so you have one less thing to manage in your charts code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use pure components, immutability and memoized selectors
&lt;/h3&gt;

&lt;p&gt;This is not really specific to React+D3, but since I’m on performance tricks, I might as well mention it. You can make big wins in render performance by reducing the need for React to even render your components (recompute the virtual DOM) and perform the reconciliation when you know there is nothing to update. There are a few techniques that you should employ together to do this.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;React components normally update when their parent component does or if their props or state change. You can extend &lt;code&gt;React.PureComponent&lt;/code&gt; instead of &lt;code&gt;React.Component&lt;/code&gt; and your component will only update if the shallow comparison of its state and props shows differences. See &lt;a href="https://facebook.github.io/react/docs/react-api.html#react.purecomponent"&gt;the docs&lt;/a&gt; for details.&lt;/li&gt;
&lt;li&gt;Because deep comparison can be expensive in Javascript, especially with visualizing large datasets, pure components only perform a shallow comparison. This means your component’s state and props are compared by reference to their previous self. In order to use pure components effectively, you should be sure to make your state and props immutable. One option to do this is the awesome &lt;a href="https://facebook.github.io/immutable-js/"&gt;immutable.js&lt;/a&gt; which, being a Redux user, I simply apply on my entire Redux store at initialization. I then make sure to apply immutable modifications to the store in my reducers.&lt;/li&gt;
&lt;li&gt;Props are passed down from parent components or &lt;a href="https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0"&gt;containers&lt;/a&gt;, they are often calculated by these components from the state. You need to make sure that new values are not recomputed when the state has not changed. To do so, you can use memoized selectors with &lt;a href="https://github.com/reactjs/reselect"&gt;reselect&lt;/a&gt;, a “selector” library for Redux. Reselect only computes new props values when the underlying state has changed, and return the reference to the previous value if the state has not changed, making it a perfect fit for pure components and immutable redux stores.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  That’s all folks!
&lt;/h2&gt;

&lt;p&gt;It has been an exciting challenge trying to get the best of React and D3 in a seamless developer experience while keeping performance in mind. A great thank you to the authors of the articles I posted above for getting much of the problem stated and for providing some great answers. A huge shout out to &lt;a href="https://twitter.com/OliverCaldwell"&gt;Oliver Caldwell&lt;/a&gt; for masterminding the &lt;a href="https://github.com/Olical/react-faux-dom"&gt;react-faux-dom&lt;/a&gt; approach. I hope to see it evolve, improve further, and get the community attention that I think it deserves. I leave the rest to the comments section. We can hopefully see some interesting ideas and debate about this these techniques.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Edit: a recent article by &lt;a href="https://twitter.com/golodhros"&gt;Marcos Iglesias&lt;/a&gt; is a great addition to this with a look at more charting libs for React and D3, it’s at &lt;a href="https://www.smashingmagazine.com/2018/02/react-d3-ecosystem"&gt;https://www.smashingmagazine.com/2018/02/react-d3-ecosystem&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>d3</category>
      <category>dashboard</category>
      <category>dataviz</category>
    </item>
    <item>
      <title>Digital Ocean: Setup DNS, Nginx web hosting &amp; G Suite for emails</title>
      <dc:creator>Thibaut Tiberghien</dc:creator>
      <pubDate>Fri, 11 Oct 2019 05:21:36 +0000</pubDate>
      <link>https://dev.to/tibotiber/digital-ocean-setup-dns-nginx-web-hosting-g-suite-for-emails-378f</link>
      <guid>https://dev.to/tibotiber/digital-ocean-setup-dns-nginx-web-hosting-g-suite-for-emails-378f</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally posted on &lt;a href="https://medium.com/@tibotiber/digital-ocean-dns-nginx-web-server-google-apps-for-emails-a2648bd8c47b"&gt;Medium&lt;/a&gt; on May 8, 2016.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;YMMV:&lt;/strong&gt; The code below is given for the environment and use-case I had at the time of writing, adapt it for your requirements.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Web hosting on a Digital Ocean droplet, running Ubuntu 16.04&lt;/li&gt;
&lt;li&gt;Nginx web server, serving &lt;a href="http://www.planecq.com"&gt;www.planecq.com&lt;/a&gt; from &lt;code&gt;/var/www/planecq.com/html&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;G Suite for email&lt;/li&gt;
&lt;li&gt;DNS settings on Digital Ocean&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  1. DNS
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Forward DNS management to Digital Ocean
&lt;/h3&gt;

&lt;p&gt;Go to your domain name registrar, search for the DNS settings and set the following external nameservers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ns1.digitalocean.com
ns2.digitalocean.com
ns3.digitalocean.com
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This may take a while to propagate, up to 48h, but usually around an hour or two for me.&lt;/p&gt;

&lt;h3&gt;
  
  
  Direct web traffic for your domain to your droplet
&lt;/h3&gt;

&lt;p&gt;Login to &lt;a href="//cloud.digitalocean.com"&gt;cloud.digitalocean.com&lt;/a&gt;, go to Networking, then Domains. Add a domain and link it to the relevant droplet. After that, click “view” for your newly added domain and add a CNAME record for &lt;code&gt;www&lt;/code&gt; pointing towards your main A record, in my case &lt;code&gt;planecq.com.&lt;/code&gt;, with the trailing dot. You should be good to go.&lt;/p&gt;

&lt;h3&gt;
  
  
  Direct email traffic for your domain to G Suite
&lt;/h3&gt;

&lt;p&gt;Login to &lt;a href="//cloud.digitalocean.com"&gt;cloud.digitalocean.com&lt;/a&gt;, go to Networking, then Domains. Click “view” for your domain, then “MX”, then hit the “Add Gmail MX records”. You’re done!&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Web Hosting
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Setup Nginx to host your website
&lt;/h3&gt;

&lt;p&gt;Install Nginx:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get update
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; nginx
&lt;span class="nb"&gt;sudo &lt;/span&gt;update-rc.d nginx defaults
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;It should be running after install, you can check with&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;service nginx status
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Add the site’s config file (/etc/nginx/sites-available/planecq.com)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;server {
       listen       80;
       listen       [::]:80;
       server_name  planecq.com;
       return       301 $scheme://www.planecq.com$request_uri;
}

server {
       listen 80 default;
       listen [::]:80 default;

       root /var/www/planecq.com/html;

       index index.min.html index.html;

       server_name www.planecq.com;

       gzip_static on;
       gzip_proxied no-cache no-store private expired auth;
       gzip_http_version 1.0;

       location / {
                try_files $uri $uri/ =404;
       }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Note: you can only have one &lt;code&gt;default&lt;/code&gt; server, so in case you are co-hosting several websites, remember to choose the one you want.&lt;/p&gt;

&lt;p&gt;Enable your site:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo ln&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; /etc/nginx/sites-available/planecq.com /etc/nginx/sites-enabled/planecq.com
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Disable the default Nginx site:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo rm /etc/nginx/sites-enabled/default
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You can play around with more settings in your site’s config file or in Nginx config (&lt;code&gt;/etc/nginx/nginx.conf&lt;/code&gt;) to affect all of your websites.&lt;/p&gt;

&lt;p&gt;You can now copy your website’s files to &lt;code&gt;/var/www/planecq.com/html&lt;/code&gt; and it should work once the DNS is propagated. You can already try to hit the site using the IP of your droplet.&lt;/p&gt;

</description>
      <category>digitalocean</category>
      <category>nginx</category>
      <category>gsuite</category>
      <category>devops</category>
    </item>
    <item>
      <title>Deployment of static websites to Digital Ocean using TravisCI</title>
      <dc:creator>Thibaut Tiberghien</dc:creator>
      <pubDate>Fri, 11 Oct 2019 05:06:42 +0000</pubDate>
      <link>https://dev.to/tibotiber/deployment-of-static-websites-to-digital-ocean-using-travisci-4c2c</link>
      <guid>https://dev.to/tibotiber/deployment-of-static-websites-to-digital-ocean-using-travisci-4c2c</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally posted on &lt;a href="https://medium.com/@tibotiber/deployment-of-static-websites-to-digital-ocean-using-travisci-957e6e0f1f9d"&gt;Medium&lt;/a&gt; on May 8, 2016.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;YMMV:&lt;/strong&gt; The code below is given for the environment and use-case I had at the time of writing, adapt it for your requirements.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Code on GitHub, master branch deploys to prod, and staging branch deploys to a subdomain for preview and QA.&lt;/li&gt;
&lt;li&gt;Web hosting on a Digital Ocean droplet, running Ubuntu 16.04. Using Nginx web server, serving &lt;a href="http://www.planecq.com"&gt;www.planecq.com&lt;/a&gt; from &lt;code&gt;/var/www/planecq.com/html&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;TravisCI for the automatic deployment when git pushing to specific branches. It pushes to a bare git repo at &lt;code&gt;/var/www/planecq.com/.git&lt;/code&gt;. From there, a post-receive hook checks out the latest code to the html folder.&lt;/li&gt;
&lt;li&gt;Gulp used for the build and test workflow&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  1. Create an encrypted private key for travis (on your dev machine)
&lt;/h2&gt;

&lt;p&gt;Install Travis CLI if needed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;gem &lt;span class="nb"&gt;install &lt;/span&gt;travis
travis login
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Create the encrypted key and public key in your local repo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ~/Sites/planecq.com
&lt;span class="nb"&gt;touch&lt;/span&gt; .travis.yml
ssh-keygen &lt;span class="nt"&gt;-t&lt;/span&gt; rsa &lt;span class="nt"&gt;-N&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt; &lt;span class="nt"&gt;-C&lt;/span&gt; &lt;span class="s2"&gt;"travis@planecq.com"&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; travis_rsa
travis encrypt-file travis_rsa &lt;span class="nt"&gt;--add&lt;/span&gt;
&lt;span class="nb"&gt;rm &lt;/span&gt;travis_rsa
&lt;span class="nb"&gt;cat &lt;/span&gt;travis_rsa.pub &lt;span class="c"&gt;# &amp;lt;&amp;lt; copy this for later&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  2. Setup Travis on the droplet
&lt;/h2&gt;

&lt;p&gt;Create a passwordless travis user on the droplet, setup the generated access key, and give it access to the folder where the website is hosted:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;adduser &lt;span class="nt"&gt;--disabled-password&lt;/span&gt; &lt;span class="nt"&gt;--gecos&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt; travis
&lt;span class="nb"&gt;sudo chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; travis:travis /var/www/planecq.com
&lt;span class="nb"&gt;sudo &lt;/span&gt;su travis
&lt;span class="nb"&gt;mkdir&lt;/span&gt; ~/.ssh
&lt;span class="nb"&gt;chmod &lt;/span&gt;700 .ssh
emacs .ssh/authorized_keys
&lt;span class="s2"&gt;"copy content of previous cat, save, exit"&lt;/span&gt;
&lt;span class="nb"&gt;chmod &lt;/span&gt;600 .ssh/authorized_keys
&lt;span class="nb"&gt;exit&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Prepare remote repository on the droplet
&lt;/h2&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;su travis
&lt;span class="nb"&gt;cd&lt;/span&gt; /var/www/planecq.com
&lt;span class="nb"&gt;mkdir&lt;/span&gt; .git
&lt;span class="nb"&gt;cd&lt;/span&gt; .git
git init &lt;span class="nt"&gt;--bare&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;hooks
emacs post-receive
&lt;span class="s2"&gt;"copy the content of the hook posted below, save, exit"&lt;/span&gt;
&lt;span class="nb"&gt;chmod&lt;/span&gt; +x post-receive
&lt;span class="nb"&gt;exit&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;post-receive hook:&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;#!/bin/sh&lt;/span&gt;
git &lt;span class="nt"&gt;--work-tree&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/var/www/planecq.com/html/ &lt;span class="nt"&gt;--git-dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/var/www/planecq.com/.git checkout &lt;span class="nt"&gt;-f&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  4. Travis config
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;.travis.yml&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;language&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node_js&lt;/span&gt;
&lt;span class="na"&gt;node_js&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;4.3.1&lt;/span&gt;
&lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;global&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;NOKOGIRI_USE_SYSTEM_LIBRARIES=true&lt;/span&gt; &lt;span class="c1"&gt;# speeds up installation of html-proofer&lt;/span&gt;
&lt;span class="na"&gt;addons&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;ssh_known_hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;webhost.planecq.xyz&lt;/span&gt;
&lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;only&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;master&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;staging&lt;/span&gt;
&lt;span class="na"&gt;before_install&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;rvm install 2.2.2&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;openssl aes-256-cbc -K $encrypted_b0b2958c016f_key -iv $encrypted_b0b2958c016f_iv -in .travis/travis_rsa.enc -out ~/.ssh/travis_rsa -d&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;chmod 600 ~/.ssh/travis_rsa&lt;/span&gt;
&lt;span class="na"&gt;install&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;gem install html-proofer&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;npm install -g gulp&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;npm install&lt;/span&gt;
&lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;gulp build &amp;amp;&amp;amp; gulp test&lt;/span&gt;
&lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;skip_cleanup&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;provider&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;script&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.travis/deploy.sh&lt;/span&gt;
  &lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;all_branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
&lt;span class="na"&gt;notifications&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;
  &lt;span class="na"&gt;slack&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;secure&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rWg9[...]x69&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;gulp build&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is where the website is compressed for production. I have written &lt;a href="https://medium.com/@tibotiber/compile-and-compress-static-websites-for-production-using-gulp-8af38135b2ad"&gt;a separate post covering that&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;gulp test&lt;/strong&gt; (extract of gulpfile.js)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;exec&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;child_process&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;gulp&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;gulp&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;runSequence&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;run-sequence&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;bootlint&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;gulp-bootlint&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Validate html, links, etc.&lt;/span&gt;
&lt;span class="nx"&gt;gulp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;html-proofer&lt;/span&gt;&lt;span class="dl"&gt;'&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;done&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;htmlproofer ./index.min.html --check-html --check-favicon --check-external-hash&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="nx"&gt;done&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Validate bootstrap&lt;/span&gt;
&lt;span class="nx"&gt;gulp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bootlint&lt;/span&gt;&lt;span class="dl"&gt;'&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="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;gulp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./index.html.min&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="nx"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bootlint&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;stoponerror&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="p"&gt;}));&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Full test task&lt;/span&gt;
&lt;span class="nx"&gt;gulp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test&lt;/span&gt;&lt;span class="dl"&gt;'&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;cb&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;runSequence&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;html-proofer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bootlint&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Util to execute external command&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;done&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;opts&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;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;stderr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&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="nx"&gt;stderr&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;done&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;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;.travis/deploy.sh&lt;/strong&gt;&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;#!/bin/bash&lt;/span&gt;

&lt;span class="c"&gt;# print outputs and exit on first failure&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-xe&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$TRAVIS_BRANCH&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"master"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;

    &lt;span class="c"&gt;# setup ssh agent, git config and remote&lt;/span&gt;
    &lt;span class="nb"&gt;eval&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;ssh-agent &lt;span class="nt"&gt;-s&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    ssh-add ~/.ssh/travis_rsa
    git remote add deploy &lt;span class="s2"&gt;"travis@webhost.planecq.xyz:/var/www/planecq.com"&lt;/span&gt;
    git config user.name &lt;span class="s2"&gt;"Travis CI"&lt;/span&gt;
    git config user.email &lt;span class="s2"&gt;"travis@planecq.com"&lt;/span&gt;

    &lt;span class="c"&gt;# commit compressed files and push it to remote&lt;/span&gt;
    &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; .gitignore
    &lt;span class="nb"&gt;cp&lt;/span&gt; .travis/deployignore .gitignore
    git add &lt;span class="nb"&gt;.&lt;/span&gt;
    git status &lt;span class="c"&gt;# debug&lt;/span&gt;
    git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Deploy compressed files"&lt;/span&gt;
    git push &lt;span class="nt"&gt;-f&lt;/span&gt; deploy HEAD:master

&lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$TRAVIS_BRANCH&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"staging"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;

    &lt;span class="c"&gt;# setup ssh agent, git config and remote&lt;/span&gt;
    &lt;span class="nb"&gt;eval&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;ssh-agent &lt;span class="nt"&gt;-s&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    ssh-add ~/.ssh/travis_rsa
    git remote add deploy &lt;span class="s2"&gt;"travis@webhost.planecq.xyz:/var/www/planecq.xyz"&lt;/span&gt;
    git config user.name &lt;span class="s2"&gt;"Travis CI"&lt;/span&gt;
    git config user.email &lt;span class="s2"&gt;"travis@planecq.com"&lt;/span&gt;

    &lt;span class="c"&gt;# commit compressed files and push it to remote&lt;/span&gt;
    &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; .gitignore
    &lt;span class="nb"&gt;cp&lt;/span&gt; .travis/deployignore .gitignore
    git add &lt;span class="nb"&gt;.&lt;/span&gt;
    git status &lt;span class="c"&gt;# debug&lt;/span&gt;
    git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Deploy compressed files"&lt;/span&gt;
    git push &lt;span class="nt"&gt;-f&lt;/span&gt; deploy HEAD:master

&lt;span class="k"&gt;else

    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"No deploy script for branch '&lt;/span&gt;&lt;span class="nv"&gt;$TRAVIS_BRANCH&lt;/span&gt;&lt;span class="s2"&gt;'"&lt;/span&gt;

&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;.travis/deployignore&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This file replaces &lt;code&gt;.gitignore&lt;/code&gt; for deploy only and lets the following files to be committed and deployed (files generated by gulp build and normally ignored in the repository):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gulp/
index.min.html
*.gz
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



</description>
      <category>digitalocean</category>
      <category>travis</category>
      <category>devops</category>
      <category>staticwebsite</category>
    </item>
  </channel>
</rss>
