<?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: Djoe Pramono</title>
    <description>The latest articles on DEV Community by Djoe Pramono (@djoepramono).</description>
    <link>https://dev.to/djoepramono</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%2F275794%2F8c44920d-027b-4c4e-8c79-7a3a885fe8f0.jpeg</url>
      <title>DEV Community: Djoe Pramono</title>
      <link>https://dev.to/djoepramono</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/djoepramono"/>
    <language>en</language>
    <item>
      <title>A Case Study of Date and Time</title>
      <dc:creator>Djoe Pramono</dc:creator>
      <pubDate>Sat, 08 Aug 2020 06:34:49 +0000</pubDate>
      <link>https://dev.to/djoepramono/a-case-study-of-date-and-time-4e0g</link>
      <guid>https://dev.to/djoepramono/a-case-study-of-date-and-time-4e0g</guid>
      <description>&lt;p&gt;In IT, dealing with date and time, can be quite challenging. There are a lot of things to consider and things can be quite complicated especially when you need to make changes to an already existing system.&lt;/p&gt;

&lt;p&gt;The purpose of this blog post is to surface the importance of thinking about your data type more carefully, especially when you are dealing with date and time.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Basic
&lt;/h2&gt;

&lt;p&gt;To limit the scope, we would only talk about three data types&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;DATE&lt;/code&gt;, which represents a date&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DATETIME&lt;/code&gt;, which represents a date with a time component&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;TIMESTAMP&lt;/code&gt;, which represents a date, time and a timezone offset &lt;/li&gt;
&lt;/ul&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Sample&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DATE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;2020-02-23&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DATETIME&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;2020-02-23 18:09:18&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;TIMESTAMP&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;2020-02-23 18:09:18+00&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Pretty simple eh? But sometimes some people forgot that only &lt;code&gt;TIMESTAMP&lt;/code&gt; can represent an absolute value of time. Depending on the function you use to deal with  &lt;code&gt;DATE&lt;/code&gt; and &lt;code&gt;DATETIME&lt;/code&gt;, it may give you a different absolute value.&lt;/p&gt;

&lt;h2&gt;
  
  
  Case Study
&lt;/h2&gt;

&lt;p&gt;You live in Melbourne (UTC+10) and you just inherited and are now responsible for a legacy database that stores user registrations in &lt;code&gt;DATETIME&lt;/code&gt; format. One day, an account manager asks you to graph the user registrations for the last 1 month.&lt;/p&gt;

&lt;p&gt;It sounds like a simple task, but if you have been dealing with data that contains all sort of time value for a long time. There are a few alarms bells that should get you on your toes. Let's pretend that you are having this conversation with your colleague, John.&lt;/p&gt;

&lt;h3&gt;
  
  
  "Since there is no timezone explicitly stored, the timezone for the data should be UTC or UTC+10. We just need to check the configuration"
&lt;/h3&gt;

&lt;p&gt;Yes, you could check the timezone of your host machine. You also need to check if your database takes the host machine timezone configuration into account, or if it has its own configuration. But it's not that simple.&lt;/p&gt;

&lt;p&gt;Since we have been saving the registration in &lt;code&gt;DATETIME&lt;/code&gt; you cannot 100% sure that when the data was stored, it was at the &lt;code&gt;DATETIME&lt;/code&gt; + the current timezone config. Who knows the timezone settings was changed a month ago. If an event data is stored without representing its absolute value, some of its data is  lost.&lt;/p&gt;

&lt;h3&gt;
  
  
  "We should've stored the registration time in UTC"
&lt;/h3&gt;

&lt;p&gt;This is a bit misleading, UTC is not a type, &lt;code&gt;TIMESTAMP&lt;/code&gt; is. 2020-02-23 18:09:18+00 UTC time is the same as 2020-02-24 04:09:18+10 Melbourne time.&lt;/p&gt;

&lt;p&gt;Most of the time when people say this, they should've said "&lt;em&gt;we should've stored the user registration in timestamp format&lt;/em&gt;". When storing a timestamp, you don't have to always store it in UTC. UTC or UTC+10 are just a display format.&lt;/p&gt;

&lt;p&gt;However, choosing the right display format is still important. Especially because, when you pass the data across to the next micro service or from backend to frontend, this &lt;code&gt;DATETIME&lt;/code&gt; would be transmitted as a &lt;code&gt;STRING&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  "Fine, but it's a common sense to store all time related field in &lt;code&gt;TIMESTAMP&lt;/code&gt; "
&lt;/h3&gt;

&lt;p&gt;In this particular case, yes. It's good to store an event in &lt;code&gt;TIMESTAMP&lt;/code&gt;. In fact, you MUST store an event as a &lt;code&gt;TIMESTAMP&lt;/code&gt;. &lt;code&gt;TIMESTAMP&lt;/code&gt; has the most information in it and it is meant to represent absolute value of a time. This is very useful if you want to do some analysis later.&lt;/p&gt;

&lt;p&gt;But calling to store every time related fields in a &lt;code&gt;TIMESTAMP&lt;/code&gt; is a bit of a stretch. There are situations where storing in &lt;code&gt;DATE&lt;/code&gt; or &lt;code&gt;DATETIME&lt;/code&gt; can be good. Let's start with the most obvious example: storing a user birthday. Should we store this in &lt;code&gt;TIMESTAMP&lt;/code&gt; ? Of course not, &lt;code&gt;DATE&lt;/code&gt; is better in this case.&lt;/p&gt;

&lt;p&gt;How about storing an appointment time? This is a tricky one, but in my opinion we should store it a &lt;code&gt;DATETIME&lt;/code&gt; format. Why? Because when making an appointment most people do not care about the timezone. Imagine that you try to make an appointment for tomorrow and there is a day light saving. Even when the timezone change, the date and time of the appointment does not. If the appointment is at 9 AM tomorrow, it will be held at 9 AM tomorrow regardless of the timezone offset.&lt;/p&gt;

&lt;p&gt;Sometimes timezone can even change with a very short notice just because a government decide to do so. There are case studies which you can read &lt;a href="https://codeofmatt.com/on-the-timing-of-time-zone-changes/"&gt;here&lt;/a&gt;. If what you are trying to record doesn't need to care about the timezone, record it in &lt;code&gt;DATETIME&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;So depending on the situation, we should choose carefully whether we should store data in &lt;code&gt;DATE&lt;/code&gt; , &lt;code&gt;DATETIME&lt;/code&gt;, or &lt;code&gt;TIMESTAMP&lt;/code&gt;. &lt;/p&gt;

