<?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>Forem: Mike Siegiel</title>
    <description>The latest articles on Forem by Mike Siegiel (@msiegiel).</description>
    <link>https://forem.com/msiegiel</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%2F293237%2F16bfb950-cef8-45a0-bdcf-a39c64a8f8cc.jpg</url>
      <title>Forem: Mike Siegiel</title>
      <link>https://forem.com/msiegiel</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/msiegiel"/>
    <language>en</language>
    <item>
      <title>Server side code highlighting in Node</title>
      <dc:creator>Mike Siegiel</dc:creator>
      <pubDate>Fri, 27 Dec 2019 20:56:04 +0000</pubDate>
      <link>https://forem.com/msiegiel/server-side-code-highlighting-in-node-1689</link>
      <guid>https://forem.com/msiegiel/server-side-code-highlighting-in-node-1689</guid>
      <description>&lt;h2&gt;
  
  
  What we are building
&lt;/h2&gt;

&lt;p&gt;A simple server side Node / Express code to transform Markdown content into a fully formatted HTML with highlighted code blocks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Technologies we use
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Markdown"&gt;Markdown&lt;/a&gt; is a lightweight markup language with plain text formatting syntax. Its design allows it to be converted to many output formats.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/highlightjs/highlight.js"&gt;Highlight.js&lt;/a&gt; is a syntax highlighter written in JavaScript. It works in the browser as well as on the server. It works with pretty much any markup, doesn’t depend on any framework, and has automatic language detection.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/unifiedjs/unified"&gt;Unified&lt;/a&gt; is a friendly interface backed by an ecosystem of plugins built for creating and manipulating content.&lt;/p&gt;

&lt;p&gt;Unified plugins: &lt;a href="https://github.com/remarkjs/remark/tree/master/packages/remark-parse"&gt;remark-parse&lt;/a&gt;, &lt;a href="https://github.com/remarkjs/remark-rehype"&gt;remark-rehype&lt;/a&gt;, &lt;a href="https://github.com/rehypejs/rehype/tree/master/packages/rehype-stringify"&gt;rehype-stringify&lt;/a&gt;, &lt;a href="https://github.com/rehypejs/rehype-highlight"&gt;rehype-highlight&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction - why use server side code highlighting
&lt;/h2&gt;

&lt;p&gt;At &lt;a href="https://regbrain.com"&gt;Regbrain&lt;/a&gt; we decided to implement server side code highlighting to boost loading time of our main website. We constantly benchmark our website with &lt;a href="https://developers.google.com/web/tools/lighthouse"&gt;Lighthouse&lt;/a&gt; and aim for top performance scores. &lt;/p&gt;

&lt;p&gt;Loading JavaScript to highlight code in the browser was taking too much time. First, the JavaScript files had to be fetched and then the browser was repainting the content resulting in a slower website. To improve speed, we decided to implement code highlighting on a server and now we send fully formatted text to the browser.&lt;/p&gt;

&lt;p&gt;At this point, you may be wondering, &lt;strong&gt;how is highlighting code server side performant?&lt;/strong&gt;  We will explore that in more details later on, but first, let's walk through our technical solution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Server side code highlighting
&lt;/h2&gt;

&lt;p&gt;Our articles are written in markdown so our workflow needs to take raw markdown as input and serve a fully formatted html. We do it in the following steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Fetch markdown content&lt;/li&gt;
&lt;li&gt;Transform markdown into a markdown syntax tree using &lt;strong&gt;remark-parse&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Transform markdown syntax tree to html syntax tree using &lt;strong&gt;remark-rehype&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Traverse html syntax tree to apply code highlighting to content within &lt;code&gt;&amp;lt;code&amp;gt;&lt;/code&gt; tags using &lt;strong&gt;rehype-highlight&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Transform html syntax tree to string to send to the client using &lt;strong&gt;rehype-stringify&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We achieve all the above with &lt;strong&gt;unified&lt;/strong&gt; framework and plugins as follows:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Import required libraries&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We grab the unified framework and the required plugins&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;let&lt;/span&gt; &lt;span class="nx"&gt;unified&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;unified&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;markdown&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;remark-parse&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;remark2rehype&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;remark-rehype&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;highlight&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;rehype-highlight&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;html&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;rehype-stringify&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Create a unified processor&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We create a processor which pipes together all the plugins above to achieve our chain of transformations from markdown to fully highlighted html:&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;let&lt;/span&gt; &lt;span class="nx"&gt;processor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;unified&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="c1"&gt;// Transform markdown into a markdown syntax tree&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;markdown&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;// Transform markdown syntax tree to html syntax tree&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;remark2rehype&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;// Traverse html syntax tree to apply code highlighting to content within code tags&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;highlight&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;// Transform html syntax tree to string to send to the client&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;html&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;Transform!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We now have the &lt;strong&gt;processor&lt;/strong&gt; which can parse any markdown input as follows:&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;let&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;some&lt;/span&gt; &lt;span class="nx"&gt;markdown&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;processor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&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;Express js router implementation example&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We implement the above steps in our &lt;a href="https://expressjs.com/"&gt;Express&lt;/a&gt; app as follows:&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;let&lt;/span&gt; &lt;span class="nx"&gt;express&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;express&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Router&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;unified&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;unified&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;markdown&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;remark-parse&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;remark2rehype&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;remark-rehype&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;html&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;rehype-stringify&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;highlight&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;rehype-highlight&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/:slug&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&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;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;markdown&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;processor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;unified&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;markdown&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;remark2rehype&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;highlight&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;processor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;article&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;output&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="nx"&gt;router&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Don't forget the CSS
&lt;/h2&gt;