&lt;h3&gt;
  
  
  "Actually for the graph, we would need to break the data into dates. But does that mean we will lose some information?"
&lt;/h3&gt;

&lt;p&gt;Yup, and that is fine. The raw event data can be stored in &lt;code&gt;TIMESTAMP&lt;/code&gt; but they can then be converted into &lt;code&gt;DATE&lt;/code&gt; for easier processing both by our brain and computer's logic. Losing some information, in this case time and timezone, is not necessarily a bad thing.&lt;/p&gt;

&lt;p&gt;Let's say that the user registrations are stored in UTC timestamp, and it needs to be piped into a series of data transformation pipeline. In this case we need to take into account what is the final product that come out in the end. If we need to aggregating the events based on Melbourne dates, then it might make more sense to extract all of the required information from the raw data table into another table and transform the &lt;code&gt;TIMESTAMP&lt;/code&gt; into &lt;code&gt;DATE&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Just make sure when transforming &lt;code&gt;TIMESTAMP&lt;/code&gt; into &lt;code&gt;DATE&lt;/code&gt;, take the timezone into consideration. We must not take a shortcut, where we turn &lt;code&gt;DATETIME&lt;/code&gt; into a &lt;code&gt;STRING&lt;/code&gt; and truncate it to get the date. Instead we should use the built in functions, to get the &lt;code&gt;DATE&lt;/code&gt; value out of &lt;code&gt;TIMESTAMP&lt;/code&gt; . The catch though, the syntax might differ from one database to another.&lt;/p&gt;

&lt;p&gt;This makes the next step in the pipeline easier, since they only need to worry about &lt;code&gt;DATE&lt;/code&gt; and they don't need to do the &lt;code&gt;DATETIME&lt;/code&gt; to &lt;code&gt;DATE&lt;/code&gt; transformation from then on.&lt;/p&gt;

&lt;h3&gt;
  
  
  "But then what happen if we actually needs the time component?" 
&lt;/h3&gt;

&lt;p&gt;Then we need to go back all the way to the raw data table. We must not grab the data from the derived table. &lt;/p&gt;

&lt;p&gt;One thing that could help here is to have a derived table that stores the event in &lt;code&gt;DATETIME&lt;/code&gt;. This would help some analysis like how many users registered in the morning. But of course this depends on the business requirement. Keep the full data for as long as we need it. Once we don't, shed things that we don't need to make things lightweight for the next step.&lt;/p&gt;

&lt;p&gt;When dealing with a data pipeline, it's better to spend sometime understanding the end-to-end process including the data type used as an input or output at every steps.&lt;/p&gt;

&lt;p&gt;Be careful when trying to compare two different types. For example, when the graph that you build for the account managers suddenly need to have a date input where it is used to graph the registration time of a user for a given date. Then when building the solution you should first convert the &lt;code&gt;DATE&lt;/code&gt; input into two &lt;code&gt;TIMESTAMP&lt;/code&gt; . One representing the beginning of the day and another one representing the end of the day. In other words you need a range. Only when you have the same type between data and input parameter, in this case &lt;code&gt;TIMESTAMP&lt;/code&gt;, you can then proceed to the calculation.&lt;/p&gt;

&lt;p&gt;Alternatively you can convert all of the registration timestamps into dates and store it in an intermediate table. Then you can use the input without any transformation and the calculation shall proceed in &lt;code&gt;DATE&lt;/code&gt; format.&lt;/p&gt;

&lt;h3&gt;
  
  
  "How about the frontend?"
&lt;/h3&gt;

&lt;p&gt;It is similar. You display the data in a display format that is suitable for its purpose. You can display a date for a birthday, a date and time for a booking time, and a timestamp for an event log.&lt;/p&gt;

&lt;p&gt;Please make sure that you use a library to display these especially when the data source is a timestamp in &lt;code&gt;STRING&lt;/code&gt; format. Do not ever try to process it yourself. Let the library help you to convert &lt;code&gt;STRING&lt;/code&gt; into the appropriate display with the correct timezone settings. &lt;/p&gt;

&lt;p&gt;Note that there are two timezone settings that you need to be aware of. One is the user machine settings and another one is the browser settings. These two settings can have different timezone setup&lt;/p&gt;

&lt;h3&gt;
  
  
  "And that's it I guess"
&lt;/h3&gt;

&lt;p&gt;Yes, that's it for this blog. Thank you for reading and any feedbacks are welcomed.&lt;/p&gt;

</description>
      <category>systemdesign</category>
      <category>database</category>
      <category>sql</category>
      <category>architecture</category>
    </item>
    <item>
      <title>How to Publish NPM Packages with CircleCI</title>
      <dc:creator>Djoe Pramono</dc:creator>
      <pubDate>Mon, 03 Feb 2020 11:21:14 +0000</pubDate>
      <link>https://dev.to/djoepramono/how-to-publish-npm-packages-with-circleci-4hmj</link>
      <guid>https://dev.to/djoepramono/how-to-publish-npm-packages-with-circleci-4hmj</guid>
      <description>&lt;p&gt;I had been hearing good things about &lt;a href="https://circleci.com/"&gt;CircleCI&lt;/a&gt; for a while and I was itching to give it a try. Coincidentally, I published an NPM package not too long ago called &lt;a href="https://www.npmjs.com/package/@codeallnight/falidator"&gt;Falidator&lt;/a&gt;, a module to validate TypeScript objects in &lt;a href="https://medium.com/@djoepramono/how-to-validate-javascript-object-better-with-typescript-e43314d97f9c"&gt;a more functional way&lt;/a&gt;. So I thought, this is it, let's set up CircleCI for it.&lt;/p&gt;

&lt;p&gt;So far the experience has been great and their &lt;a href="https://circleci.com/blog/publishing-npm-packages-using-circleci-2-0/"&gt;guide&lt;/a&gt; is pretty good. Unfortunately some topics were not covered there, and as I set it up, more and more questions popped up along the way. Thus I decided to write a yet another guide/blog about publishing an NPM package with CircleCI. If you are just interested in seeing the final config, feel free to jump straight to the conclusion.&lt;/p&gt;

&lt;h1&gt;
  
  
  1. Default Sample Config and The First Few Changes
&lt;/h1&gt;

&lt;p&gt;The first few steps are nothing unusual&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a CircleCI account &lt;/li&gt;
&lt;li&gt;Authorise CircleCI to access the Github account, &lt;/li&gt;
&lt;li&gt;Go to dashboard. &lt;/li&gt;
&lt;li&gt;Click on &lt;code&gt;Add new project&lt;/code&gt;, &lt;/li&gt;
&lt;li&gt;Select the NPM package repository, &lt;/li&gt;
&lt;li&gt;Select Node in the dropdown and we will be greeted with a sample barebone config.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="nx"&gt;jobs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="nx"&gt;build&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nx"&gt;docker&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;circleci&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;7.10&lt;/span&gt;

    &lt;span class="nx"&gt;working_directory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="sr"&gt;/rep&lt;/span&gt;&lt;span class="err"&gt;o
&lt;/span&gt;
    &lt;span class="nx"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;checkout&lt;/span&gt;

      &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;restore_cache&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
          &lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;v1&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;dependencies&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="nx"&gt;checksum&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;package.json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;
            &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;v1&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;dependencies&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;

      &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;yarn&lt;/span&gt; &lt;span class="nx"&gt;install&lt;/span&gt;

      &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;save_cache&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
          &lt;span class="nx"&gt;paths&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;node_modules&lt;/span&gt;
          &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;v1&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;dependencies&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="nx"&gt;checksum&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;package.json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;

      &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;yarn&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This config needs to be copied to &lt;code&gt;.circleci/config.yml&lt;/code&gt; and once committed, we can click &lt;code&gt;Start Building&lt;/code&gt;, but before that let's make some changes first. &lt;/p&gt;

&lt;p&gt;The docker image is quite old. Thankfully CircleCI has a wide range of docker images available for us to use. Most of the time, changing the version to the desired value is enough. But to be certain, we can check &lt;a href="https://circleci.com/docs/2.0/circleci-images/#nodejs"&gt;here&lt;/a&gt; or &lt;a href="https://circleci.com/docs/2.0/docker-image-tags.json"&gt;here&lt;/a&gt; for more complete set.&lt;/p&gt;

&lt;p&gt;Leave the &lt;code&gt;working_directory&lt;/code&gt; as is. This is where the code will be checked out and where the CI process will take place. Since I use &lt;code&gt;npm&lt;/code&gt; instead of &lt;code&gt;yarn&lt;/code&gt;, I change the &lt;code&gt;run&lt;/code&gt; configs to &lt;code&gt;npm install&lt;/code&gt; and &lt;code&gt;npm run test&lt;/code&gt; respectively.&lt;/p&gt;

&lt;h1&gt;
  
  
  2. Utilising CircleCI Cache
&lt;/h1&gt;

&lt;p&gt;How about &lt;code&gt;restore_cache&lt;/code&gt; and &lt;code&gt;save_cache&lt;/code&gt;? As we know, installing npm modules can take a while, especially if we have tons of them. The idea here is to save the state after we install them and re-use that state in the next run. The cache is stored for up to 30 days. It's good enough especially if we have multiple builds in a day with minimal changes to the dependencies.&lt;/p&gt;

&lt;h2&gt;
  
  
  What happen behind saving and restoring cache
&lt;/h2&gt;

&lt;p&gt;Let's talk about the &lt;code&gt;save_cache&lt;/code&gt; first. The config above basically says, save the state of &lt;code&gt;node_modules&lt;/code&gt; folder after &lt;code&gt;npm install&lt;/code&gt; and give it a key based on the checksum of &lt;code&gt;package.json&lt;/code&gt;. This means if our &lt;code&gt;package.json&lt;/code&gt; doesn't change the checksum value would stay the same.&lt;/p&gt;

&lt;p&gt;During &lt;code&gt;restore_cache&lt;/code&gt;, CircleCI would try to get the latest cache that match the key(s). In our settings, we are matching against 2 keys: &lt;code&gt;v1-dependencies-{{ checksum "package.json" }}&lt;/code&gt; and &lt;code&gt;v1-dependencies-&lt;/code&gt;. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;if none match the first key, the cache will then be matched against the second key. &lt;/li&gt;
&lt;li&gt;if multiple caches are found, the newest one is used, regardless if it's an exact match or partial match.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At first I was not sure if I should match the cache against multiple keys or just one key. I was afraid that the cache would cause more headache and not worth the time saved. But then, I realised &lt;code&gt;npm install&lt;/code&gt; will be run anyway regardless of the cache. It just means that if the cache is older, it may need to install more modules, whatever the cache doesn't have yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Immutable cache and how npm install works
&lt;/h2&gt;

&lt;p&gt;Cache in CircleCI are immutable. This means if we want to save a new cache we need to give it a different key. So I was wondering if putting the checksum of &lt;code&gt;package-lock.json&lt;/code&gt; instead of &lt;code&gt;package.json&lt;/code&gt; is a better idea. Well, let's have a look on how &lt;code&gt;npm install&lt;/code&gt; works. From what I know in npm version 6.12, these behaviours exists.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;npm install&lt;/code&gt; install modules based on package.json and &lt;em&gt;not&lt;/em&gt; package-lock.json&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;npm install&lt;/code&gt; does not install the latest version available but rather it matches the version specified in package.json&lt;/li&gt;
&lt;li&gt;If package.json and package-lock.json are out of sync, during &lt;code&gt;npm install&lt;/code&gt;, npm will attempt to correct package-lock.json based on package.json&lt;/li&gt;
&lt;li&gt;To check the installed and available versions of node modules, run &lt;code&gt;npm outdated&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;To update all of our node modules based on our package.json, run &lt;code&gt;npm update&lt;/code&gt;. If we just want to update one module, run &lt;code&gt;npm install &amp;lt;module-name&amp;gt;&lt;/code&gt;. Running either of this function will adjust our package.json and package-lock.json if a new version is found.&lt;/li&gt;
&lt;li&gt;Symbols in &lt;code&gt;package.json&lt;/code&gt; actually have a meaning. &lt;code&gt;^&lt;/code&gt; means the module can be updated (&lt;em&gt;not installed&lt;/em&gt;) to the latest minor version that satisfy the &lt;a href="https://semver.org/"&gt;semantic versioning&lt;/a&gt;. While &lt;code&gt;~&lt;/code&gt; means the module can be updated to the latest patch version that satisfy the semantic versioning.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Based on these behaviours, it makes more sense to use the checksum of package.json instead of package-lock.json as package.json seems to be &lt;strong&gt;the&lt;/strong&gt; source of everything node module. But hey check the npm version that you are using, and make sure you are using the latest node version if possible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Busting CircleCI cache
&lt;/h2&gt;

&lt;p&gt;What's the &lt;code&gt;v1&lt;/code&gt; in the config for? It's actually to bust the cache. Say we want to start fresh without any of the previous cache, we can change the name of cache key from &lt;code&gt;v1&lt;/code&gt; to &lt;code&gt;v2&lt;/code&gt; and there we have it, a clean slate as the key won't match.&lt;/p&gt;