&lt;p&gt;The last thing we need to do is to include highlight css styles on our pages. The easiest way would be to simply link them as external styles, but that would impair our website loading speed as fetching external styles blocks page rendering. To avoid the performance penalty, we include all css as an internal style on a page.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!doctype html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;{all&lt;/span&gt; &lt;span class="err"&gt;page's&lt;/span&gt; &lt;span class="err"&gt;style&lt;/span&gt; &lt;span class="err"&gt;including&lt;/span&gt; &lt;span class="err"&gt;highlightjs&lt;/span&gt; &lt;span class="err"&gt;css&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  The performance question revisited
&lt;/h2&gt;

&lt;p&gt;How do we make server side rendering performant? Even though the above code highlighting slows down our server a little bit compared to sending 'clean' html, we implement a number of additional layers below which allow us achieve excellent page loading speed:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AMP&lt;/strong&gt; - we serve our main content as AMP pages by default. That means that Google and Bing can cache our pages and serve it really fast on mobile devices.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No external styles or JavaScript (other than async AMP)&lt;/strong&gt; - we do not use any blocking external resources such as styles, images or JavaScript files. This is already enforced by following the AMP specification, but even if we did not implement AMP, this would a good approach to take to improve page load speed. All our css is internal. We prepare css server side and make it specific to the type of content that we serve to avoid including unused styles (...within reason...).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Minification&lt;/strong&gt; - we use css and html minification to further reduce the size of our pages.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CDN&lt;/strong&gt; - we use a global content distribution network and &lt;a href="https://regbrain.com/article/cache-headers-express-js"&gt;configure our HTTP headers to get benefits of CDN caching&lt;/a&gt;, we also configure asset compression for our CDN.&lt;/p&gt;

&lt;p&gt;With the set up above we can serve even ten &lt;a href="https://regbrain.com/article/node-nginx-ec2"&gt;Express apps on the smallest AWS EC2 instance&lt;/a&gt;, which works out to be really cost attractive compared to various options of hosting individual apps separately as a service. &lt;/p&gt;

</description>
      <category>node</category>
      <category>express</category>
      <category>javascript</category>
      <category>markdown</category>
    </item>
    <item>
      <title>Node app with Nginx on Amazon EC2</title>
      <dc:creator>Mike Siegiel</dc:creator>
      <pubDate>Sun, 15 Dec 2019 15:58:31 +0000</pubDate>
      <link>https://forem.com/msiegiel/node-app-with-nginx-on-amazon-ec2-50mo</link>
      <guid>https://forem.com/msiegiel/node-app-with-nginx-on-amazon-ec2-50mo</guid>
      <description>&lt;p&gt;How to launch a Node js app behind an Nginx proxy on Amazon EC2 instance in a step by step walkthrough.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two things before we start
&lt;/h2&gt;

&lt;p&gt;You need a few things before we start the launch:&lt;/p&gt;

&lt;p&gt;1) &lt;strong&gt;Node js app in a git repo&lt;/strong&gt; - make sure your Node app is ready and available to clone from a git repo&lt;/p&gt;

&lt;p&gt;2) &lt;strong&gt;AWS account&lt;/strong&gt; - if you don't have one yet, you can sign up &lt;a href="https://aws.amazon.com"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That's it, we are ready to roll!&lt;/p&gt;

&lt;h2&gt;
  
  
  Launch an EC2 instance
&lt;/h2&gt;

&lt;p&gt;1) Sign in to your AWS management console&lt;/p&gt;

&lt;p&gt;2) Go to &lt;strong&gt;EC2&lt;/strong&gt; service&lt;/p&gt;