&lt;p&gt;At one point I was thinking can I put &lt;code&gt;v1&lt;/code&gt; into environment variable e.g. &lt;code&gt;CACHE_VERSION&lt;/code&gt;? After all it seems that &lt;a href="https://circleci.com/docs/2.0/configuration-reference/#environment"&gt;it can be configured&lt;/a&gt; like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="nx"&gt;CACHE_VERSION&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
    &lt;span class="nx"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;restore_cache&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
          &lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CACHE_VERSION&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;dependencies&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="nx"&gt;checksum&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;package.json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;CACHE_VERSION&lt;/span&gt; &lt;span class="nx"&gt;environment&lt;/span&gt; &lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="nx"&gt;can&lt;/span&gt; &lt;span class="nx"&gt;only&lt;/span&gt; &lt;span class="nx"&gt;be&lt;/span&gt; &lt;span class="kd"&gt;set&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;web&lt;/span&gt; &lt;span class="nx"&gt;UI&lt;/span&gt; &lt;span class="nx"&gt;settings&lt;/span&gt;
            &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CACHE_VERSION&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;dependencies&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;fallback&lt;/span&gt; &lt;span class="nx"&gt;match&lt;/span&gt;
      &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;save_cache&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
          &lt;span class="nx"&gt;paths&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;node_modules&lt;/span&gt;
          &lt;span class="nx"&gt;key&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="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CACHE_VERSION&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;dependencies&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="nx"&gt;checksum&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;package.json&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;Turns out we cannot do it this way. Once run, the above config returns the following error&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="nx"&gt;computing&lt;/span&gt; &lt;span class="nx"&gt;cache&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cacheKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;executing&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cacheKey&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;at&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CACHE_VERSION&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;can&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;t evaluate field CACHE_VERSION in type cache.TemplateValues
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The job itself runs just fine, but no cache is used. I gave it another try, this time I put set environment variable in the &lt;a href="https://circleci.com/docs/2.0/contexts/#creating-and-using-a-context"&gt;context&lt;/a&gt;. Alas it's still not working. From &lt;a href="https://discuss.circleci.com/t/cannot-use-circle-yml-environment-variables-in-cache-keys/10994"&gt;here&lt;/a&gt;, I found out that it's not possible to use environment variable for the cache config. So I gave up and reverted everything to what it was without a variable.&lt;/p&gt;

&lt;h1&gt;
  
  
  3. Jobs and Workflow
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Jobs
&lt;/h2&gt;

&lt;p&gt;All right so far we know what a job is. It's basically a series of steps to be run consecutively by CircleCI e.g. checking out code, testing, linting etc. This is the config that I end up having.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;jobs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="nx"&gt;test_and_lint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;defaults&lt;/span&gt;
    &lt;span class="nx"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;checkout&lt;/span&gt;
      &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;restore_cache&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
          &lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;v1&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;dependencies&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="nx"&gt;checksum&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;package.json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;
            &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;v1&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;dependencies&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;fallback&lt;/span&gt; &lt;span class="nx"&gt;match&lt;/span&gt;
      &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;run&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;Install&lt;/span&gt; &lt;span class="nx"&gt;dependencies&lt;/span&gt;
          &lt;span class="nx"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;npm&lt;/span&gt; &lt;span class="nx"&gt;install&lt;/span&gt;
      &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;save_cache&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
          &lt;span class="nx"&gt;paths&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;node_modules&lt;/span&gt;
          &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;v1&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;dependencies&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="nx"&gt;checksum&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;package.json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;
      &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;npm&lt;/span&gt; &lt;span class="nx"&gt;run&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt;
      &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;npm&lt;/span&gt; &lt;span class="nx"&gt;run&lt;/span&gt; &lt;span class="nx"&gt;lint&lt;/span&gt;
      &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;persist_to_workspace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
          &lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="sr"&gt;/rep&lt;/span&gt;&lt;span class="err"&gt;o
&lt;/span&gt;          &lt;span class="nx"&gt;paths&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;
  &lt;span class="nx"&gt;build&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;defaults&lt;/span&gt;
    &lt;span class="nx"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;attach_workspace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
          &lt;span class="nx"&gt;at&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="sr"&gt;/rep&lt;/span&gt;&lt;span class="err"&gt;o
&lt;/span&gt;      &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;run&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;Build&lt;/span&gt; &lt;span class="kr"&gt;package&lt;/span&gt;
          &lt;span class="nx"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;npm&lt;/span&gt; &lt;span class="nx"&gt;run&lt;/span&gt; &lt;span class="nx"&gt;build&lt;/span&gt;
      &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;persist_to_workspace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
          &lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="sr"&gt;/rep&lt;/span&gt;&lt;span class="err"&gt;o
&lt;/span&gt;          &lt;span class="nx"&gt;paths&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;
  &lt;span class="nx"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;defaults&lt;/span&gt;
    &lt;span class="nx"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;attach_workspace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
          &lt;span class="nx"&gt;at&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="sr"&gt;/rep&lt;/span&gt;&lt;span class="err"&gt;o
&lt;/span&gt;      &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;run&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;Authenticate&lt;/span&gt; &lt;span class="kd"&gt;with&lt;/span&gt; &lt;span class="nx"&gt;registry&lt;/span&gt;
          &lt;span class="nx"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;echo&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;//registry.npmjs.org/:_authToken=$NPM_TOKEN&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="sr"&gt;/repo/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;npmrc&lt;/span&gt;
      &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;run&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;Publish&lt;/span&gt; &lt;span class="kr"&gt;package&lt;/span&gt;
          &lt;span class="nx"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;npm&lt;/span&gt; &lt;span class="nx"&gt;publish&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You might wonder why we don't run &lt;code&gt;npm run test&lt;/code&gt; and &lt;code&gt;npm run lint&lt;/code&gt; in one go like so &lt;code&gt;npm run test &amp;amp; npm run lint&lt;/code&gt;. This is because by splitting them into two separate steps, we could get more output printed in the CircleCi UI. &lt;/p&gt;

&lt;p&gt;What is &lt;code&gt;&amp;lt;&amp;lt;: *defaults&lt;/code&gt;? It's basically a shortcut to repeat configs. For example we need to keep repeating the docker image and working directory for each job. That's why we put it in the &lt;code&gt;defaults&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;defaults&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;defaults&lt;/span&gt;
  &lt;span class="nx"&gt;working_directory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="sr"&gt;/rep&lt;/span&gt;&lt;span class="err"&gt;o
&lt;/span&gt;  &lt;span class="nx"&gt;docker&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;circleci&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;12.13&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Workflows
&lt;/h2&gt;

&lt;p&gt;Workflow is how one or more jobs are laid out to work together. With workflow, you can set the jobs to be executed in certain order, stopped for &lt;code&gt;approval&lt;/code&gt;, or run only if a certain condition are met. For example, we can set a workflow like below to run &lt;code&gt;test_and_lint&lt;/code&gt; then run &lt;code&gt;build&lt;/code&gt; and &lt;code&gt;publish&lt;/code&gt; only if it's master branch.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;workflows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
  &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nx"&gt;jobs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;test_and_lint&lt;/span&gt;
      &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;build&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
          &lt;span class="nx"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nx"&gt;branches&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
              &lt;span class="nx"&gt;only&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;master&lt;/span&gt;
          &lt;span class="nx"&gt;requires&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;test_and_lint&lt;/span&gt;
      &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;hold&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
          &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;approval&lt;/span&gt;
          &lt;span class="nx"&gt;requires&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;build&lt;/span&gt;
      &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
          &lt;span class="nx"&gt;requires&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;hold&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This in turn enables branch protection. When I raise a pull request from my branch to master, there will be a CI check running. We can confirm this by going to the Github repository, &lt;code&gt;Settings&lt;/code&gt; --&amp;gt; &lt;code&gt;Branches&lt;/code&gt; --&amp;gt; &lt;code&gt;Branch Protection Rules&lt;/code&gt; --&amp;gt; &lt;code&gt;Require status checks to pass before merging&lt;/code&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  4. NPM Token, Tagging and Publishing
&lt;/h1&gt;

&lt;h2&gt;
  
  
  NPM Token
&lt;/h2&gt;

&lt;p&gt;Before we can publish, we need to have an NPM token ready. The token is created in &lt;a href="https://www.npmjs.com"&gt;https://www.npmjs.com&lt;/a&gt;, we need to&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create an account and login&lt;/li&gt;
&lt;li&gt;Click on the avatar&lt;/li&gt;
&lt;li&gt;Go to &lt;code&gt;Auth Token&lt;/code&gt; --&amp;gt; &lt;code&gt;Create New Token&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Copy the token into the clipboard&lt;/li&gt;
&lt;li&gt;Go back to Circle CI and pick the correct pipeline&lt;/li&gt;
&lt;li&gt;Go to &lt;code&gt;Settings&lt;/code&gt; --&amp;gt; &lt;code&gt;Build Settings&lt;/code&gt; --&amp;gt; &lt;code&gt;Environment Variables&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Create a new  environment variable &lt;code&gt;NPM_TOKEN&lt;/code&gt; and paste the token in&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By now you probably have noticed that in the previous topic, we have a step like so&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;        &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;run&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;Authenticate&lt;/span&gt; &lt;span class="kd"&gt;with&lt;/span&gt; &lt;span class="nx"&gt;registry&lt;/span&gt;
          &lt;span class="nx"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;echo&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;//registry.npmjs.org/:_authToken=$NPM_TOKEN&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="sr"&gt;/repo/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;npmrc&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;That's basically adding the token to the home directory, a requirement before we can run &lt;code&gt;npm publish&lt;/code&gt;. It baffles me as using environment variables is fine here but not when setting up cache keys.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tagging
&lt;/h2&gt;

&lt;p&gt;Another requirement before we can run &lt;code&gt;npm publish&lt;/code&gt; is that we need to version the package. The version needs to be incremented each time as well. This is tricky because it can be patch, minor, or major version. I was hoping that I could enter the new version during the &lt;code&gt;approval&lt;/code&gt; step, but alas I don't think it's possible. Thus I decided that the versioning shall happen locally on my dev machine and I'll push the tag up to GitHub and the workflow will pick up from there&lt;/p&gt;

&lt;p&gt;So locally on my dev machine&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;npm version patch &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"bump to 0.0.7"&lt;/span&gt;
git push origin &amp;lt;new_tag&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Meanwhile on the CircleCI, these needs to be setup&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;workflows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
  &lt;span class="nx"&gt;generic&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nx"&gt;jobs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;test_and_lint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;implicitly&lt;/span&gt; &lt;span class="nx"&gt;all&lt;/span&gt; &lt;span class="nx"&gt;jobs&lt;/span&gt; &lt;span class="nx"&gt;always&lt;/span&gt; &lt;span class="nx"&gt;run&lt;/span&gt; &lt;span class="nx"&gt;on&lt;/span&gt; &lt;span class="nx"&gt;branches&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;unless&lt;/span&gt; &lt;span class="nx"&gt;filtered&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
          &lt;span class="nx"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  
            &lt;span class="nx"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;required&lt;/span&gt; &lt;span class="nx"&gt;since&lt;/span&gt; &lt;span class="s2"&gt;`build`&lt;/span&gt; &lt;span class="nx"&gt;has&lt;/span&gt; &lt;span class="nx"&gt;tag&lt;/span&gt; &lt;span class="nx"&gt;filters&lt;/span&gt; &lt;span class="nx"&gt;AND&lt;/span&gt; &lt;span class="nx"&gt;requires&lt;/span&gt; &lt;span class="s2"&gt;`test_and_lint`&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
              &lt;span class="nx"&gt;only&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&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;*&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;      &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;build&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
          &lt;span class="nx"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  
            &lt;span class="nx"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;required&lt;/span&gt; &lt;span class="nx"&gt;since&lt;/span&gt; &lt;span class="s2"&gt;`publish`&lt;/span&gt; &lt;span class="nx"&gt;has&lt;/span&gt; &lt;span class="nx"&gt;tag&lt;/span&gt; &lt;span class="nx"&gt;filters&lt;/span&gt; &lt;span class="nx"&gt;AND&lt;/span&gt; &lt;span class="nx"&gt;requires&lt;/span&gt; &lt;span class="s2"&gt;`build`&lt;/span&gt;
              &lt;span class="nx"&gt;only&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&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;*&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;            &lt;span class="nx"&gt;branches&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;required&lt;/span&gt; &lt;span class="nx"&gt;since&lt;/span&gt; &lt;span class="nx"&gt;we&lt;/span&gt; &lt;span class="nx"&gt;don&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;t want to build on branches, just tags
              ignore: /.*/
          requires:
            - test_and_lint
      - publish:
          filters:
            tags:
              only: /^v.*/
            branches:
              ignore: /.*/
          requires:
            - build
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;What's with all of these filters? These are needed because these fundamental behaviours in CircleCI&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;By default, jobs are running on all branches.&lt;/li&gt;
&lt;li&gt;By default, jobs are &lt;strong&gt;not&lt;/strong&gt; running on all tags.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sounds simple, but combined with the execution order config, we end up with a rather nasty set of filters.&lt;/p&gt;