&lt;p&gt;3) Go to &lt;strong&gt;Launch instance&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;4) Select the first one from the list: &lt;strong&gt;Amazon Linux 2 AMI&lt;/strong&gt; for the 64-bit (x86) - basically the first default option&lt;/p&gt;

&lt;p&gt;5) Select instance type. Assuming you are running a simple app then go for the cheapest instance: &lt;strong&gt;t3a.nano&lt;/strong&gt;. Click &lt;strong&gt;Review and launch&lt;/strong&gt; as we are blasting through the complexity here and will take all the default setting and only change the few we need later. On the next screen confirm again by clicking &lt;strong&gt;Launch&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;6) You will be asked to select an existing key pair or create a new key pair - it doesn't matter, we will be using a web based SSH client from AWS console to select any option. Click &lt;strong&gt;Launch Instances&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;7) Click &lt;strong&gt;View Instances&lt;/strong&gt; and go make yourself a cup of coffee while AWS is launching your new instance. It will take a few minutes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Set up security groups
&lt;/h2&gt;

&lt;p&gt;As your instance is being launched, you need to configure AWS security groups so that you and your website visitors will be able to access your app (and you will be able to connect to it via SSH).&lt;/p&gt;

&lt;p&gt;1) On EC2 Dashboard, find your new instance and scroll the view to the right to see what Security Group your instance is in. Remember the name of &lt;strong&gt;the security group&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;2) In the left hand side menu find and go to &lt;strong&gt;Security Groups&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;3) Select the security group and from 'Actions' select &lt;strong&gt;Edit inbound rules&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;4) In inbound rules you want to add the following two rules: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Type: SSH, Source: Anywhere&lt;/li&gt;
&lt;li&gt;Type: HTTP Source: Anywhere
Click &lt;strong&gt;Save&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;5) Go back to &lt;strong&gt;Instances&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Connect to your instance via SSH
&lt;/h2&gt;

&lt;p&gt;Ok, so you got your coffee and you can see your new instance status is 'running' and the status checks are green on the AWS EC2 Dashboard? Let's go then and connect to the instance via SSH:&lt;/p&gt;

&lt;p&gt;1) From the AWS EC2 Instances dashboard, select your instance, press &lt;strong&gt;Connect&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;2) From connection options choose &lt;strong&gt;EC2 Instance Connect (browser-based SHH connection)&lt;/strong&gt; - this is the easiest and fastest option with no config required!&lt;/p&gt;

&lt;h2&gt;
  
  
  Install Node, git, yarn and PM2 to run your app
&lt;/h2&gt;

&lt;p&gt;Now the fun begins, we will blast through the installation of all the software you need. &lt;strong&gt;You will now be working in your SSH terminal&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;1) Update the system with the latest packages and basic environment&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;yum update
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;2) Install nvm in order to install Node in the next step:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-o-&lt;/span&gt; https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.1/install.sh | bash
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;3) Close and re-open your SSH  console for the change to take effect&lt;/p&gt;

&lt;p&gt;4) Install Node&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;nvm &lt;span class="nb"&gt;install &lt;/span&gt;node
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;5) Install Git&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;yum &lt;span class="nb"&gt;install &lt;/span&gt;git
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;6) Install yarn. You are wondering &lt;strong&gt;why not an npm&lt;/strong&gt;? Here is an article &lt;a href="https://engineering.fb.com/web/yarn-a-new-package-manager-for-javascript/"&gt;why you should use yarn&lt;/a&gt;. Bottom line, yarn is quicker and more resilient.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-o-&lt;/span&gt; &lt;span class="nt"&gt;-L&lt;/span&gt; https://yarnpkg.com/install.sh | bash
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;7) Re-open the terminal window for changes to take effect&lt;/p&gt;

&lt;p&gt;8) Clone your Node app from your git repo&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/&amp;lt;username&amp;gt;/&amp;lt;repository-name&amp;gt;.git &amp;lt;folder-name&amp;gt;
&lt;span class="nt"&gt;--&lt;/span&gt; or &lt;span class="nt"&gt;--&lt;/span&gt;
git clone https://&amp;lt;username&amp;gt;@bitbucket.org/&amp;lt;username&amp;gt;/&amp;lt;repository-name&amp;gt;.git
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;9) Check if it's there&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;ls&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;10) Do you see a new folder with your app? Great, go to your app folder&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; &amp;lt;your-app-folder&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;11) Install your app packages&lt;br&gt;
&lt;/p&gt;

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



&lt;p&gt;12) Install Node process manager to run your node app as a service.&lt;/p&gt;