&lt;p&gt;Alternatively, since CircleCI allows us to have more than one workflow, we can set it up like below. We just need to make sure that only one workflow runs at any given time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;workflows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
  &lt;span class="nx"&gt;on_branch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nx"&gt;jobs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;test_and_lint&lt;/span&gt;
  &lt;span class="nx"&gt;on_tags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nx"&gt;jobs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;test_and_lint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
          &lt;span class="nx"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nx"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
              &lt;span class="nx"&gt;only&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&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;*&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;            &lt;span class="nx"&gt;branches&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
              &lt;span class="nx"&gt;ignore&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;      &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;build&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
          &lt;span class="nx"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nx"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
              &lt;span class="nx"&gt;only&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&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;*&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;          &lt;span class="nx"&gt;requires&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;test_and_lint&lt;/span&gt;
      &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
          &lt;span class="nx"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nx"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
              &lt;span class="nx"&gt;only&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&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;*&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;          &lt;span class="nx"&gt;requires&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;build&lt;/span&gt;

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



&lt;p&gt;Unfortunately as you can see, we still cannot get away from the filters due to how CircleCI works with tags. So which one is better? I slightly prefer the latter, but honestly I don't really like either of them. If any of you have a better solution, please let me know. For more reading, you can refer to &lt;a href="https://circleci.com/docs/2.0/workflows/#executing-workflows-for-a-git-tag"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  5. Conclusion
&lt;/h1&gt;

&lt;p&gt;That concludes my experience publishing an NPM package with CircleCI. Some pros:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Easy to setup and decent initial config&lt;/li&gt;
&lt;li&gt;Plenty of documentations&lt;/li&gt;
&lt;li&gt;Everything is configured through the config file with minimal web UI interaction&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But of course there are some things that could be improved&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Inconsistent experience dealing with environment variables in &lt;code&gt;command&lt;/code&gt;, &lt;code&gt;cache&lt;/code&gt;, and &lt;code&gt;context&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Workflows can be streamlined better. &lt;code&gt;tags&lt;/code&gt; and &lt;code&gt;branches&lt;/code&gt; can be made to use similar config pattern&lt;/li&gt;
&lt;li&gt;When you login to their website, you randomly get the new or old UI. There's a menu to go back to old UI from new UI. But unfortunately there's no way to go from old UI to new UI. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Overall CircleCI is a pretty good. The downsides are there, but they are not critical. CircleCI is easy to use and perfect for a small open source project like mine. If you'd like to see the full config, it's available in the GitHub &lt;a href="https://github.com/wecodeallnight/falidator/blob/master/.circleci/config.yml"&gt;repo&lt;/a&gt;. And that's it, thank you for reading. Feedbacks are welcomed :)&lt;/p&gt;

</description>
      <category>devops</category>
      <category>tutorial</category>
      <category>typescript</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Gatsby One Pager</title>
      <dc:creator>Djoe Pramono</dc:creator>
      <pubDate>Sun, 05 Jan 2020 07:49:16 +0000</pubDate>
      <link>https://dev.to/djoepramono/gatsby-one-pager-3eb8</link>
      <guid>https://dev.to/djoepramono/gatsby-one-pager-3eb8</guid>
      <description>&lt;p&gt;How to build a &lt;a href="https://www.gatsbyjs.org/"&gt;Gatsby&lt;/a&gt; site? Why the guides online are so fragmented? Isn't there a one pager guide for Gatsby with a working example? &lt;em&gt;Well&lt;/em&gt; you have found it. This one page guide would help you build a static site with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[x] Markdown based blog post&lt;/li&gt;
&lt;li&gt;[x] Client side search&lt;/li&gt;
&lt;li&gt;[x] Pagination&lt;/li&gt;
&lt;li&gt;[x] Code Highlighting&lt;/li&gt;
&lt;li&gt;[x] Google Analytics&lt;/li&gt;
&lt;li&gt;[x] Responsive design, &lt;em&gt;well we won't really cover this but you can have a look at the Github code.&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;See it in action on &lt;a href="https://www.codeallnight.com"&gt;https://www.codeallnight.com&lt;/a&gt; or take a peek at the &lt;a href="https://github.com/djoepramono/code-all-night"&gt;git repo&lt;/a&gt;. Feel free to build on top of it. Empty the &lt;code&gt;src/posts&lt;/code&gt; folder and start write your own.&lt;/p&gt;

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

&lt;p&gt;First thing first, install &lt;code&gt;gatsby-cli&lt;/code&gt; and clone &lt;a href="(https://github.com/djoepramono/code-all-night)"&gt;the repo&lt;/a&gt;. Cloning the repo is optional, but isn't it always nicer to have a code example at your disposal?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; gatsby-cli
git clone git@github.com:djoepramono/code-all-night.git
&lt;span class="nb"&gt;cd &lt;/span&gt;code-all-night
npm &lt;span class="nb"&gt;install
&lt;/span&gt;gatsby develop &lt;span class="nt"&gt;-H&lt;/span&gt; 0.0.0.0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Running &lt;code&gt;gatsby develop&lt;/code&gt; only, makes the site only avaiable on the host computer via localhost. But sometimes you want to make it accessible to your local network, so that you can test your site with your mobile phone. For this, you need the &lt;code&gt;-H 0.0.0.0&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Each section on this guide might depends on a specific npm package. These packages are already included in the repo &lt;code&gt;package.json&lt;/code&gt;. If you don't clone the repo and start fresh instead, make sure you install them.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Markdown Posts
&lt;/h2&gt;

&lt;p&gt;Markdown files can be made into pages in Gatsby with the help of &lt;a href="https://www.gatsbyjs.org/packages/gatsby-transformer-remark/"&gt;gatsby-transformer-remark&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Put your markdown files into &lt;code&gt;src/posts&lt;/code&gt;. &lt;em&gt;There are some examples there already&lt;/em&gt;. Next up, you need to put the following entry into &lt;code&gt;gatsby-node.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createPages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;graphql&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reporter&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createPage&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;actions&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;blogPostTemplate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`src/templates/post.js`&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;result&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;graphql&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`
    {
      allMarkdownRemark(
        sort: { order: DESC, fields: [frontmatter___date] }
        limit: 1000
      ) {
        edges {
          node {
            frontmatter {
              title
              date(formatString: "DD MMMM YYYY")
              author
              path
            }
            excerpt
            timeToRead
          }
        }
      }
    }
  `&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="c1"&gt;// Handle errors&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;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;reporter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;panicOnBuild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Error while running GraphQL query.`&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="c1"&gt;// Create post pages&lt;/span&gt;
  &lt;span class="nx"&gt;result&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;allMarkdownRemark&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;edges&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;node&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;createPage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;frontmatter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;blogPostTemplate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The code above is utilising Gatsby's &lt;code&gt;createPages&lt;/code&gt; API to create a static page for each markdown posts. Each of this markdown files can be enriched with &lt;code&gt;frontmatter&lt;/code&gt;, a set of key value pair that exists on top of each markdown file.&lt;/p&gt;

&lt;p&gt;Under the hood, Gatsby is using GraphQL, which you can read more &lt;a href="https://www.gatsbyjs.org/docs/graphql-concepts/"&gt;here&lt;/a&gt; . It also provides you with graphical UI client at &lt;a href="http://localhost:8000/__graphql"&gt;http://localhost:8000/__graphql&lt;/a&gt;. It's a pretty good tool to explore what queries are available to use.&lt;/p&gt;

&lt;p&gt;And if you want to change the template, you can change &lt;code&gt;src/templates/posts&lt;/code&gt;. It's a React component, so go nuts if you are already familiar with &lt;a href="https://reactjs.org/"&gt;React&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;All right, by now you should know what &lt;code&gt;createPages&lt;/code&gt; does.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Client Side Search
&lt;/h2&gt;

&lt;p&gt;Before we are talking about pagination, let's talk about search first. I am using &lt;a href="https://github.com/bvaughn/js-search"&gt;js-search&lt;/a&gt; to power the search page. The concept is quite simple, during the &lt;code&gt;post&lt;/code&gt; pages creation, we also want build the context for the search page. If you want to learn more, have a look at &lt;a href="https://www.gatsbyjs.org/docs/adding-search-with-js-search/"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In your &lt;code&gt;gatsby-node.js&lt;/code&gt;'s &lt;code&gt;createPages&lt;/code&gt;, put the following code&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;const&lt;/span&gt; &lt;span class="nx"&gt;posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;result&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;allMarkdownRemark&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;edges&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;transformRemarkEdgeToPost&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;createPage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/posts/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`./src/templates/clientSearch.js`&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;search&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;indexStrategy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Prefix match&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;searchSanitizer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Lower Case&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;TitleIndex&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="na"&gt;AuthorIndex&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="na"&gt;SearchByTerm&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="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;where &lt;code&gt;transformRemarkEdgeToPost&lt;/code&gt; is just simple data transformation 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;const&lt;/span&gt; &lt;span class="nx"&gt;transformRemarkEdgeToPost&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;edge&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;edge&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;frontmatter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;edge&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;frontmatter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;author&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;edge&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;frontmatter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;edge&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;frontmatter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;excerpt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;edge&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;excerpt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;timeToRead&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;edge&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timeToRead&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;The search here is a client side search. Meaning it doesn't talk to the server during the search as the javascript client already know the whole &lt;code&gt;context&lt;/code&gt;, which is passed into the pages via &lt;code&gt;createPages&lt;/code&gt;. This makes the search very responsive. Try it out!&lt;/p&gt;

&lt;p&gt;Now you hopefully you know the concept of passing data into pages via &lt;code&gt;context&lt;/code&gt;. As for the templates, it's using a custom &lt;a href="https://reactjs.org/docs/react-component.html"&gt;React class component&lt;/a&gt;, as it will need to use state. It's available in the repo at &lt;code&gt;src/components/clientSearch&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. List Page with Pagination
&lt;/h2&gt;

&lt;p&gt;Next up we are going to create a list page with pagination. The &lt;a href="https://www.gatsbyjs.org/docs/adding-pagination/"&gt;default Gatsby guide&lt;/a&gt; is good enough, but I went slightly further.&lt;/p&gt;

&lt;p&gt;Put the following into &lt;code&gt;gatsby-node.js&lt;/code&gt;'s &lt;code&gt;createPages&lt;/code&gt; function&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;const&lt;/span&gt; &lt;span class="nx"&gt;postsPerPage&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;noOfPostsPerPage&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;noOfPages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ceil&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;postsPerPage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;Array&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="na"&gt;length&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;noOfPages&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;forEach&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;i&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;createPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;createListPageParameter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="s2"&gt;`/list-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./src/templates/list.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;postsPerPage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;i&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;Basically, it goes through all of your &lt;code&gt;posts&lt;/code&gt; and create pages that contains a subset of your overall &lt;code&gt;posts&lt;/code&gt;. Meanwhile &lt;code&gt;createListPageParameter&lt;/code&gt; is yet another function that transform data&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;const&lt;/span&gt; &lt;span class="nx"&gt;createListPageParameter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;routePath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;templatePath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;noOfPostsPerPage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;currentPageIndex&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="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;routePath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;templatePath&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;noOfPostsPerPage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;skip&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;currentPageIndex&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;noOfPostsPerPage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;noOfPages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ceil&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;noOfPostsPerPage&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;currentPage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;currentPageIndex&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&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;Now since we want to have the index page / landing page to be the same with the list page. We need to create it the same way in &lt;code&gt;gatsby-node.js&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;createPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;createListPageParameter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&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="s2"&gt;./src/templates/list.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;postsPerPage&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;So far so good, now as you can see the &lt;code&gt;context&lt;/code&gt; passed contains things like &lt;code&gt;limit&lt;/code&gt;, &lt;code&gt;skip&lt;/code&gt;, &lt;code&gt;noOfPages&lt;/code&gt;, and &lt;code&gt;currentPage&lt;/code&gt;. These metadata are then used in the template to invoke yet another GraphQL query as seen in the &lt;code&gt;src/templates/list.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;listQuery&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;graphql&lt;/span&gt;&lt;span class="s2"&gt;`
  query listQuery($skip: Int!, $limit: Int!) {
    allMarkdownRemark(
      sort: { fields: [frontmatter___date], order: DESC }
      limit: $limit
      skip: $skip
    ) {
      ...MarkdownEdgesFragment
    }
  }