&lt;p&gt;You need a &lt;strong&gt;Node process manager&lt;/strong&gt; so that it takes care of automatically restarting and reloading your app when something goes wrong. We will use PM2 - a very popular and production ready process manager.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn global add pm2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;13) Launch your node app with the PM2 process manager. The example below assumes your app starts via index.js. Replace index.js with another file, e.g. app.js or server.js depending on how you would normally start your app in Node.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;pm2 start index.js &lt;span class="nt"&gt;--name&lt;/span&gt; my-app
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;14) Make PM2 automatically restart your app if something goes wrong&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;pm2 startup
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Retype the command the console asks you for in order to create the start up configuration. &lt;strong&gt;You need to re-type it as copy-paste does not really work in the console!&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;It is a bit painful but make sure you get this one right as otherwise PM2 will not re-start.&lt;/p&gt;

&lt;p&gt;Now save the PM2 set up:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;pm2 save
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Set up Nginx to run your app behind the proxy
&lt;/h2&gt;

&lt;p&gt;1) 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;yum &lt;span class="nb"&gt;install &lt;/span&gt;nginx
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You will get a warning command to install AWS curated Nginx package - great, that's what we need!&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;amazon-linux-extras &lt;span class="nb"&gt;install &lt;/span&gt;nginx1.12
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;2) Edit Nginx config to redirect HTTP traffic from port :80 to the port your app is running on the local host.&lt;/p&gt;

&lt;p&gt;In the example below I assumed your app is running on port 3000. If your app is running on a different port make sure to reflect that in the line of code &lt;code&gt;proxy_pass http://127.0.0.1:3000;&lt;/code&gt; in the configuration below:&lt;/p&gt;

&lt;p&gt;Open the editor:&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;nano /etc/nginx/nginx.conf
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;and edit the config file to contain the following server block (leave everything else as is):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="kn"&gt;listen&lt;/span&gt;         &lt;span class="mi"&gt;80&lt;/span&gt; &lt;span class="s"&gt;default_server&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="kn"&gt;listen&lt;/span&gt;         &lt;span class="s"&gt;[::]:80&lt;/span&gt; &lt;span class="s"&gt;default_server&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="kn"&gt;server_name&lt;/span&gt;    &lt;span class="s"&gt;localhost&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="kn"&gt;root&lt;/span&gt;           &lt;span class="n"&gt;/usr/share/nginx/html&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="kn"&gt;proxy_pass&lt;/span&gt; &lt;span class="s"&gt;http://127.0.0.1:3000&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;Just in case you are new to the &lt;a href="https://www.nano-editor.org/"&gt;nano editor&lt;/a&gt; - you can press &lt;code&gt;Ctr + X&lt;/code&gt; to finish editing and you will be prompt whether to save the file.&lt;/p&gt;

&lt;p&gt;3) Restart 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;service nginx restart
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;4) Set up Nginx to restart automatically if something goes wrong&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;chkconfig nginx on
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;You are done!&lt;/strong&gt; Up and running. &lt;/p&gt;

&lt;p&gt;Go to your AWS dashboard with EC2 instances and find the &lt;strong&gt;Public DNS (IPv4)&lt;/strong&gt; for your new instance. If you copy the url into the browser you should be able to see the output from your Node app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next steps
&lt;/h2&gt;

&lt;p&gt;1) You still need to think how to set up a proper &lt;strong&gt;domain&lt;/strong&gt; for your app&lt;/p&gt;

&lt;p&gt;I like to use AWS Route 53 and Cloudfront CDN to forward the traffic into the EC2 instance. That gives me resilience, free SSLs certificates and HTTPs redirects without having to worry about further Nginx configurations and management of certificate on the EC2 instance.&lt;/p&gt;

&lt;p&gt;2) You need to configure your &lt;strong&gt;Nginx proxy headers&lt;/strong&gt; and learn more about the &lt;code&gt;server&lt;/code&gt; and &lt;code&gt;location&lt;/code&gt; blocks configurations.&lt;/p&gt;

&lt;p&gt;3) You probably want to set up a Git repo with your &lt;strong&gt;Nginx configuration&lt;/strong&gt; files so that you can edit them in a proper code editor and than just pull their latest versions into your EC2 instance&lt;/p&gt;

&lt;p&gt;4) Use a &lt;strong&gt;proper SSH&lt;/strong&gt; client like &lt;a href="https://www.putty.org/"&gt;PuTTY&lt;/a&gt; from your local machine. It will be a bit faster and smoother experience than the web client via AWS Console&lt;/p&gt;

&lt;p&gt;5) This about the &lt;a href="https://regbrain.com/article/cache-headers-express-js"&gt;caching strategy for your app&lt;/a&gt;&lt;/p&gt;

</description>
      <category>node</category>
      <category>ec2</category>
      <category>nginx</category>
      <category>aws</category>
    </item>
  </channel>
</rss>