`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This result of the call is then available in the bespoke React component's &lt;code&gt;props.data.allMarkdownRemark.edges&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;What do learn here? It's possible after you passed some metadata to the page through &lt;code&gt;context&lt;/code&gt;, e.g. &lt;code&gt;skip&lt;/code&gt; and &lt;code&gt;limit&lt;/code&gt; you can use them to make another GraphQL call. This is a powerful concept which allows you to add more data into the page.&lt;/p&gt;

&lt;p&gt;But what is &lt;code&gt;...MarkdownEdgesFragment&lt;/code&gt;? It's GraphQL &lt;a href="https://www.apollographql.com/docs/react/data/fragments/"&gt;fragment&lt;/a&gt;. But it behaves slightly differently in Gatsby.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Fragment
&lt;/h2&gt;

&lt;p&gt;For the better or worse, Gatsby is using their own version of GraphQL. That's why on the file where a GraphQL query is executed, usually there's this import&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;graphql&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gatsby&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Gatsby handles GraphQL fragments in slightly different way than standard GraphQL. Normally GraphQL fragments are imported, interpolated at the top of the GraphQL query and then used by spreading it. In Gatsby's GraphQL, the first and second steps are not needed as Gatsby crawls through all of your files and makes all fragments available in the query automagically.&lt;/p&gt;

&lt;p&gt;Let's look back at &lt;code&gt;src/templates/list.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&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="nx"&gt;graphql&lt;/span&gt;&lt;span class="s2"&gt;`
  query HomePageQuery {
    allMarkdownRemark(sort: { fields: [frontmatter___date], order: DESC }) {
      ...MarkdownEdgesFragment
    }
  }
`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;code&gt;MarkdownEdgesFragment&lt;/code&gt; is not explicitly imported/interpolated anywhere and yet it can be used in the GraphQL query. It's magic.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Styled Components
&lt;/h2&gt;

&lt;p&gt;Gatsby by default uses CSS Modules. However I prefer to use &lt;a href="https://www.styled-components.com/"&gt;Styled Components&lt;/a&gt;. There's a gotcha though. From my experience, sometimes in production the produced css is just missing even though everything is fine when run via &lt;code&gt;gatsby develop&lt;/code&gt;. This happens most often on the first page load.&lt;/p&gt;

&lt;p&gt;How did I fixed it? Apparently I was missing a module. So make sure that these 3 are installed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save&lt;/span&gt; gatsby-plugin-styled-components &lt;span class="se"&gt;\&lt;/span&gt;
  styled-components &lt;span class="se"&gt;\&lt;/span&gt;
  babel-plugin-styled-components
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;and make sure &lt;code&gt;gatsby-config.js&lt;/code&gt; has the following&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&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;plugins&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="na"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`gatsby-plugin-styled-components`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Add any options here&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;h2&gt;
  
  
  7. Code Highlighting
&lt;/h2&gt;

&lt;p&gt;To highlight code in the posts, I found &lt;a href="https://prismjs.com/"&gt;PrismJs&lt;/a&gt; seems to be popular and easy enough to use. Based on this &lt;a href="https://dev.to/fidelve/the-definitive-guide-for-using-prismjs-in-gatsby-4708"&gt;tutorial&lt;/a&gt;, you can either use &lt;a href="https://www.gatsbyjs.org/packages/gatsby-remark-prismjs/"&gt;gatsby-remark-prismjs&lt;/a&gt; or set it up manually like so:&lt;/p&gt;

&lt;p&gt;Install the dependencies from the command line&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save&lt;/span&gt; prismjs &lt;span class="se"&gt;\&lt;/span&gt;
  babel-plugin-prismjs &lt;span class="se"&gt;\&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Set &lt;code&gt;.babelrc&lt;/code&gt; in the root folder of your project. Make sure that the languages that you want to highlight are included in the config.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;presets&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;babel-preset-gatsby&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="s2"&gt;plugins&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;prismjs&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;languages&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;javascript&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="s2"&gt;css&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="s2"&gt;markup&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="s2"&gt;ruby&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="s2"&gt;plugins&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;show-language&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="s2"&gt;theme&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="s2"&gt;tomorrow&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="s2"&gt;css&lt;/span&gt;&lt;span class="dl"&gt;"&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Lastly, make sure that you invoke it on your pages/templates, i.e. &lt;code&gt;src/templates/post.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;useEffect&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;Prism&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;highlightAll&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;h2&gt;
  
  
  8. Google Analytics
&lt;/h2&gt;

&lt;p&gt;A website without any tracking is not complete and we are implementing Google Analytics via &lt;a href="https://www.gatsbyjs.org/packages/gatsby-plugin-gtag/"&gt;Gatsby Plugin GTag&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It's reasonably simple to use. Add the following to &lt;code&gt;gatsby-config.js&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&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;plugins&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="na"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`gatsby-plugin-gtag`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;trackingId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;YOUR_GOOGLE_ANALYTICS_ID&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;head&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="na"&gt;anonymize&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="na"&gt;respectDNT&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="na"&gt;pageTransitionDelay&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="na"&gt;sampleRate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;siteSpeedSampleRate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;cookieDomain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;codeallnight.com&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;There are several important things here.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Google Tag Assistant prefer the tracking script to be put in &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt;, thus &lt;code&gt;head:true&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The plugin must be put as &lt;strong&gt;the first plugin&lt;/strong&gt; in &lt;code&gt;plugins&lt;/code&gt; array. I missed this at my first attempt.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Originally I tried to follow &lt;a href="https://www.gatsbyjs.org/packages/gatsby-plugin-google-analytics/"&gt;this default guide&lt;/a&gt; but it did not work, as I couldn't see any traffic on &lt;a href="https://get.google.com/tagassistant/"&gt;Google Tag Assistant&lt;/a&gt;. It simply says &lt;code&gt;No HTTP response detected&lt;/code&gt;. Once I switch to Gatsby Plugin GTag, I can see the tracking data on Google Analytics &lt;strong&gt;real time&lt;/strong&gt;. I am not 100% certain why but it's probably related to &lt;a href="https://developers.google.com/analytics/devguides/collection/gtagjs/migration"&gt;analytics.js being deprecated&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  9. Epilogue
&lt;/h2&gt;

&lt;p&gt;And there you have it, one pager guide for Gatsby. It's quite long, but it reflects my time spent in building my personal website at &lt;a href="https://www.codeallnight.com"&gt;https://www.codeallnight.com&lt;/a&gt;. Maybe it's just that I'm not experienced enough, but there are quite a number of things to implement before I'm finally happy with my site.&lt;/p&gt;

&lt;p&gt;If you have any feedback, feel free to hit me up on &lt;a href="https://twitter.com/djoepramono"&gt;Twitter&lt;/a&gt; and as always thanks for reading.&lt;/p&gt;

</description>
      <category>gatsby</category>
      <category>javascript</category>
      <category>react</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
